#include "stinc.h"
#include "stmisc.h"
#include "stplayer.h"
#include "stgame.h"
#include "stchat.h"
#include "stconsolecommands.h"
#include "sttranslate.h"
#include "stteamcommander.h"

class Chat_TeamCommander;
class Chat_TeamPool;

void gzSettingsTeamCommanderClass::Delete()
{
	this->m_commanderList.Clear();
}
void gzSettingsTeamCommanderClass::Load()
{
	// Cleanups
	this->m_commanderList.Clear();

	if (!this->Open("TeamCommander.ini"))
		stConsole::Out("[Error] TeamCommander.ini can not be loaded or not found.\n");

	this->m_enable                  = this->Load_Bool("General", "Enable", false);
	this->m_vehiclePurchaseInterval = this->Load_Int("General", "VehiclePurchaseInterval", 3) * 60;
	this->m_minimumVictoryLog       = this->Load_Int("General", "minimumVictoryLog", 0) * 60;

	// Load commander list
	for (int i = 1; i <= this->m_cfg->GetSubCount("Commanders"); i++)
	{
		aToken list(this->m_cfg->GetData(aString::Format("Commanders.[%d]", i)));
		for (int j = 1; j <= list.numtok(' '); j++)
			this->m_commanderList.Add(list.gettok(j, ' ').GetData());
	}

	this->Close();

	// Game mode settings
	switch (gzGameMgr->m_settings->m_GameMode)
	{
		case GAMEMODE_SNIPER:
		case GAMEMODE_500SNIP:
		case GAMEMODE_DM:
		case GAMEMODE_TDM:
			if (this->m_enable)
			{
				stConsole::Out("[TeamCommander] Error - Team Commander can not be enabled in %s game mode.\n", gzGameMgr->GetGame()->ModeName());
				this->m_enable = false;
			}
	}
}

