//FlagStats
// by Mine GO BOOM
//  for CEBot

#include "FlagStats.h"
	//Our of defines are in that header..

extern "C" __declspec (dllexport) void CALLBACK DATABOT_Cmd(BOT_INFO *bi) {
	//This callback is always done right after the dll was loaded.. tells the core information about this dll

	strcpy(bi->DllInfo->HeaderVersion,BOT_HEADER_VERSION);
	//This line always needs to be in here just like that..

	//These tell anyone looking at the webpage extra info about this dll..
	strcpy(bi->DllInfo->Name,"FlagStats");
	strcpy(bi->DllInfo->Version,"0.01");
	strcpy(bi->DllInfo->Info,"Will print out game statistics and a mini-ranking system for that game.");
	BOT_NAMES bn[MAX_DLLS];
	bi->DllInfo->MaxBotsAllowed=LoadBotName(bn);
	//This last line tells how many 'bots' are allowed.. so we need to know how many we could use, we
	// do a little call to our function to load all bot names, and return the number of names we got..

	//Just some credits for the maker of this dll
	strcpy(bi->DllMaker->Name,"Mine GO BOOM");
	strcpy(bi->DllMaker->Email,"murphyj@cvn.net");
}

extern "C" __declspec (dllexport) void CALLBACK WEBBOT_Cmd(BOT_WEB *bw,BOT_MSG2 *bn) {
	//We don't need to do anything in here, but if we wanted to, we could have a webpage print out
	//all the stats we have so far for the game..
}