gzTeamCommanderClass *gzTeamCommander = NULL;
gzTeamCommanderClass::gzTeamCommanderClass()
{
	this->RegisterEvent(EVT_GAME_LEVEL_ENDED);
	this->RegisterEvent(EVT_GAME_THINK);
	this->RegisterEvent(EVT_PLAYER_LEFT);
	this->RegisterEvent(EVT_OBJECT_CREATE);

	this->m_settings = new gzSettingsTeamCommanderClass;
	this->m_settings->SetOwner(this);

	gzChatCommand_Reg<Chat_TeamCommander> Chat_TeamCommander_Reg("!commander,!com", MSG_ALL | MSG_TEAM, "RequestRepairs", L"repairbuilding");
	gzChatCommand_Reg<Chat_TeamPool> Chat_TeamPool_Reg("!teampool,!tp");
}
void gzTeamCommanderClass::Settings_Loaded()
{
	for (int i = 0; i < 2; i++)
	{
		this->m_commander[i] = NULL;
		this->m_teamPool[i] = 0;
		this->m_canRequestVehicleNotify[i] = false;
		nc_BuildingGameObj *Refinery = nc_BaseControllerClass::Find_Base(i)->Find_Building(nc_REFINERY);
		if (Refinery && Refinery->As_RefineryGameObj() && ((nc_RefineryGameObjDef *)Refinery->As_RefineryGameObj()->definition)->Harvester != 0)
			this->m_harvMaxEngineTorque[i] = ((nc_TrackedVehicleDefClass *)nc_DefinitionMgrClass::Find_Definition(((nc_VehicleGameObjDef *)nc_DefinitionMgrClass::Find_Definition(((nc_RefineryGameObjDef *)Refinery->As_RefineryGameObj()->definition)->Harvester, true))->PhysDefID, true))->MaxEngineTorque;
		else
			this->m_harvMaxEngineTorque[i] = 0.0f;
		for (int j = 0; j < sizeof(this->m_buildingWarn[i]) / sizeof(int); j++)
			this->m_buildingWarn[i][j] = 0;
		this->m_lastBuildingWarn[i] = -1;
		this->m_nextPurchaseTime[i] = this->m_settings->m_vehiclePurchaseInterval;
	}
	this->m_lastSelection = 0;
}
void gzTeamCommanderClass::Level_Ended()
{
	int winTeam = -1;
	if (nc_cTeamManager::Find_Team(0)->Score > nc_cTeamManager::Find_Team(1)->Score)
		winTeam = 0;
	else if (nc_cTeamManager::Find_Team(0)->Score < nc_cTeamManager::Find_Team(1)->Score)
		winTeam = 1;

#ifdef DEVMSG
	stConsole::Out("Win team: %d\n", winTeam);
#endif

	if (winTeam != -1 && this->m_commander[winTeam] != NULL &&
		(((int)this->m_commandTime[winTeam] >= this->m_settings->m_minimumVictoryLog) ||
		(this->m_commandTime[winTeam] >= ((float)this->m_settings->m_minimumVictoryLog / 2.0f))))
	{
		if (this->m_commander[winTeam])
		{
#if VERC(1, 1, 0)
			int victoryCount = 1;
			aSQLite3Result stats;
			if (stDb->Exec(aString::Format("SELECT * FROM commander_stats WHERE player = '%d'", this->m_commander[winTeam]->GetStId()), &stats) != aSQLITE_ERROR)
			{
				if (stats.Count() == 0)
				{
					if (stDb->Exec(aString::Format("INSERT INTO commander_stats VALUES ('%d', '%d', '0')", this->m_commander[winTeam]->GetStId(), victoryCount), NULL) == aSQLITE_ERROR)
						stDb->PrintError();
				}
				else
				{
					victoryCount = ((*stats.GetFirst())["victory"].ToLong() + 1);
					if (stDb->Exec(aString::Format("UPDATE commander_stats SET victory = '%d' WHERE player = '%d'", victoryCount, this->m_commander[winTeam]->GetStId()), NULL) == aSQLITE_ERROR)
						stDb->PrintError();
				}
			}
			else
				stDb->PrintError();
#else
			aTextConfig tccfg("TeamCommander.ini", true);
			tccfg.Set("Victory.\"" + this->m_commander[winTeam]->GetPlayerName() + "\"", tccfg.GetData("Victory.\"" + this->m_commander[winTeam]->GetPlayerName() + "\"", 0).ToLong() + 1);
  #ifdef DEVMSG
			stConsole::Out("%ls is now have %d victory for being a commander. [%d;%d]\n",
				this->m_commander[winTeam]->GetPlayerName().GetString(),
				tccfg.GetData("Victory.\"" + this->m_commander[winTeam]->GetPlayerName() + "\"", 0).ToLong(),
				(GetTickCount() - SystemTime - nc_cNetwork::PServerConnection->RemoteList[this->m_commander[winTeam]->GetPlayerData()->PlayerId]->JoinTime) / 1000,
				cGame->GameDuration_Seconds
			);
  #endif
#endif
		}
	}
}
void gzTeamCommanderClass::Think()
{
	if (this->m_settings->m_enable)
	{
		if (cGame->Is_Gameplay_Permitted() && cGame->GameDuration_Seconds != this->m_lastSelection)
		{
			for (int i = 0; i < 2; i++)
			{
				if (this->m_commander[i] != NULL)
				{
					if (i != this->m_commander[i]->GetPlayerData()->PlayerType.Get())
					{
						stConsole::In("msg %ls is no longer the Team Commander of %s.",
							this->m_commander[i]->GetPlayerData()->PlayerName.m_Buffer,
							((i == 0) ? "Nod" : "GDI")
						);
						this->m_commander[i]    = NULL;
						this->m_commandTime[i]  = 0.0f;
					}
					AddFrameTime(this->m_commandTime[i], true);
				}
			}

#if ISDEV()
			if ((cGame->GameDuration_Seconds % 2) == 0)
#else
			if ((cGame->GameDuration_Seconds % 15) == 0)
#endif
			{
				if (this->m_commander[0] == NULL || this->m_commander[1] == NULL)
				{
					aVector<gzPlayer *> ComList[2];
					for (nc_GenericSLNode<nc_cPlayer> *PlayerList = nc_cPlayerManager::PlayerList->HeadNode; PlayerList != NULL; PlayerList = PlayerList->NodeNext)
					{
						if (PlayerList->NodeData != NULL && PlayerList->NodeData->IsActive)
						{
							if (this->m_commander[PlayerList->NodeData->PlayerType.Get()] == NULL)
							{
								for (unsigned int j = 0; j < this->m_settings->m_commanderList.Count(); j++)
								{
									if (this->m_settings->m_commanderList[j] == PlayerList->NodeData->PlayerName.m_Buffer)
									{
										gzPlayer *gzData = gzPlayerManager::Find(PlayerList->NodeData);
										if (gzData)
										{
											ComList[PlayerList->NodeData->PlayerType.Get()].Add(gzData);
											break;
										}
									}
								}
							}
						}
					}
					for (int i = 0; i < 2; i++)
					{
						if (this->m_commander[i] == NULL && ComList[i].Count() > 0)
						{
							this->m_commander[i] = ComList[i][stRandom.Get_Int(0, ComList[i].Count() - 1)];
#if VERC(1, 1, 0)
							aSQLite3Result stats;
							if (stDb->Exec(aString::Format("SELECT * FROM commander_stats WHERE player = '%d'", this->m_commander[i]->GetStId()), &stats) != aSQLITE_ERROR)
							{
								if (stats.Count() == 0)
								{
									stConsole::In("msg %ls is now the %s Team Commander, and has no victory under his belt.",
										this->m_commander[i]->GetPlayerName().GetString(),
										((i == 0) ? "Nod" : "GDI")
									);
								}
								else
								{
									stConsole::In("msg %ls is now the %s Team Commander, and has %d %s under his belt.",
										this->m_commander[i]->GetPlayerName().GetString(),
										((i == 0) ? "Nod" : "GDI"),
										(*stats.GetFirst())["victory"].ToLong(),
										((*stats.GetFirst())["victory"].ToLong() > 1) ? "victories" : "victory"
									);
								}
							}
							else
								stDb->PrintError();
#else
							aTextConfig cfg("TeamCommander.ini");
							int victoryCount = cfg.GetData("Victory.\"" + this->m_commander[i]->GetPlayerName() + "\"", 0).ToLong();
							stConsole::In("msg %ls is now the %s Team Commander, and has %d %s under his belt.",
								this->m_commander[i]->GetPlayerName().GetString(),
								((i == 0) ? "Nod" : "GDI"),
								victoryCount,
								(victoryCount > 1) ? "victories" : "victory"
							);
#endif
							PagePlayer(this->m_commander[i]->GetPlayerData()->PlayerId, "[Commander]: Currently the Team Pool has %d credit(s).", this->m_teamPool[i]);
							this->m_canRequestVehicleNotify[i] = true;
							this->m_commandTime[i] = 0.0f;
						}
					}
				}
				this->m_lastSelection = cGame->GameDuration_Seconds;
			}
		}
		for (int i = 0; i < 2; i++)
		{
			// Allow buy vehicle notice
			if (cGame->GameDuration_Seconds > this->m_nextPurchaseTime[i])
			{
				if (this->m_canRequestVehicleNotify[i] && this->m_commander[i])
				{
					PagePlayer(this->m_commander[i]->GetPlayerData()->PlayerId, "[Commander]: You can now use the Commander Buy Vehicle ability.");
					this->m_canRequestVehicleNotify[i] = false;
				}
			}

			// Buildings health check
			nc_BaseControllerClass *base = nc_BaseControllerClass::Find_Base(i);
			if (base)
			{
				for (int j = 0; j < base->BuildingList.Count(); j++)
				{
					nc_BuildingGameObj *Building = (nc_BuildingGameObj *)base->BuildingList[j];
					float Percent = (Building->Defense.Health.Get() / Building->Defense.HealthMax.Get());
					if (!Building->Destroyed && Percent <= 0.5f)
					{
						if ((cGame->GameDuration_Seconds - this->m_buildingWarn[i][j]) >= 15)
						{
							if (this->m_commander[i])
							{
								this->m_buildingWarn[i][j] = cGame->GameDuration_Seconds;
								this->m_lastBuildingWarn[i] = j;
								if (Percent <= 0.2f)
									PagePlayer(this->m_commander[i]->GetPlayerData()->PlayerId, "[Commander]: Critical - Building %s is below 20%% of it's max health. You may request repairs by using \"!com rb\" or keyboard shortcut(RequestRepairs - numpad *). (%.0f/%.0f)", gzTranslator->Get(Building), Building->Defense.Health.Get(), Building->Defense.HealthMax.Get());
								else if (Percent <= 0.5f)
									PagePlayer(this->m_commander[i]->GetPlayerData()->PlayerId, "[Commander]: Warning - Building %s is below 50%% of it's max health. You may request repairs by using \"!com rb\" or keyboard shortcut(RequestRepairs - numpad *). (%.0f/%.0f)", gzTranslator->Get(Building), Building->Defense.Health.Get(), Building->Defense.HealthMax.Get());
							}
						}
					}
				}
			}
		}
		for (nc_GenericSLNode<nc_SmartGameObj> *vehList = nc_GameObjManager::SmartGameObjList->HeadNode; vehList != NULL; vehList = vehList->NodeNext)
		{
			if (vehList->NodeData->As_VehicleGameObj() && vehList->NodeData->As_VehicleGameObj()->SeatsList.Length() > 0)
			{
				if (this->m_commander[vehList->NodeData->PlayerType] && this->m_commanderPurchase[vehList->NodeData->PlayerType])
				{
					if ((!vehList->NodeData->As_VehicleGameObj()->Delivered || (GetTickCount() - SystemTime - vehList->NodeData->CreationTime) < 1000) && vehList->NodeData->As_VehicleGameObj()->Owner.Reference == this->m_commander[vehList->NodeData->PlayerType]->GetPlayerData()->Owner.Reference)
					{
						vehList->NodeData->As_VehicleGameObj()->OwnerExpireCounter = 99999.0f;
						this->m_commanderPurchase[vehList->NodeData->PlayerType] = false;
#ifdef DEVMSG
						stConsole::Out("Set %s owner time to %f...\n",
							vehList->NodeData->definition->Get_Name(),
							vehList->NodeData->As_VehicleGameObj()->OwnerExpireCounter
						);
#endif
					}
				}
			}
		}
	}
}
void gzTeamCommanderClass::Player_Left(gzEventPlayerBase &evt)
{
	if (this->m_commander[evt.GetPlayer()->PlayerType.Get()] && this->m_commander[evt.GetPlayer()->PlayerType.Get()]->GetPlayerData() == evt.GetPlayer())
	{
		nc_BuildingGameObj *Refinery = nc_BaseControllerClass::Find_Base(evt.GetPlayer()->PlayerType.Get())->Find_Building(nc_REFINERY);
		if (Refinery && Refinery->As_RefineryGameObj())
		{
			if (((nc_TrackedVehicleDefClass *)nc_DefinitionMgrClass::Find_Definition(((nc_VehicleGameObjDef *)nc_DefinitionMgrClass::Find_Definition(((nc_RefineryGameObjDef *)Refinery->definition)->Harvester, true))->PhysDefID, true))->MaxEngineTorque != this->m_harvMaxEngineTorque[evt.GetPlayer()->PlayerType.Get()])
				((nc_TrackedVehicleDefClass *)nc_DefinitionMgrClass::Find_Definition(((nc_VehicleGameObjDef *)nc_DefinitionMgrClass::Find_Definition(((nc_RefineryGameObjDef *)Refinery->definition)->Harvester, true))->PhysDefID, true))->MaxEngineTorque = this->m_harvMaxEngineTorque[evt.GetPlayer()->PlayerType.Get()];
		}
		this->m_commander[evt.GetPlayer()->PlayerType.Get()] = NULL;
		stConsole::In("msg %ls is no longer the Team Commander of %s.", evt.GetPlayer()->PlayerName.m_Buffer, evt.GetPlayer()->PlayerType.Get() == 0 ? "Nod" : "GDI");
	}
}
void gzTeamCommanderClass::Object_Created(gzEventObjectCreate &evt)
{
	// Commander harvester control
	if (evt.m_obj->As_VehicleGameObj())
	{
		nc_BaseControllerClass *Base = nc_BaseControllerClass::Find_Base(evt.m_obj->As_DamageableGameObj()->PlayerType);
		if (Base)
		{
			nc_BuildingGameObj *Refinery = Base->Find_Building(nc_REFINERY);
			if (Refinery && !Refinery->As_RefineryGameObj()->Harvester)
			{
				if (!evt.m_obj->As_VehicleGameObj()->Owner.Reference && ((nc_RefineryGameObjDef *)Refinery->definition)->Harvester == evt.m_obj->definition->Get_ID())
					((nc_TrackedVehicleDefClass *)nc_DefinitionMgrClass::Find_Definition(((nc_VehicleGameObjDef *)nc_DefinitionMgrClass::Find_Definition(((nc_RefineryGameObjDef *)Refinery->definition)->Harvester, true))->PhysDefID, true))->MaxEngineTorque = gzTeamCommander->m_harvMaxEngineTorque[evt.m_obj->As_DamageableGameObj()->PlayerType];
			}
		}
	}
}
bool gzTeamCommanderClass::CanBecome(int Id)
{
	gzPlayer *gzData = gzPlayerManager::Find(Id);
	if (gzData && this->m_settings->m_commanderList.Count() > 0)
	{
		for (unsigned int i = 0; i < this->m_settings->m_commanderList.Count(); i++)
		{
			if (gzData->GetPlayerName().Compare(this->m_settings->m_commanderList[i].GetString()))
				return true;
		}
	}
	return false;
}
bool gzTeamCommanderClass::IsCommander(int Team, int pID)
{
	if (this->m_commander[Team] == NULL)
		return false;
	if ((Team != 0 && Team != 1) || this->m_commander[Team]->GetPlayerData()->PlayerId != pID)
		return false;
	return true;
}
void gzTeamCommanderClass::Log(const char *Message, ...)
{
	va_list args;
	va_start(args, Message);
	int size = _vscprintf(Message, args);
	char *LogMsg = new char[size +1];
	vsprintf(LogMsg, Message, args);
	aDateTime now = aDateTime::Now();
	aFile log(aString::Format("tc_%d-%d-%4d.txt",now.GetMonth(), now.GetDay(), now.GetYear()), "a");
	if (log.IsOpened())
		log.Write(aString::Format("[%02d:%02d:%02d] %s", now.GetHour(), now.GetMinute(), now.GetSecond(), LogMsg));
	delete [] LogMsg;
}