extern "C" __declspec (dllexport) void CALLBACK CEBOT_Cmd(BOT_CMD *bc,BOT_MSG *bm) {
	if (bc->Cmd == BOTCMD_Shutdown)
		return;


	int c,i;
	//I always use c for loops :)
	//'i' is just another random define i use..

	switch (bc->Cmd) {
		//Figure out why we were called by looking at which event took place..
			//For this example, i'm going to try to put things in a little bit of order for how the bot logs in with them..

		case BOTCMD_Init:
			//This bot has been started, so we need to setup our data and log into a zone...
			if (bm->SendDataSize(bc,sizeof(FLAGSTAT_EXTRA))) {
				//Params: BOT_CMD *bc,int DataSize
						//If this function returns any non-0 value, an error happened, and we can't live without our data, so we shutdown..
				bm->SendShutdown(bc);
					//Params: BOT_CMD *bc
			} else {
				//Ok, our DataSize call worked, so lets log into a zone..
				BOT_NAMES bn[MAX_DLLS];
				int l=LoadBotName(bn);
					//the variable 'l' now has the max number of names we could possibly have..
				for (c=0;c<l;c++) {
					//Looping through all names in the BOT_NAMES's..
					BOOL match=FALSE;
					for (i=0;i<MAX_DLLS;i++) {
						if (bc->BotInfo[i].BotStatus->IsValid && i != bc->BehaviorID) {
							//Looping through all the bots already on, checking to see if they have one of our names..
								//The i != bc->BehaviorID makes sure we skip ourselves..
							if (!strcmpi(bc->BotInfo[i].BotStatus->BotName,bn[c].BotName)) {
								//Its match..
								match=TRUE;
								break;
							}
						}
					}
					if (match==FALSE)
						break;
				}
				if (c==l) {
					//We went through all names, and they were all used, so we can't enter a zone.. disconnecting..
					bm->SendShutdown(bc);
				} else {
					//'c' now contains which bn has our login stuff..
					bm->SendEnterZone(bc,bn[c].IP,bn[c].Port,bn[c].BotName,bn[c].Password,bn[c].Arena,bn[c].ArenaType,16,1);
						//Params: BOT_CMD *bc,char *IP,int Port,char *Name,char *Password,char *Arena,char ArenaType,short XRes,short YRes
					//For the Res sizes, they aren't vital, but just something neat you could use if you want.. i always set mine to
					// 16 by 1, because if a sysop /*einfo's it, it just looks neat :)
				}
			}
			break;

		case BOTCMD_EnterZone:
			//We are excepted to enter a zone, we need to check how we were accepted..

			switch (bc->Data1) {
				//Contains a certain ENTERINFO...
				case ENTERINFO_NoBilling:
					//No billing found.. since our dll doesn't care about player's names at all, we could just enter the zone without any problem..
					bm->SendChangeArenas(bc,"",ARENATYPE_Random);
						//Params: BOT_CMD *bc,char *Arena,int ArenaType
							//Just as a note, it doesn't matter what we put in here.. the core is waiting for us to say we want in for billing server, so
							//all we need to do to say we want in is to just send this.. we could put any data into the Arena/ArenaType, and the core will
							//ignore it, since it will use the arena and arenatype send in the SendEnterZone function...
					//Please note that there is no break after this case.. this bot logs in during normal and no billing, so this is a little shortcut
					//to do both login works..
				case ENTERINFO_Normal:
					if (bc->Data2 == 0) {
						//If this is anything but 0, it means the zone wants a user information form, and we can't send it..
						FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
							//This just sets a pointer to our BotData, and gives us a nice struct to work with off it..

						//Please note that the core WILL set everything in BotData to 0, so anything you need to set inside of it to non-0,
						//now is the best time.

						//Right here, we could check to see if a new "news.txt" was updatedm since bc->DataC[0] would be a non-zero value..

					} else {
						//Didn't set correctly, lets shutdown...
						//please note that this will not happen at all most likely, but i'm including it in here for just an example..
						bm->SendShutdown(bc);
					}
					break;

				default:
					//Everything else is an error, so we need to shut ourselves down..
					bm->SendShutdown(bc);
					break;
			}
			break;

		//All other case's from here on our, we are in, and we will get our BotData's ready and everything..

		case BOTCMD_PlayerEntering:
			//Usually the first thing after the EnterZone is player's entering the arena.. they arn't always entering
			//the arena, because the first time you enter an arena, they are already there..
				//Yes, i am sorry, but we do get these things before a BOTCMD_EnteredArena

			if (bc->UserData->IsValid) {
				//Just a safety check.. i do these all the time, even though almost all of them i could do away with..
				FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
					//Our favorite line.. this will be repeated a lot in here..
				memset(&e->Players[bc->UserData->UID],0,sizeof(FLAG_PLAYER_EXTRA));
					//Just clearing out anything that might be left in here..
				e->Players[bc->UserData->UID].FreqTick=GetTickCount();
					//Record when they joined this freq
				e->Players[bc->UserData->UID].KillPoints=bc->UserData->KillPoints;
					//Record their current kill points...
				e->Players[bc->UserData->UID].ShipTick=GetTickCount();
					//Record time they got in this ship..
				e->Players[bc->UserData->UID].Deaths=bc->UserData->Deaths;
					//Record their starting deaths
				e->Players[bc->UserData->UID].Kills=bc->UserData->Kills;
					//Record their starting kills
				e->Players[bc->UserData->UID].KillPoints=bc->UserData->KillPoints;
					//Record their starting kill points
			}
			break;

		case BOTCMD_PlayerLeaving:
			//Sorry, I'm going a bit out of line of the order of BOTCMD's, but this one is nice to keep around the PlayerEntering..
			if (bc->UserData->IsValid) {
				FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
					//I'm not going to comment on these anymore, you should understand it by now, or can atleast copy it...

				if (bc->UserData->Shiptype) {
					//Don't save spec info
					e->ShipTime[bc->UserData->Shiptype - 1] += GetTickCount() - e->Players[bc->UserData->UID].ShipTick;
				}
					//Add their time in that ship to the global one..
			}
			//We don't really need to clean out this spot, because whenever a person enters, it clears it all out..
			break;

		case BOTCMD_FlagSet:
			//The next thing after players entering the arena is getting some flag data for flags already down
				//NOTE: When this code was written, flags that people are already were carrying are not sent to the dll
				//so the dll does not know who has them, or if they even exists until they drop them...

			//Since this bot works off the idea that they know who dropped the flag because of the FlagDrop, we don't need to worry about it
			//But if we did want to know information about this flag, we can get it from bc->Flags[bc->Data1].***, since bc->Data1
			//is the flag id of the flag being updated..
				//NOTE: The zone sends these updates every so often, to make sure the client has them.. you need to understand that just because
				//this FlagSet was sent doesn't mean a flag was just dropped. It could be just one of the refreshments of it
			break;

		//We would get some BOTCMD_Line messages (ie: the message the zone sends to all people entering arena), but i'll skip that for after the EnteredArena

		case BOTCMD_EnteredArena:
			//We finnally entered an arena..
			//bc->DataC would contain the arena name we are in..
			break;

		case BOTCMD_Line:
			//Ok, so lets handle this stuff now..
			HandleCommands(bc,bm);
				//Sending this stuff out, so it won't flood the switch area..
			break;

		//So we handled all the entering stuff, so lets handle some quiting stuff..

		case BOTCMD_Disconnect:
			//In here, this is your VERY last chance to use anything in your BotData, because after this, it gets cleaned out.
				//Reason why we disconnected is in Data1
			break;

		case BOTCMD_Shutdown:
			//Here is our very last chance to save any of our global data that wasn't in the BotData..
			break;

		//Since this bot needs to set data up for who dropped which flag, we need to save it.

		case BOTCMD_FlagPickup:
			if (bc->UserData->IsValid) {
				FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
				strcpy(e->Flags[bc->Data1].Name,bc->UserData->Name);
				e->Players[bc->UserData->UID].Pickups++;
			}
			break;

		case BOTCMD_Kill:
			if (bc->Killed->IsValid && bc->Killer->IsValid) {
				FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
				if (bc->Killed->Shiptype) {
					e->ShipDeaths[bc->Killed->Shiptype-1]++;
				}
				if (bc->Killer->Shiptype) {
					e->ShipKills[bc->Killer->Shiptype-1]++;
				}
					//Our total data about ship kills/deaths is updated..
				if (bc->Data2) {
					//Data2 contains the number of flags transfered.. if the person with the death had a flag, and was dropped, we'd get a FlagDrop
					for (c=0;c<MAX_FLAGS;c++) {
						if (bc->Flags[c].Exists) {
							if (bc->Flags[c].Carried && bc->Flags[c].PUID == bc->Killer->UID) {
								strcpy(e->Flags[c].Name,bc->Killer->Name);
								//Force all flags that the killer has back into our flag list..
									//Please note that this may re-copy ones we already had, but better safe than sorry
							}
						}
					}
					e->Players[bc->Killed->UID].FlagDeath++;
					e->Players[bc->Killer->UID].FlagKills++;
				}
			}
			break;

		case BOTCMD_FlagReset:
			HandleFlagReset(bc,bm);
			//We do a LOT in here, so we are exporting it out to keep the space this switch takes up..
			break;

		case BOTCMD_ScoreUpdate:
			//Since our player stats works off the idea of their last one, if they get scorereseted, we need to reset ours back to 0's..
			if (bc->UserData->IsValid) {
				if (bc->UserData->KillPoints == 0 && bc->UserData->FlagPoints == 0 && bc->UserData->Kills == 0 && bc->UserData->Deaths == 0) {
					//Yep, they got reseted..
					FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
					e->Players[bc->UserData->UID].Deaths=0;
					e->Players[bc->UserData->UID].KillPoints=0;
					e->Players[bc->UserData->UID].Kills=0;
				}
			}
			break;

		case BOTCMD_FreqChange:
			if (bc->UserData->IsValid) {
				FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
				e->Players[bc->UserData->UID].FreqTick=GetTickCount();
				e->Players[bc->UserData->UID].FreqChanges++;
			}
			break;

		case BOTCMD_ShipChange:
			if (bc->UserData->IsValid) {
				FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
				if (bc->UserData->Lastship != bc->UserData->Shiptype) {
					//Just a safety check..
					if (bc->UserData->Lastship) {
						//Keeps us from saving spec data
						e->Players[bc->UserData->UID].ShipTime[bc->UserData->Lastship-1] += GetTickCount() - e->Players[bc->UserData->UID].ShipTick;
						e->ShipTime[bc->UserData->Lastship-1] += GetTickCount() - e->Players[bc->UserData->UID].ShipTick;
					}
					if (bc->UserData->Shiptype) {
						//Keeps us from saving spec data
						e->ShipChange[bc->UserData->Shiptype-1]++;
					}
					e->Players[bc->UserData->UID].ShipTick=GetTickCount();
				}
			}
			break;

		//And thats all the BOTCMD's we care about.. everything else is not needed for this example..
	}
}



int LoadBotName(BOT_NAMES *bn) {
	memset(bn,0,sizeof(BOT_NAMES)*MAX_DLLS);
	int i=0,l,c;
	char str[255];
	FILE *f = fopen(BOT_NAME_FILE,"r");
	if (f!=NULL) {
		while (!feof(f)) {
			fgets(str,255,f);
			l=strlen(str)-1;
			if (str[l]=='\n') str[l]=0;
			l=strlen(str);
			int step=0;
			int last=0;
			for (c=0;c<l;c++) {
				if (str[c]==':') {
					str[c]=0;
					if (step == 0) {
						strcpy(bn[i].BotName,&str[last]);
					} else if (step == 1) {
						strcpy(bn[i].Password,&str[last]);
					} else if (step == 2) {
						strcpy(bn[i].IP,&str[last]);
						bn[i].Port = atoi(&str[c+1]);
						bn[i].Arena[0]=0;
						bn[i].ArenaType=ARENATYPE_Random;
						i++;
						break;
					}
					step++;
					last=c+1;
				}
			}
		}
	}
	if (i>MAX_DLLS) i=MAX_DLLS;
	return i;
}

int ChangeArenas(BOT_CMD *bc,BOT_MSG *bm,char *ArenaName) {
	int ArenaType;
	if ((ArenaName[0] >= 48 || ArenaName[0] <= 57) && strlen(ArenaName)==1) {
		ArenaType=ARENATYPE_Public;
	} else if (strlen(ArenaName)==0) {
		ArenaType=ARENATYPE_Random;
	} else {
		ArenaType=ARENATYPE_Private;
	}
	return bm->SendChangeArenas(bc,ArenaName,ArenaType);
}