class Chat_TeamCommander : public gzChatCommandBase {
	void Activate(const wchar_t *msg, int type, int sender, int receiver, aWideString &ret) 
	{
		// Game mode check
		switch (gzGameMgr->m_settings->m_GameMode)
		{
			case GAMEMODE_SNIPER:
			case GAMEMODE_500SNIP:
			case GAMEMODE_DM:
				PagePlayer(sender, "Team commander commands are not available in %s game mode.", gzGameMgr->GetGame()->ModeName());
		}

		if (gzTeamCommander->m_settings->m_enable == false)
		{
			PagePlayer(sender, "Team Commander is disabled.");
			return;
		}
		gzPlayer *gzData = gzPlayerManager::Find(sender);
		if (!gzData)
			return;
		int Team = gzData->GetPlayerData()->PlayerType.Get();
		if (!*msg)
		{
			if (gzTeamCommander->m_commander[Team] == NULL)
				PagePlayer(sender, "Your team currently has no Team Commander.");
			else
				PagePlayer(sender, "Your current Team Commander is: %ls", gzTeamCommander->m_commander[Team]->GetPlayerName().GetString());
		}
		else
		{
			if (!gzTeamCommander->IsCommander(Team, gzData->GetPlayerData()->PlayerId))
			{
				PagePlayer(sender, "You are not the Team Commander of your team.");
				return;
			}
			aToken tokmsg(msg);
			aString cmd = tokmsg.gettok(1, ' ').GetData();
			if (cmd == "harv" || cmd == "harvester")
			{
				nc_RefineryGameObj* Refinery = (nc_BaseControllerClass::Find_Base(Team)->Find_Building(nc_REFINERY)) ? nc_BaseControllerClass::Find_Base(Team)->Find_Building(nc_REFINERY)->As_RefineryGameObj() : NULL;
				if (Refinery)
				{
					if (!Refinery->Harvester || !Refinery->Harvester->Vehicle)
						PagePlayer(sender, "[Commander] The Harvester for your team cannot be found.");

					else if (tokmsg.gettok(2, ' ').GetData() == "start")
					{
						if (((nc_TrackedVehicleDefClass *)Refinery->Harvester->Vehicle->Physics->Definition)->MaxEngineTorque == 0.0f)
						{
							((nc_TrackedVehicleDefClass *)Refinery->Harvester->Vehicle->Physics->Definition)->MaxEngineTorque = gzTeamCommander->m_harvMaxEngineTorque[Team];
							gzCommands->Enable_Engine(Refinery->Harvester->Vehicle, true);
							PagePlayer(sender, "[Commander] The Harvester for your team has returned to normal.");
							gzTeamCommander->Log("%ls issued a Harvester Start command.\n", gzTeamCommander->m_commander[Team]->GetPlayerName().GetString());
						}
						else
							PagePlayer(sender, "[Commander] The Harvester is not stopped.");
					}

					else if (tokmsg.gettok(2, ' ').GetData() == "stop")
					{
						if (((nc_TrackedVehicleDefClass *)Refinery->Harvester->Vehicle->Physics->Definition)->MaxEngineTorque == gzTeamCommander->m_harvMaxEngineTorque[Team])
						{
							((nc_TrackedVehicleDefClass *)Refinery->Harvester->Vehicle->Physics->Definition)->MaxEngineTorque = 0.0f;
							gzCommands->Enable_Engine(Refinery->Harvester->Vehicle, false);
							PagePlayer(sender, "[Commander] The Harvester for your team has been stopped.");
							gzTeamCommander->Log("%ls issued a Harvester Stop command.\n", gzTeamCommander->m_commander[Team]->GetPlayerName().GetString());
						}
						else
							PagePlayer(sender, "The Harvester was already stopped.");
					}

					else
						PagePlayer(sender, "Usage: !com %s [start|stop]", cmd.GetString());
				}
				else
					PagePlayer(sender, "[Commander] Refinery for your team not found.");
			}
			else if (cmd == "transfer")
			{
				if (tokmsg.numtok(' ') < 2)
				{
					PagePlayer(sender, "Usage: !com transfer [player]");
					return;
				}

				// Nickname unique check
				aWideString nickw = tokmsg.gettok(2, ' ').GetData();
				int nickCount = gzPlayerManager::FindByPartNameCount(nickw);
				if (nickCount >= 2 && !gzPlayerManager::Find(nickw))
				{
					PagePlayer(sender, "Found %d result(s) for \"%ls\". Please specify a unique string for the nickname.", nickCount, nickw.GetString());
					return;
				}

				gzPlayer *newData = gzPlayerManager::FindByPartName(nickw);
				if (!newData)
					PagePlayer(sender, "%s not found.", nickw.GetString());
				else
				{
					if (Team != newData->GetPlayerData()->PlayerType.Get())
						PagePlayer(sender, "%ls is not on your team.", newData->GetPlayerName().GetString());
					else
					{
						if (gzTeamCommander->CanBecome(newData->GetPlayerData()->PlayerId))
						{
							gzTeamCommander->Log("%ls issued a Transfer command to %ls\n",
								gzTeamCommander->m_commander[Team]->GetPlayerName().GetString(),
								newData->GetPlayerName().GetString()
							);
							gzTeamCommander->m_commander[Team] = newData;
							PagePlayer(sender, "[Commander] You've transfered your Team Commander status to %ls.", newData->GetPlayerName().GetString());
							stConsole::In("msg %ls is now %s Team Commander.", newData->GetPlayerName().GetString(), (Team == 0) ? "Nod" : "GDI");
							PagePlayer(newData->GetPlayerData()->PlayerId, "Currently Team Pool has %d credit(s).", gzTeamCommander->m_teamPool[Team]);
							gzTeamCommander->m_canRequestVehicleNotify[Team] = true;
							gzTeamCommander->m_commandTime[Team] = 0.0f;
						}
						else
							PagePlayer(sender, "[Commander] %ls can not become a Team Commander.", newData->GetPlayerName().GetString());
					}
				}
			}
			else if (cmd == "o" || cmd == "order")
			{
				if (tokmsg.numtok(' ') < 2)
					PagePlayer(sender, "Usage: !com %s [message]", cmd.GetString());
				else
				{
					stConsole::In("tpage %d [Order from Commander]: %s", Team, tokmsg.gettok(2, tokmsg.numtok(' ') - 1, ' ').GetData().GetString());
					gzTeamCommander->Log("%ls issued a Order command: %s\n",
						gzTeamCommander->m_commander[Team]->GetPlayerName().GetString(),
						tokmsg.gettok(2, tokmsg.numtok(' ') - 1, ' ').GetData().GetString()
					);
				}
			}
			else if (cmd == "w" || cmd == "warn" || cmd == "warning")
			{
				if (tokmsg.numtok(' ') < 2)
					PagePlayer(sender, "Usage: !com %s [message]",cmd.GetString());
				else
				{
					gzTeamCommander->Log("%ls issued a Warning command: %s\n",
						gzTeamCommander->m_commander[Team]->GetPlayerName().GetString(),
						tokmsg.gettok(2, tokmsg.numtok(' ') - 1, ' ').GetData().GetString()
					);
					stConsole::In("tpage %d [Warning from Commander]: %s", Team, tokmsg.gettok(2, tokmsg.numtok(' ') - 1, ' ').GetData().GetString());
				}
			}
			else if (cmd == "buyveh")
			{
				if (gzTeamCommander->m_nextPurchaseTime[Team] >cGame->GameDuration_Seconds)
				{
					PagePlayer(sender, "You have to wait %s for next purchase.", SecsToAscTime(gzTeamCommander->m_nextPurchaseTime[Team] - cGame->GameDuration_Seconds).GetString());
					return;
				}
				int ResponseType = -1;
				float Distance = 99999.0f;
				nc_SimpleGameObj *Terminal = NULL;
				nc_Vector3 pos;
				gzData->GetPlayerData()->Owner.Reference->obj->As_PhysicalGameObj()->Get_Position(&pos);
				for (nc_GenericSLNode<nc_BaseGameObj> *objList = nc_GameObjManager::GameObjList->HeadNode; objList != NULL; objList = objList->NodeNext)
				{
					if (objList->NodeData->definition->Get_Class_ID() == 0x3004)
					{
						if (gzData->GetPlayerData()->Owner.Reference->obj->As_DamageableGameObj()->PlayerType == (((nc_SimpleGameObjDef *)objList->NodeData->definition)->PlayerTerminalType ^ 1))
						{
							nc_Vector3 ptPos;
							objList->NodeData->As_PhysicalGameObj()->Get_Position(&ptPos);
							float ptDistance = abs(ptPos.X - pos.X) + abs(ptPos.X - pos.X);
							if (ptDistance < Distance && gzData->GetPlayerData()->Owner.Reference->obj->As_SmartGameObj()->Is_Object_Visible(objList->NodeData->As_PhysicalGameObj()))
							{
								Distance = ptDistance;
								Terminal = (nc_SimpleGameObj *)objList->NodeData;
							}
						}
					}
				}
				if (!Terminal || Distance > 4.0f)
					PagePlayer(sender, "[Commander]: Vehicle purchase denied because you're either not close to any Purchase Terminal or no visible Purchase Terminal in your screen.");

				else if (Terminal->PlayerType != gzData->GetPlayerData()->Owner.Reference->obj->As_DamageableGameObj()->PlayerType)
					PagePlayer(sender, "[Commander]: Vehicle purchase denied because the closest Purchase Terminal is belongs to enemy.");

				else
				{
					int ID = tokmsg.gettok(2,' ').ToLong() - 1;
					nc_PurchaseSettingsDefClass *PurchaseDef = nc_PurchaseSettingsDefClass::Find_Definition(1, Team ^ 1);
					if (ID < 0 || ID > 10 || !nc_DefinitionMgrClass::Find_Definition(PurchaseDef->presetids[ID], true))
						ResponseType = 4;
					else if (!nc_MapMgrClass::EnableVTOL && ((nc_VehicleGameObjDef *)nc_DefinitionMgrClass::Find_Definition(PurchaseDef->presetids[ID], true))->VehicleType == 3)
						ResponseType = 4;
					else
					{
						nc_BaseControllerClass *Base = nc_BaseControllerClass::Find_Base(gzData->GetPlayerData()->Owner.Reference->obj->As_DamageableGameObj()->PlayerType);
						if (Base)
						{
							unsigned int Cost = PurchaseDef->costs[ID] * (int)Base->OperationTimeFactor;
							nc_BuildingGameObj *Factory = Base->Find_Building(nc_VEHICLE_FACTORY);
							if (Factory && Factory->As_VehicleFactoryGameObj())
							{
								if (Factory->As_VehicleFactoryGameObj()->IsGenerating)
									ResponseType = 3;
								else if (Factory->Destroyed)
									ResponseType = 3;
								else if (gzTeamCommander->m_teamPool[Team] < Cost)
								{
									PagePlayer(sender, "Team Pool has %d credit(s) but %s requires %d credit(s).",
										gzTeamCommander->m_teamPool[Team],
										gzTranslator->Get(nc_DefinitionMgrClass::Find_Definition(PurchaseDef->presetids[ID], true)->Get_Name()),
										Cost
									);
								}
								else
								{
									Factory->As_VehicleFactoryGameObj()->Request_Vehicle(PurchaseDef->presetids[ID], 5.0f, gzData->GetPlayerData()->Owner.Reference->obj->As_SoldierGameObj());
									gzTeamCommander->m_teamPool[Team] -= Cost;
									gzTeamCommander->m_nextPurchaseTime[Team] = cGame->GameDuration_Seconds + gzTeamCommander->m_settings->m_vehiclePurchaseInterval;
									gzTeamCommander->m_canRequestVehicleNotify[Team] = true;
									PagePlayer(sender, "[Commander] Request accepted(%s). You have to wait %s for next request.",
										gzTranslator->Get(nc_DefinitionMgrClass::Find_Definition(PurchaseDef->presetids[ID], true)->Get_Name()),
										SecsToAscTime(gzTeamCommander->m_settings->m_vehiclePurchaseInterval).GetString()
									);
									PagePlayer(sender, "You have used %d credit(s) from TeamPool. TeamPool is now have %d credit(s).", Cost, gzTeamCommander->m_teamPool[Team]);
									gzTeamCommander->Log("%ls issued a Buyveh command: %s\n",
										gzTeamCommander->m_commander[Team]->GetPlayerName().GetString(),
										gzTranslator->Get(nc_DefinitionMgrClass::Find_Definition(PurchaseDef->presetids[ID], true)->Get_Name())
									);
									gzTeamCommander->m_commanderPurchase[Team] = true;
								}
							}
						}
					}
				}
				if (ResponseType > 0)
				{
					Send_Purchase_Response(sender, ResponseType);
				}
			}
			else if (cmd == "rb" || cmd == "repairbuilding")
			{
				if (gzTeamCommander->m_lastBuildingWarn[Team] == -1)
					return;
				nc_BuildingGameObj *Building = nc_BaseControllerClass::Find_Base(Team)->BuildingList[gzTeamCommander->m_lastBuildingWarn[Team]];
				if (Building->Destroyed)
				{
					gzTeamCommander->m_lastBuildingWarn[Team] = -1;
					return;
				}
				gzTeamCommander->Log("%ls issued a Repair-Building command: %s\n",
					gzTeamCommander->m_commander[Team]->GetPlayerName().GetString(),
					gzTranslator->Get(Building->definition->Get_Name())
				);
				float HealthValue = Building->Defense.Health.Get() / Building->Defense.HealthMax.Get();
				if (HealthValue <= 0.2f)
					stConsole::In("tpage %d [Commander]: Critical: Please repair %s because it's lower than 20%% of it's max health!", Team, gzTranslator->Get(Building->definition->Get_Name()));
				else if (HealthValue <= 0.5f)
					stConsole::In("tpage %d [Commander]: Warning: Please repair %s because it's lower than 50%% of it's max health!", Team, gzTranslator->Get(Building->definition->Get_Name()));
			}
			else if (cmd == "donate")
			{
				int Amount = tokmsg.gettok(3, ' ').ToLong();
				if (tokmsg.numtok(' ') < 3)
					PagePlayer(sender, "Usage: !com donate [player] [amount]");

				else if (Amount < 0)
					PagePlayer(sender, "Invalid amount.");

				else if ((unsigned)Amount > gzTeamCommander->m_teamPool[gzData->GetPlayerData()->PlayerType.Get()])
					PagePlayer(sender, "Your Team Pool does NOT have %d credit(s). It currently has %d credit(s).", Amount, gzTeamCommander->m_teamPool[gzData->GetPlayerData()->PlayerType.Get()]);

				else
				{
					// Nickname unique check
					int nickCount = gzPlayerManager::FindByPartNameCount(tokmsg.gettok(2, ' ').GetData());
					if (nickCount >= 2 && !gzPlayerManager::Find(tokmsg.gettok(2, ' ').GetData()))
					{
						PagePlayer(sender, "Found %d result(s) for \"%s\". Please specify a unique string for the nickname.", nickCount, tokmsg.gettok(2, ' ').GetData().GetString());
						return;
					}

					gzPlayer *newData = gzPlayerManager::FindByPartName(tokmsg.gettok(2, ' ').GetData());
					if (newData == NULL)
						PagePlayer(sender, "%ls not found.",tokmsg.gettok(2, ' ').GetData().GetString());

					else if (gzData->GetPlayerData()->PlayerType.Get() != newData->GetPlayerData()->PlayerType.Get())
						PagePlayer(sender, "You can NOT donate to the enemy from the Team Pool!");

					else
					{
						newData->GetPlayerData()->Money += (float)Amount;
						newData->GetPlayerData()->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
						gzTeamCommander->m_teamPool[gzData->GetPlayerData()->PlayerType.Get()] -= Amount;
						PagePlayer(sender, "[Commander] You've donated %d credit(s) to %ls from the Team Pool.", Amount, newData->GetPlayerData()->PlayerName.m_Buffer);
						PagePlayer(newData->GetPlayerData()->PlayerId, "You've received %d credit(s) from the Team Pool by the order of your Team Commander.", Amount);
						gzTeamCommander->Log("%ls issued a Donate command: %d to %ls\n",
							gzTeamCommander->m_commander[Team]->GetPlayerName().GetString(),
							Amount,
							newData->GetPlayerData()->PlayerName.m_Buffer
						);
					}
				}
			}
		}
	}
};