void HandleCommands(BOT_CMD *bc,BOT_MSG *bm) {
	FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
	int c;
	char name[20];
	char ctime[50];

	if (bc->Line->LineType==LINETYPE_PRIVATE) {
		if (!strcmpi(bc->Line->CmdStr,"!help")) {
			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"!help         - This listing of all commands");
				//Params: BOT_CMD *bc,char LineType,short UID,char Sound,char *Line,...
			//This is a good example of how you could do a ! command handler..
				//Also, the CmdStr WILL include the ! part of the command.. it also will be cased the same way as the player sent it, so do
				//command compares case insensitive if you want both !help and things like !hELp

			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"!stats        - Displays global stats about flag game");
			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"!stats [NAME] - Displays stats about that player (can be first few letters of name)");
			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"!mystats      - Just a shortcut to !stats %s",bc->UserData->Name);
				//Hehe, this one is neat.. it says the shortcut is to their own name.. just some neat little stuff most people don't notice
			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"!flags        - Information about flags");
			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"!shipstats    - Displays global stats about ships for this flag game");
		} else if (!strcmpi(bc->Line->CmdStr,"!stats") || !strcmpi(bc->Line->CmdStr,"!mystats")) {
			if (strlen(bc->Line->Params)>20 && strcmpi(bc->Line->CmdStr,"!mystats")) {
				//Since our 'name' is only 20 long (the max size of a name in the game), we need to make sure we don't write something bigger than 20 to it
				bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Sorry, player name is too long to be valid. Please correct this and try again");
			} else if (strlen(bc->Line->Params) || !strcmpi(bc->Line->CmdStr,"!mystats")) {
				//If they did something like !stats Mine GO BOOM, the Params would have "Mine GO BOOM" in it..
				if (!strcmpi(bc->Line->CmdStr,"!mystats")) {
					//Since they did a !mystats, we can just set c to their uid, and don't need to search for them
					c=bc->UserData->UID;
				} else {
					for (c=0;c<MAX_PLAYERS;c++) {
						if (bc->UserList[c].IsValid) {
							//You always need to check if it IsValid before you look at the data in it..

							strncpy(name,bc->UserList[c].Name,strlen(bc->Line->Params));
							name[strlen(bc->Line->Params)]=0;
							//This will copy to the 'name' just the length of the Params, so if they had !stats Mine G, it would find Mine GO BOOM as a compare
							if (!strcmpi(name,bc->Line->Params)) {
								//Do a case insensitive comparing. This will match the first name it can find, which may not always be good..
									//Lets say we did a !stats Mi, and player Mine GO BOOM is in, and MiFi is in..
									//A good searcher (which this isn't, its just an example) would find the Mi match to be MiFi, since it is the best via
									//name sorting, since F comes before N. For here, which ever player enter the arena first gets to be the lowest userid
									//so the Mi could match either one at different times...
								//Oh well, we found a match, so lets use this one..
								break;	//This keeps the 'c' the user's id..
							}
						}
					}
				}
				if (c == MAX_PLAYERS) {
					//This means that the for loop went the whole way, and didn't break, so found no matches..
					bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Sorry, could not find a name that matched up with %s",bc->Line->Params);
						//Please note that i did use their params in here, ONLY because i did the strlen of it earlier..
						//If you tried to post their Params without a check on it, your message may become too long, and may not send..
				} else {
					//c has the match's uid
					int kills=bc->UserList[c].Kills - e->Players[c].Kills;
					if (kills < 0)
						kills=0;
					int deaths=bc->UserData[c].Deaths - e->Players[c].Deaths;
					if (deaths < 0)
						deaths=0;
					float avg=0.0;
					if (kills != 0)
						avg=(float)(bc->UserList[c].KillPoints - e->Players[c].KillPoints)/(float)kills;

					bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Name: %s  Current Game Stats: Kills: %d  Deaths: %d  Avg Kill: %.2f",bc->UserList[c].Name,kills,deaths,avg);
					ReturnTimeStr(ctime,e->Players[c].FreqTick);
					bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Flag Pickups: %d  Flaggers Killed: %d  Died with Flag: %d",e->Players[c].Pickups,e->Players[c].FlagKills,e->Players[c].FlagDeath);
					bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Been on freq for: %s  Total freq change count: %d",ctime,e->Players[c].FreqChanges);
				}
			} else {
				//If they just did !stats, the Params would have "" in it, so strlen == 0, which would go down to here
				ReturnTimeStr(ctime,e->FlagTick);
				int fc=0;
				int kc=0;
				int dc=0;
				int rkc=0;
				int rdc=0;
				char FL[20];
				char KL[20];
				char DL[20];
				char RK[20];
				char RD[20];
				strcpy(FL,"<NONE>");
				strcpy(KL,"<NONE>");
				strcpy(DL,"<NONE>");
				strcpy(RK,"<NONE>");
				strcpy(RD,"<NONE>");
				for (c=0;c<MAX_PLAYERS;c++) {
					if (bc->UserList[c].IsValid) {
						//Have to make sure they are still valid
						if (e->Players[c].Pickups > fc) {
							strcpy(FL,bc->UserList[c].Name);
							fc=e->Players[c].Pickups;
						}
						if (bc->UserList[c].Kills - e->Players[c].Kills > kc) {
							strcpy(KL,bc->UserList[c].Name);
							kc=bc->UserList[c].Kills - e->Players[c].Kills;
						}
						if (bc->UserList[c].Deaths - e->Players[c].Deaths > dc) {
							strcpy(DL,bc->UserList[c].Name);
							dc=bc->UserList[c].Deaths - e->Players[c].Deaths;
						}
						if (e->Players[c].FlagKills > rkc) {
							strcpy(RK,bc->UserList[c].Name);
							rkc=e->Players[c].FlagKills;
						}
						if (e->Players[c].FlagDeath > rdc) {
							strcpy(RD,bc->UserList[c].Name);
							rdc=e->Players[c].FlagDeath;
						}
					}
				}
				bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Flag game time: %s  Flag Pickup Leader: %s (%d)",ctime,FL,fc);
				bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Kills Leader: %s (%d)  Deaths Leader: %s (%d)",KL,kc,DL,dc);
				bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Flagger Kills Leader: %s (%d)  Flaggers Deaths Leader: %s (%d)",RK,rkc,RD,rdc);
			}
		} else if (!strcmpi(bc->Line->CmdStr,"!sex")) {
			SaveData(bc,bm);
			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,10,"Omg, I'm not having sex with you skank! Damn perverts...");
				//Just something stupid to add in, since every other bot has some type of !sex command
		} else if (!strcmpi(bc->Line->CmdStr,"!flags")) {
			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"To validate mod status for this command, use !flags %1");
				//This is just a little shortcut to check mod status, since this bot core allows you to know which sound was played for any msg
			int count=0;
			for (c=0;c<MAX_FLAGS;c++) {
				if (bc->Flags[c].Exists) {
					//Need to make sure flag is real..
					count++;
					if (bc->Flags[c].Carried) {
						if (bc->Flags[c].Freq<100 || bc->Line->Sound == 1) {
							bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Player %s is carrying a flag for freq %d",bc->UserList[bc->Flags[c].PUID].Name,bc->Flags[c].Freq);
						} else {
							bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Player %s is carrying a flag for a private freq",bc->UserList[bc->Flags[c].PUID].Name);
						}
					} else {
						if (strlen(e->Flags[c].Name)) {
							if (bc->Flags[c].Neutral) {
								bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Player %s dropped a neutral flag",e->Flags[c].Name);
							} else if (bc->Flags[c].Freq<100 || bc->Line->Sound == 1) {
								bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Player %s dropped a flag for freq %d",e->Flags[c].Name,bc->Flags[c].Freq);
							} else {
								bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Player %s dropped a flag for a private freq",e->Flags[c].Name);
							}
						} else {
							if (bc->Flags[c].Neutral) {
								bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"A zone placed neutral flag is still placed down");
							} else if (bc->Flags[c].Freq<100 || bc->Line->Sound == 1) {
								bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"An unknown player dropped a flag for freq %d",bc->Flags[c].Freq);
							} else {
								bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"An unknown player dropped a flag for a private freq");
							}
						}
					}
					if (bc->Line->Sound == 1) {
						bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"   This flag is located at tile coords: %d,%d",bc->Flags[c].XCoord,bc->Flags[c].YCoord);
					}
				}
			}
			if (count==0) {
				bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"No infromation about flags is avaliable yet");
			}
		} else if (!strcmpi(bc->Line->CmdStr,"!shipstats")) {
			double total=0;
			long totalk=0;
			long totald=0;
			float tf;
			double stime[8];

			for (c=0;c<8;c++) {
				stime[c]=e->ShipTime[c];
			}
			for (c=0;c<MAX_PLAYERS;c++) {
				if (bc->UserList[c].IsValid) {
					if (bc->UserList[c].Shiptype) {
						//Keeps us from saving spec data
						stime[bc->UserList[c].Shiptype-1]+=GetTickCount()-e->Players[c].ShipTick;
					}
				}
			}
			for (c=0;c<8;c++) {
				total+=stime[c];
				totalk+=e->ShipKills[c];
				totald+=e->ShipDeaths[c];
			}

			bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,"Shiptype        Kills (%c of T)   Deaths (%c of T)   Percent Played",'%','%');
			char str[255];
			char str2[255];
			for (c=0;c<8;c++) {
				switch (c) {
					case 0: strcpy(str,"Warbird:     "); break;
					case 1: strcpy(str,"Javelin:     "); break;
					case 2: strcpy(str,"Spider:      "); break;
					case 3: strcpy(str,"Leviathon:   "); break;
					case 4: strcpy(str,"Terrier:     "); break;
					case 5: strcpy(str,"Weasel:      "); break;
					case 6: strcpy(str,"Lancaster:   "); break;
					case 7: strcpy(str,"Shark:       "); break;
				}
				sprintf(str2,"%d",e->ShipKills[c]);
				AppendStr(str,str2,8);
				strcat(str," (");
				if (totalk>0) {
					tf = (float)e->ShipKills[c] * 100 / (float)totalk;
				} else {
					tf = 0.0;
				}
				sprintf(str2,"%.1f",tf);
				AppendStr(str,str2,5);
				strcat(str,"%c)");

				sprintf(str2,"%d",e->ShipDeaths[c]);
				AppendStr(str,str2,9);
				strcat(str," (");
				if (totald>0) {
					tf = (float)e->ShipDeaths[c] * 100 / (float)totald;
				} else {
					tf = 0.0;
				}
				sprintf(str2,"%.1f",tf);
				AppendStr(str,str2,5);
				strcat(str,"%c)");

				if (total==0) {
					sprintf(str2,"0.0");
				} else {
					sprintf(str2,"%.1f",(float)stime[c]*100/total);
				}
				AppendStr(str,str2,16);
				strcat(str,"%c");

//				sprintf(str2,"           stime[c]: %f  total: %f  ShipTime[c]: %f",stime[c],total,e->ShipTime[c]);
				bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,str,'%','%','%');