class Chat_TeamPool : public gzChatCommandBase {
	void Activate(const wchar_t *msg, int type, int sender, int receiver, aWideString &ret) 
	{
		// Game mode check
		switch (gzGameMgr->m_settings->m_GameMode)
		{
			case GAMEMODE_SNIPER:
			case GAMEMODE_500SNIP:
			case GAMEMODE_DM:
				PagePlayer(sender, "Team Commander commands are not available in %s game mode.", gzGameMgr->GetGame()->ModeName());
		}

		if (!cGame->Is_Gameplay_Permitted())
		{
			PagePlayer(sender, "You can not donate to Team Pool while gameplay is pending.");
			return;
		}

		nc_cPlayer *pData = nc_cPlayerManager::Find_Player(sender);
		if (!pData)
			return;
		if (!*msg)
			PagePlayer(sender, "Currently the Team Pool has %d credit(s).", gzTeamCommander->m_teamPool[pData->PlayerType.Get()]);
		else
		{
			aToken tokmsg(msg);
			int Credit = tokmsg.gettok(1, ' ').ToLong();
			if (tokmsg.gettok(1, ' ').GetData() == "all")
				Credit = (int)pData->Money.Get();
			if (Credit < 1)
				PagePlayer(sender, "Invalid amount.");
			else if (Credit > pData->Money.Get())
				PagePlayer(sender, "You don't have %d credit(s).", Credit);
			else
			{
				gzTeamCommander->m_teamPool[pData->PlayerType.Get()] += Credit;
				pData->Money -= (float)Credit;
				pData->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
				PagePlayer(sender, "The Team Pool now has %d credit(s). Thank you for your contribution!", gzTeamCommander->m_teamPool[pData->PlayerType.Get()]);
				if (gzTeamCommander->m_commander[pData->PlayerType.Get()] != NULL && gzTeamCommander->m_commander[pData->PlayerType.Get()]->GetPlayerData() != pData)
					PagePlayer(gzTeamCommander->m_commander[pData->PlayerType.Get()]->GetPlayerData()->PlayerId, "%ls has donated %d credit(s) to the Team Pool. Currently, the Team Pool has %d credit(s).", pData->PlayerName.m_Buffer, Credit, gzTeamCommander->m_teamPool[pData->PlayerType.Get()]);
			}
		}
	}
};