//				bm->SendTextMessage(bc,LINETYPE_PRIVATE,bc->UserData->UID,0,str2);
			}
		}
	}
}

void AppendStr(char *str,char *Add,int Length) {
	int c,l=strlen(Add);
	for (c=0;c<(Length-l);c++)
		strcat(str," ");
	strcat(str,Add);
}

void ReturnTimeStr(char *TimeStr,int Ticks) {
	//This section looks ugly, and it is, but i do this because i'm always a stickler for extra 's' on things that don't need them..

	if (Ticks == 0) {
		sprintf(TimeStr,"<UNKNOWN>");
		return;
	}

//	int qqq = GetTickCount();
	int time = GetTickCount() - Ticks;

	if (time < 0) {
		sprintf(TimeStr,"<UNKNOWN>");
		return;
	}

	int hours = (time) / 3600000;
//	int mins = ((GetTickCount()-Ticks)-(((GetTickCount()-Ticks)/3600000)*3600000))/60000;
	int mins = (time % 3600000) / 60000;
	int secs = ((time % 3600000) % 60000) / 1000;

	TimeStr[0]=0;

	if (hours > 1) {
		strcatf(TimeStr,"%d hours and ",hours);
	} else if (hours == 1) {
		strcatf(TimeStr,"%d hour and ",hours);
	}

	if (mins != 1) {
		strcatf(TimeStr,"%d minutes",mins);
	} else {
		strcatf(TimeStr,"%d minute",mins);
	}

	strcat(TimeStr," and ");

	if (secs != 1) {
		strcatf(TimeStr,"%d seconds",secs);
	} else {
		strcatf(TimeStr,"%d second",secs);
	}

/*
	if (hours > 1) {
		if (mins > 1) {
			sprintf(TimeStr,"%d hours and %d minutes",hours,mins);
		} else if (mins == 1) {
			sprintf(TimeStr,"%d hours and %d minute",hours,mins);
		} else {
			sprintf(TimeStr,"%d hours",hours);
		}
	} else if (hours == 1) {
		if (mins > 1) {
			sprintf(TimeStr,"%d hour and %d minutes",hours,mins);
		} else if (mins == 1) {
			sprintf(TimeStr,"%d hour and %d minute",hours,mins);
		} else {
			sprintf(TimeStr,"Exactly %d hour",hours);
		}
	} else {
		if (mins > 1) {
			sprintf(TimeStr,"%d minutes",mins);
		} else if (mins == 1) {
			sprintf(TimeStr,"Exactly %d minute",mins);
		} else {
			sprintf(TimeStr,"Less than 1 minute");
		}
	}
//*/
}

void HandleFlagReset(BOT_CMD *bc,BOT_MSG *bm) {
	FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
	int c,i=-1;
	char str[255];
	char str2[255];

	if (e->FlagTick > 0) {
		double total=0;
		if (SHIP_STATS) {
			long totalk=0;
			long totald=0;
			float tf;
			double stime[8];

			for (c=0;c<8;c++) {
				stime[c]=e->ShipTime[c];
			}
			for (c=0;c<MAX_PLAYERS;c++) {
				if (bc->UserList[c].IsValid) {
					if (bc->UserList[c].Shiptype) {
						//Keeps us from saving spec data
						stime[bc->UserList[c].Shiptype-1]+=GetTickCount()-e->Players[c].ShipTick;
					}
				}
			}
			for (c=0;c<8;c++) {
				total+=stime[c];
				totalk+=e->ShipKills[c];
				totald+=e->ShipDeaths[c];
			}

			bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena Shiptype        Kills (%c of T)   Deaths (%c of T)   Percent Played",'%','%');

			for (c=0;c<8;c++) {
				switch (c) {
					case 0: strcpy(str,"*arena Warbird:     "); break;
					case 1: strcpy(str,"*arena Javelin:     "); break;
					case 2: strcpy(str,"*arena Spider:      "); break;
					case 3: strcpy(str,"*arena Leviathon:   "); break;
					case 4: strcpy(str,"*arena Terrier:     "); break;
					case 5: strcpy(str,"*arena Weasel:      "); break;
					case 6: strcpy(str,"*arena Lancaster:   "); break;
					case 7: strcpy(str,"*arena Shark:       "); break;
				}
				sprintf(str2,"%d",e->ShipKills[c]);
				AppendStr(str,str2,8);
				strcat(str," (");
				if (totalk>0) {
					tf = (float)e->ShipKills[c] * 100 / (float)totalk;
				} else {
					tf = 0.0;
				}
				sprintf(str2,"%.1f",tf);
				AppendStr(str,str2,5);
				strcat(str,"%c)");

				sprintf(str2,"%d",e->ShipDeaths[c]);
				AppendStr(str,str2,9);
				strcat(str," (");
				if (totald>0) {
					tf = (float)e->ShipDeaths[c] * 100 / (float)totald;
				} else {
					tf = 0.0;
				}
				sprintf(str2,"%.1f",tf);
				AppendStr(str,str2,5);
				strcat(str,"%c)");

				if (total==0) {
					sprintf(str2,"0.0");
				} else {
					sprintf(str2,"%.1f",(float)stime[c]*100/total);
				}
				AppendStr(str,str2,16);
				strcat(str,"%c");

				bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,str,'%','%','%');
			}
		}

		char ctime[50];
		ReturnTimeStr(ctime,e->FlagTick);
		int fc=0;
		int kc=0;
		int dc=0;
		int rkc=0;
		int rdc=0;
		char FL[20];
		char KL[20];
		char DL[20];
		char RK[20];
		char RD[20];
		strcpy(FL,"<NONE>");
		strcpy(KL,"<NONE>");
		strcpy(DL,"<NONE>");
		strcpy(RK,"<NONE>");
		strcpy(RD,"<NONE>");
		total=0;

		for (c=0;c<MAX_PLAYERS;c++) {
			if (bc->UserList[c].IsValid) {
				//Have to make sure they are still valid
				if (e->Players[c].Pickups > fc) {
					strcpy(FL,bc->UserList[c].Name);
					fc=e->Players[c].Pickups;
				}
				if (bc->UserList[c].Kills - e->Players[c].Kills > kc) {
					strcpy(KL,bc->UserList[c].Name);
					kc=bc->UserList[c].Kills - e->Players[c].Kills;
				}
				if (bc->UserList[c].Deaths - e->Players[c].Deaths > dc) {
					strcpy(DL,bc->UserList[c].Name);
					dc=bc->UserList[c].Deaths - e->Players[c].Deaths;
				}
				if (e->Players[c].FlagKills > rkc) {
					strcpy(RK,bc->UserList[c].Name);
					rkc=e->Players[c].FlagKills;
				}
				if (e->Players[c].FlagDeath > rdc) {
					strcpy(RD,bc->UserList[c].Name);
					rdc=e->Players[c].FlagDeath;
				}
				if (bc->Data1 > -1) {
					if (bc->UserList[c].Freq == bc->Data1 && bc->UserList[c].Shiptype) {
						if (total == 0 || (e->Players[c].FreqTick > total)) {
							i=c;
							total=e->Players[c].FreqTick;
						}
					}
				}
			}
		}

		bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena Flag game length: %s  Flag Pickup Leader: %s (%d)",ctime,FL,fc);
		bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena Kills Leader: %s (%d)  Deaths Leader: %s (%d)",KL,kc,DL,dc);
		bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena Flagger Kills Leader: %s (%d)  Flaggers Deaths Leader: %s (%d)",RK,rkc,RD,rdc);
		if (i>-1 && bc->Data1 > -1) {
			ReturnTimeStr(ctime,e->Players[i].FreqTick);
			bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena Last person to join winning freq: %s    Time joined: %s ago",bc->UserList[i].Name,ctime);
		}

		for (c=0;c<MAX_PLAYERS;c++) {
			if (bc->UserList[c].IsValid) {
				if (c==bc->iMyUID) {
					continue;
				}

				int kills=bc->UserList[c].Kills - e->Players[c].Kills;
				if (kills < 0) {
					kills=0;
				}

				int deaths=bc->UserList[c].Deaths - e->Players[c].Deaths;
				if (deaths < 0) {
					deaths=0;
				}

				float avg=0.0;
				if (kills != 0) {
					avg=(float)(bc->UserList[c].KillPoints - e->Players[c].KillPoints)/(float)kills;
				}

				bm->SendTextMessage(bc,LINETYPE_PRIVATE,c,0,"Last Game Stats: Kills: %d  Deaths: %d  Avg Kill: %.2f  Flag Pickups: %d  Flaggers Killed: %d  Died with Flag: %d",kills,deaths,avg,e->Players[c].Pickups,e->Players[c].FlagKills,e->Players[c].FlagDeath);
			}
		}
	}

	if (bc->Data1 == -1) {
		//Someone did a *flagreset, or game timer ran out..
		bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena No team won due to flaggame reset");
	} else {
		//A team won!
		if (bc->Data1 > 99) {
			bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena Good job to the private frequency !  They received %d points",bc->Data2);
		} else {
			bm->SendTextMessage(bc,LINETYPE_PUBLIC,0,0,"*arena Good job to frequency %d !  They received %d points",bc->Data1,bc->Data2);
		}
	}

	SaveData(bc,bm);

	memset(e,0,sizeof(FLAGSTAT_EXTRA));
		//Reset all of our e data now..
	e->FlagTick=GetTickCount();
	for (c=0;c<MAX_PLAYERS;c++) {
		if (bc->UserList[c].IsValid) {
			e->Players[c].Deaths=bc->UserList[c].Deaths;
			e->Players[c].FreqTick=GetTickCount();
			e->Players[c].KillPoints=bc->UserList[c].KillPoints;
			e->Players[c].Kills=bc->UserList[c].Kills;
			e->Players[c].ShipTick=GetTickCount();
		}
	}
}