/********************/
/* Console commands */
/********************/
class SetTeamCommanderConsoleFunction : public gzConsoleCommand {
public:
	char *Get_Name(void)
	{
		return "setteamcommander";
	}
	char *Get_Alias(void)
	{
		return "settc";
	}
	char *Get_Help(void)
	{
		return "SETTEAMCOMMANDER - Force <player> to become a team commander.";
	}
	void Activate(char *text)
	{
		if (!*text)
			return;
		gzPlayer *gzData = gzPlayerManager::Find(atoi(text));
		if (!gzData)
			stConsole::Out("Player not found.\n");
		else
		{
			gzTeamCommander->Log("setteamcommander console issued; player: %ls\n", gzData->GetPlayerName().GetString());
			gzTeamCommander->m_commander[gzData->GetPlayerData()->PlayerType.Get()] = gzData;
			stConsole::In("msg %ls is now %s Team Commander.", gzData->GetPlayerName().GetString(), (Team == 0) ? "Nod" : "GDI");
			PagePlayer(gzData->GetPlayerData()->PlayerId, "Currently Team Pool has %d credit(s).", gzTeamCommander->m_teamPool[Team]);
			gzTeamCommander->m_canRequestVehicleNotify[gzData->GetPlayerData()->PlayerType.Get()] = true;
		}
	}
};
SetTeamCommanderConsoleFunction setteamcommander;