int strcatf(char *str,const char *Line,...) {
		//This function is just a favorite of mine.. strcat and sprintf combinded
	char Line2[5000];
	va_list arglist;
	va_start(arglist,Line);
	vsprintf(Line2,Line,arglist);
	va_end(arglist);
	strcat(str,Line2);
	return strlen(str);
}

void SaveData(BOT_CMD *bc,BOT_MSG *bm) {
	FLAGSTAT_EXTRA *e = (FLAGSTAT_EXTRA*)bc->BotData;
	FILE *f = fopen(BOT_SAVE_DATA,"at");
	if (f == NULL) {
		return;
	}

	int c;
	char ctime[50];
	char str[5000];
	char str2[5000];
	ReturnTimeStr(ctime,e->FlagTick);

	if (bc->Cmd == BOTCMD_FlagReset) {
		fprintf(f,"\n  --  FLAG GAME SAVING  --\n");
	} else {
		fprintf(f,"\n  --  QUICK SAVE  --\n");
	}
	fprintf(f,"Global data:\n");
	if (bc->Cmd == BOTCMD_FlagReset) {
		fprintf(f,"Winning freq: %d   Jackpot: %d\n",bc->Data1,bc->Data2);
	}
	fprintf(f,"FlagTick: %d   GetTickCount(): %d    TimeStr: %s\n\n",e->FlagTick,GetTickCount(),ctime);

	fprintf(f,"Ship data:\n");
	for (c=0;c<8;c++) {
		str[0]=0;
		strcatf(str,"Ship #%d  ",c+1);

		strcat(str,"Kills:");
		sprintf(str2,"%d",e->ShipKills[c]);
		AppendStr(str,str2,5);

		strcat(str,"  Deaths:");
		sprintf(str2,"%d",e->ShipDeaths[c]);
		AppendStr(str,str2,5);

		strcat(str,"  SC:");
		sprintf(str2,"%d",e->ShipChange[c]);
		AppendStr(str,str2,5);

		strcat(str,"  Time:");
		sprintf(str2,"%d",e->ShipTime[c]);
		AppendStr(str,str2,18);

		strcat(str,"\n");

		fprintf(f,str);
//		fprintf(f,"Ship #%d   Kills: %d   Deaths: %d   SC: %d   Time: %d\n",c+1,e->ShipKills[c],e->ShipDeaths[c],e->ShipChange[c],e->ShipTime[c]);
	}
	fprintf(f,"\n");

	fprintf(f,"Flag data:\n");
	for (c=0;c<MAX_FLAGS;c++) {
		if (strlen(e->Flags[c].Name)) {
			fprintf(f,"c: %d   Name: %s\n",c,e->Flags[c].Name);
		}
	}
	fprintf(f,"\n");

	fprintf(f,"Each player's data:\n");
	for (c=0;c<MAX_PLAYERS;c++) {
		if (bc->UserList[c].IsValid) {
			str[0]=0;
			
			strcat(str,"UID: ");
			sprintf(str2,"%d",c);
			AppendStr(str,str2,3);

			strcat(str,"  Name: ");
			sprintf(str2,"%s",bc->UserList[c].Name);
			AppendStr(str,str2,20);

			strcat(str,"  Kills: ");
			sprintf(str2,"%d",e->Players[c].Kills);
			AppendStr(str,str2,5);
			strcat(str," (");
			sprintf(str2,"%d",bc->UserList[c].Kills);
			AppendStr(str,str2,5);
			strcat(str,")");

			strcat(str,"  Deaths: ");
			sprintf(str2,"%d",e->Players[c].Deaths);
			AppendStr(str,str2,5);
			strcat(str," (");
			sprintf(str2,"%d",bc->UserList[c].Deaths);
			AppendStr(str,str2,5);
			strcat(str,")");

			strcat(str,"  Kill Points: ");
			sprintf(str2,"%d",e->Players[c].KillPoints);
			AppendStr(str,str2,6);
			strcat(str," (");
			sprintf(str2,"%d",bc->UserList[c].KillPoints);
			AppendStr(str,str2,6);
			strcat(str,")");

			strcat(str,"  Flag Kills: ");
			sprintf(str2,"%d",e->Players[c].FlagKills);
			AppendStr(str,str2,3);

			strcat(str,"  Flag Deaths: ");
			sprintf(str2,"%d",e->Players[c].FlagDeath);
			AppendStr(str,str2,3);

			strcat(str,"  Flag Pickups: ");
			sprintf(str2,"%d",e->Players[c].Pickups);
			AppendStr(str,str2,3);

			strcat(str,"  Freq Tick: ");
			sprintf(str2,"%d",e->Players[c].FreqTick);
			AppendStr(str,str2,12);

			strcat(str,"  Freq Count: ");
			sprintf(str2,"%d",e->Players[c].FreqChanges);
			AppendStr(str,str2,4);

			strcat(str,"  Ship Tick: ");
			sprintf(str2,"%d",e->Players[c].ShipTick);
			AppendStr(str,str2,12);

			strcat(str,"  Ship Times: ");
			sprintf(str2,"%d",e->Players[c].ShipTime[0]);
			AppendStr(str,str2,12);
			sprintf(str2,"%d",e->Players[c].ShipTime[1]);
			AppendStr(str,str2,12);
			sprintf(str2,"%d",e->Players[c].ShipTime[2]);
			AppendStr(str,str2,12);
			sprintf(str2,"%d",e->Players[c].ShipTime[3]);
			AppendStr(str,str2,12);
			sprintf(str2,"%d",e->Players[c].ShipTime[4]);
			AppendStr(str,str2,12);
			sprintf(str2,"%d",e->Players[c].ShipTime[5]);
			AppendStr(str,str2,12);
			sprintf(str2,"%d",e->Players[c].ShipTime[6]);
			AppendStr(str,str2,12);
			sprintf(str2,"%d",e->Players[c].ShipTime[7]);
			AppendStr(str,str2,12);

			strcat(str,"\n");
			fprintf(f,str);

//			fprintf(f,"UID: %d  Name: %s   Kills: %d (%d)  Deaths: %d (%d)  Kill Points: %d (%d)  Flag Kills: %d  Flag Deaths: %d  Flag Pickups: %d  Freq Tick: %d  Freq Count: %d  Ship Tick: %d  Ship Times: %d %d %d %d %d %d %d %d\n"
//				,c,bc->UserList[c].Name,e->Players[c].Kills,bc->UserList[c].Kills,e->Players[c].Deaths,bc->UserList[c].Deaths,e->Players[c].KillPoints,bc->UserList[c].KillPoints
//				,e->Players[c].FlagKills,e->Players[c].FlagKills,e->Players[c].Pickups,e->Players[c].FreqTick,e->Players[c].FreqChanges,e->Players[c].ShipTick
//				,e->Players[c].ShipTime[0],e->Players[c].ShipTime[1],e->Players[c].ShipTime[2],e->Players[c].ShipTime[3],e->Players[c].ShipTime[4],e->Players[c].ShipTime[5],e->Players[c].ShipTime[6],e->Players[c].ShipTime[7]);
		}
	}

	if (bc->Cmd == BOTCMD_FlagReset) {
		fprintf(f,"  --  FLAG GAME ENDED  --\n\n");
	} else {
		fprintf(f,"  --  QUICK SAVE ENDED --\n\n");
	}
	fclose(f);
}

