#include "stinc.h"
#include "stmisc.h"
#include "stgame.h"
#include "sttranslate.h"
#include "stplayer.h"
#include "stweapon.h"
#include "sttibcrystal.h"
#include "stveteran.h"
#include "stconsolecommands.h"
#include "stcrate.h"

#pragma warning(disable: 4127)

/***************/
/* Percentages */
/***************/
gzCratePercentStruct::gzCratePercentStruct()
{
	this->Reset(CRATE_SOLDIER | CRATE_VEHICLE);
}
void gzCratePercentStruct::Reset(int Type)
{
	if ((Type & CRATE_SOLDIER) == CRATE_SOLDIER)
	{
		this->Weapon = 150;
		this->Money = 120;
		this->Points = 100;
		this->Vehicle = 80;
		this->Death = 80;
		this->Ammo = 50;
		this->Armor = 60;
		this->Health = 60;
		this->Character = 80;
		this->ButterFingers = 40;
		this->Spy = 30;
		this->Refill = 60;
		this->Beacon = 40;
		this->Thief = 50;
		this->WeaponPack = 0;
		this->Donation = 0;
		this->ChemSuit = 0;
		this->ExplosiveSuit = 0;
		this->Medic = 0;
		this->MetalGear = 0;
		this->EnemyThief = 0;
		this->Hover = 0;
		this->StealthMine = 0;
		this->Veteran = 0;
	}
	if ((Type & CRATE_VEHICLE) == CRATE_VEHICLE)
	{
		this->vAntiEnemy = 100;
		this->vMaxArmor = 200;
		this->vMaxHealth = 200;
		this->vAutoRepair = 150;
		this->vRepair = 150;
		this->vSpy = 100;
		this->vDemoTruck = 100;
		this->vSteal = 0;
	}
}


/******************/
/* Crate settings */
/******************/
gzSettingsCrateClass::gzSettingsCrateClass()
{
	this->m_percent = new gzCratePercentStruct;
}
void gzSettingsCrateClass::Delete()
{
	delete this->m_percent;
	this->m_percent = NULL;
	this->m_posList.Clear();
}
void gzSettingsCrateClass::Load()
{
	this->m_cfg = new aTextConfig("Crate.ini");
	if (!this->m_cfg->IsLoaded())
		stConsole::Out("[Error] Crate.ini can not be loaded or not found. Default settings will be used.\n");

	// Cleanups
	this->m_percent->Reset(CRATE_SOLDIER | CRATE_VEHICLE);

	// General settings
	this->m_enable					= this->Load_Bool("General", "Enable", false);
	this->m_enableYellowCrate		= this->Load_Bool("General", "EnableYellowCrate", true);
	this->m_existCheckTime			= this->Load_Float("General", "CrateCheck", 30.0f);
	this->m_existCheckVariationMin	= this->Load_Int("General", "CrateCheckVariationMin", 5);
	this->m_existCheckVariationMax	= this->Load_Int("General", "CrateCheckVariationMax", 30);
	this->m_liveTime				= this->Load_Float("General", "CrateLive", 900.0f);

	if (this->m_enable)
		this->m_enableYellowCrate = false;

	// Percentage
	int totalPercent = 0;
	totalPercent += this->m_percent->Weapon			= this->Load_Int("Percentage_Soldier", "Weapon", 0);
	totalPercent += this->m_percent->Money			= this->Load_Int("Percentage_Soldier", "Money", 0);
	totalPercent += this->m_percent->Points			= this->Load_Int("Percentage_Soldier", "Points", 0);
	totalPercent += this->m_percent->Vehicle		= this->Load_Int("Percentage_Soldier", "Vehicle", 0);
	totalPercent += this->m_percent->Death			= this->Load_Int("Percentage_Soldier", "Death", 0);
	totalPercent += this->m_percent->Ammo			= this->Load_Int("Percentage_Soldier", "Ammo", 0);
	totalPercent += this->m_percent->Armor			= this->Load_Int("Percentage_Soldier", "Armor", 0);
	totalPercent += this->m_percent->Health			= this->Load_Int("Percentage_Soldier", "Health", 0);
	totalPercent += this->m_percent->Character		= this->Load_Int("Percentage_Soldier", "Character", 0);
	totalPercent += this->m_percent->ButterFingers	= this->Load_Int("Percentage_Soldier", "ButterFingers", 0);
	totalPercent += this->m_percent->Refill			= this->Load_Int("Percentage_Soldier", "Refill", 0);
	totalPercent += this->m_percent->Beacon			= this->Load_Int("Percentage_Soldier", "Beacon", 0);
	totalPercent += this->m_percent->Spy			= this->Load_Int("Percentage_Soldier", "Spy", 0);
	totalPercent += this->m_percent->Thief			= this->Load_Int("Percentage_Soldier", "Thief", 0);
	totalPercent += this->m_percent->WeaponPack		= this->Load_Int("Percentage_Soldier", "WeaponPack", 0);
	totalPercent += this->m_percent->Donation		= this->Load_Int("Percentage_Soldier", "Donation", 0);
	totalPercent += this->m_percent->ChemSuit		= this->Load_Int("Percentage_Soldier", "ChemSuit", 0);
	totalPercent += this->m_percent->ExplosiveSuit	= this->Load_Int("Percentage_Soldier", "ExplosiveSuit", 0);
	totalPercent += this->m_percent->Medic			= this->Load_Int("Percentage_Soldier", "Medic", 0);
	totalPercent += this->m_percent->MetalGear		= this->Load_Int("Percentage_Soldier", "MetalGear", 0);
	totalPercent += this->m_percent->EnemyThief		= this->Load_Int("Percentage_Soldier", "EnemyThief", 0);
	totalPercent += this->m_percent->Hover			= this->Load_Int("Percentage_Soldier", "Hover", 0);
	totalPercent += this->m_percent->StealthMine	= this->Load_Int("Percentage_Soldier", "StealthMine", 0);
	totalPercent += this->m_percent->Veteran		= this->Load_Int("Percentage_Soldier", "Veteran", 0);
	totalPercent += this->m_percent->Visceroid		= this->Load_Int("Percentage_Soldier", "Visceroid", 0);
	totalPercent += this->m_percent->BaseDefensePower = this->Load_Int("Percentage_Soldier", "BaseDefensePower", 0);

	if (totalPercent != 1000)
	{
		this->m_percent->Reset(CRATE_SOLDIER);
		stConsole::Out("Total percentage for soldier crates is %d instead of 1000 and values have been reset to default.\n",totalPercent);
	}

	totalPercent = 0;
	totalPercent += this->m_percent->vAntiEnemy = this->Load_Int("Percentage_Vehicle", "AntiEnemy", 0);
	totalPercent += this->m_percent->vMaxArmor = this->Load_Int("Percentage_Vehicle", "MaxArmor", 0);
	totalPercent += this->m_percent->vMaxHealth = this->Load_Int("Percentage_Vehicle", "MaxHealth", 0);
	totalPercent += this->m_percent->vAutoRepair = this->Load_Int("Percentage_Vehicle", "AutoRepair", 0);
	totalPercent += this->m_percent->vRepair = this->Load_Int("Percentage_Vehicle", "Repair", 0);
	totalPercent += this->m_percent->vSpy = this->Load_Int("Percentage_Vehicle", "Spy", 0);
	totalPercent += this->m_percent->vDemoTruck = this->Load_Int("Percentage_Vehicle", "DemoTruck", 0);
	totalPercent += this->m_percent->vSteal = this->Load_Int("Percentage_Vehicle", "Steal", 0);
	if (totalPercent != 1000)
	{
		this->m_percent->Reset(CRATE_VEHICLE);
		stConsole::Out("Total percentage for vehicle crates is %d instead of 1000 and values have been reset to default.\n",totalPercent);
	}

	// Vehicle crate drop data
	this->m_vehFacing[1] = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVC_Face", -181.0f);
	this->m_vehSpawn[1].X = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVC_X", 0.0f);
	this->m_vehSpawn[1].Y = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVC_Y", 0.0f);
	this->m_vehSpawn[1].Z = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVC_Z", -999.0f);
	this->m_vehPFacing[1] = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVCP_Face", -181.0f);
	this->m_vehPSpawn[1].X = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVCP_X", 0.0f);
	this->m_vehPSpawn[1].Y = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVCP_Y", 0.0f);
	this->m_vehPSpawn[1].Z = this->Load_Float(cGame->MapName.m_Buffer, "GDIRVCP_Z", -999.0f);

	this->m_vehFacing[0] = this->Load_Float(cGame->MapName.m_Buffer, "NodRVC_Face", -181.0f);
	this->m_vehSpawn[0].X = this->Load_Float(cGame->MapName.m_Buffer, "NodRVC_X", 0.0f);
	this->m_vehSpawn[0].Y = this->Load_Float(cGame->MapName.m_Buffer, "NodRVC_Y", 0.0f);
	this->m_vehSpawn[0].Z = this->Load_Float(cGame->MapName.m_Buffer, "NodRVC_Z", -999.0f);
	this->m_vehPFacing[0] = this->Load_Float(cGame->MapName.m_Buffer, "NodRVCP_Face", -181.0f);
	this->m_vehPSpawn[0].X = this->Load_Float(cGame->MapName.m_Buffer, "NodRVCP_X", 0.0f);
	this->m_vehPSpawn[0].Y = this->Load_Float(cGame->MapName.m_Buffer, "NodRVCP_Y", 0.0f);
	this->m_vehPSpawn[0].Z = this->Load_Float(cGame->MapName.m_Buffer, "NodRVCP_Z", -999.0f);

	// Spawn positions
	this->m_posList.Clear();
	for (int i = 1; i; i++)
	{
		char CX[12], CY[12], CZ[12];
		sprintf(CX, "Crate%d_X", i);
		sprintf(CY, "Crate%d_Y", i);
		sprintf(CZ, "Crate%d_Z", i);
		nc_Vector3 CratePos;
		CratePos.X = this->Load_Float(cGame->MapName.m_Buffer, CX, 0.0f);
		CratePos.Y = this->Load_Float(cGame->MapName.m_Buffer, CY, 0.0f);
		CratePos.Z = this->Load_Float(cGame->MapName.m_Buffer, CZ, -999.0f);
		if (CratePos.X != 0.0f && CratePos.Y != 0.0f &&	CratePos.Z != -999.0f)
			this->m_posList.Add(CratePos);
		else
			break;
	}
	if (this->m_posList.Count() == 0)
		this->m_enable = false;

	delete this->m_cfg;
	this->m_cfg = NULL;

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


/*****************/
/* Crate manager */
/*****************/
gzManagerCrateClass *gzCrateMgr = NULL;
//aVector<gzCrateBase *> gzManagerCrateClass::m_list;
aList<gzCrateBase> gzManagerCrateClass::m_list;
gzManagerCrateClass::gzManagerCrateClass()
{
	this->RegisterEvent(EVT_GAME_LEVEL_LOADED);
	this->RegisterEvent(EVT_GAME_THINK);
	this->RegisterEvent(EVT_PLAYER_JOIN);
	this->RegisterEvent(EVT_PLAYER_CHAT);
	this->RegisterEvent(EVT_PLAYER_PURCHASE);
	this->RegisterEvent(EVT_BHS_POKE);
	this->RegisterEvent(EVT_OBJECT_DAMAGE);
	this->RegisterEvent(EVT_OBJECT_KILL);
	this->RegisterEvent(EVT_OBJECT_SQUISH);
	this->RegisterEvent(EVT_OBJECT_TRANSITION);
	this->RegisterEvent(EVT_OBJECT_C4_CREATE);
	this->RegisterEvent(EVT_OBJECT_C4_DETONATE);
	this->RegisterEvent(EVT_OBJECT_POWERUP_GRANT);

	this->m_settings = new gzSettingsCrateClass;
	this->m_settings->SetOwner(this);
	this->m_data = NULL;
	gzCrateBase::m_percent = this->m_settings->m_percent;
	
}
void gzManagerCrateClass::Delete()
{
	//this->m_list.Clear();
	this->m_list.RemoveAll();
	if (this->m_data)
		delete this->m_data;
}
void gzManagerCrateClass::Level_Loaded()
{
	if (this->m_data)
		delete this->m_data;
	this->m_data = new gzCrateDataStruct;

	this->m_crateId = 0;
}
void gzManagerCrateClass::Think()
{
	// Crate checks
	if (this->m_settings->m_enable && cGame->Is_Gameplay_Permitted() && this->m_settings->m_posList.Count() > 0 && this->m_list.Count() > 0)
	{
		SubFrameTime(this->m_nextCheckCounter, true);
		SubFrameTime(this->m_liveCounter, true);

		if (this->m_crateId == 0)
			this->m_liveCounter = 0.0f;

		if (this->m_settings->m_liveTime > -1.0f && this->m_liveCounter <= 0.0f)
		{
			if (nc_GameObjManager::Find_PhysicalGameObj(this->m_crateId))
			{
#ifdef CRATEMSG
				stConsole::Out("[Crate] Expired.\n");
#endif
				nc_GameObjManager::Find_PhysicalGameObj(this->m_crateId)->Set_Delete_Pending();
				this->m_crateId = 0;
			}
		}

		if (this->m_nextCheckCounter < 0.0f)
		{
			this->m_nextCheckCounter = this->m_settings->m_existCheckTime;
			if (this->m_settings->m_existCheckVariationMin >= 2.0f)
				this->m_nextCheckCounter += (float)stRandom.GetInt(this->m_settings->m_existCheckVariationMin, this->m_settings->m_existCheckVariationMax);

#ifdef CRATEMSG
			stConsole::Out("[Crate] Next spawn check will be in %.0f seconds.\n", this->m_nextCheckCounter);
#endif

			if (this->m_crateId == 0)
			{
				nc_PhysicalGameObj *CrateObj = nc_ObjectLibraryManager::Create_Object("POW_Neuro_Link")->As_PhysicalGameObj();
				if (CrateObj)
				{
					this->m_crateId = CrateObj->NetworkID;
					int spawnIndex = 0;
					if (this->m_settings->m_posList.Count() > 1)
					{
						spawnIndex = stRandom.Get_Int(0, this->m_settings->m_posList.Count() -1);
						while (spawnIndex == this->m_lastSpawnIndex)
							spawnIndex = stRandom.Get_Int(0, this->m_settings->m_posList.Count() -1);
					}
					this->m_lastSpawnIndex = spawnIndex;
					CrateObj->Set_Position(this->m_settings->m_posList[spawnIndex]);
					CrateObj->Physics->Set_Model_By_Name("vehcol2m");
#ifdef CRATEMSG
					stConsole::Out("[Crate] Spawn at position #%d: %f %f %f\n",
						spawnIndex + 1,
						this->m_settings->m_posList[spawnIndex].X,
						this->m_settings->m_posList[spawnIndex].Y,
						this->m_settings->m_posList[spawnIndex].Z);
#endif
				}

				// Crate live counter
				this->m_liveCounter = this->m_settings->m_liveTime;
#ifdef CRATEMSG
				stConsole::Out("[Crate] Crate live time is %f seconds.\n", this->m_settings->m_liveTime);
#endif
			}
		}
	}

	// Base Defense Power crate
	if (this->m_data->BasePower.enable)
	{
		SubFrameTime(this->m_data->BasePower.counter, (this->m_data->BasePower.counter >= 0.0f));
		if (this->m_data->BasePower.counter < 0.0f)
		{
			if (!this->m_data->BasePower.building->Destroyed && this->m_data->BasePower.building->Base->IsPowered != this->m_data->BasePower.building->IsPowered)
			{
				this->m_data->BasePower.building->IsPowered ^= 1;
				this->m_data->BasePower.building->Set_Object_Dirty_Bit(nc_DB_RARE, true);
				if (this->m_data->BasePower.powered == 0)
					stConsole::In("msg [Crate]: %s's extra power cell has run out. Their main base defense is now back offline.", (this->m_data->BasePower.building->PlayerType == 1 ? "GDI" : "Nod"));
				else
					stConsole::In("msg [Crate]: %s fixed their power problems. The power will now continue to flow to the main base defense.", (this->m_data->BasePower.building->PlayerType == 1 ? "GDI" : "Nod"));
			}
			this->m_data->BasePower.enable   = false;
			this->m_data->BasePower.building = NULL;
			this->m_data->BasePower.counter  = 0.0f;
			this->m_data->BasePower.powered  = true;
			this->m_data->BasePower.team     = -1;
		}
	}

	// Vehicle Auto Repair Crate
	for (unsigned int i = 0; i < this->m_data->VehicleAutoRepair.Count(); i++)
	{
		SubFrameTime(this->m_data->VehicleAutoRepair[i].counter, true);
		if (this->m_data->VehicleAutoRepair[i].counter < 0.0f)
		{
			nc_SmartGameObj *veh = nc_GameObjManager::Find_SmartGameObj(this->m_data->VehicleAutoRepair[i].vehId);
			if (veh)
			{
				float healAmount = 2.0f;

				// Only heal if player is in driver seat
				if (veh->As_VehicleGameObj()->SeatsList[0])
					gzCommands->Apply_Damage(veh, healAmount * -1.0f, "Repair", NULL);

				// Reset counter
				this->m_data->VehicleAutoRepair[i].counter = 1.0f;
			}
			else
			{
				this->m_data->VehicleAutoRepair.Delete(i);
				i--;
			}
		}
	}
}
void gzManagerCrateClass::Player_Joined(gzEventPlayerBase &evt)
{
	for (nc_GenericSLNode<nc_BaseGameObj> *c4List = nc_GameObjManager::GameObjList->HeadNode; c4List != NULL; c4List = c4List->NodeNext)
	{
		// C4
		if (c4List->NodeData->definition->Get_Class_ID() == 0x3006)
		{
			nc_C4GameObj *c4 = gzStatic_Cast(nc_C4GameObj, c4List->NodeData);

			// Proximity
			if (c4->AmmoDef->AmmoType.Get() == 3)
			{
				bool found = false;
				for (unsigned int i = 0; i < this->m_data->StealthMine.Count(); i++)
				{
					for (unsigned int j = 0; j < this->m_data->StealthMine[i]->activeList.Count(); j++)
					{
						if (c4->NetworkID == this->m_data->StealthMine[i]->activeList[j])
						{
							found = true;
							goto c4Proc;
						}
					}
				}
c4Proc:
				if (found && c4->PlayerType != evt.GetPlayer()->PlayerType.Get())
					c4->Set_Object_Dirty_Bit(evt.GetPlayer()->PlayerId, nc_DB_CREATION, false);
				else
					c4->Set_Object_Dirty_Bit(evt.GetPlayer()->PlayerId, nc_DB_CREATION, true);
			}
		}
	}
}
void gzManagerCrateClass::Player_Chat(gzEventPlayerChat &evt)
{
	if (evt.m_type == 1)
	{
		gzPlayer *gzData = gzPlayerManager::Find(evt.m_sender);
		if (gzData)
		{
			nc_cScTextObj *TextObj = nc_cScTextObj::Create();
			TextObj->Type = evt.m_type;
			TextObj->IsPopup = false;
			TextObj->SenderId = evt.m_sender;
			TextObj->ReceiverId = evt.m_receiver;
			TextObj->Message.Format(L"%s", evt.m_message.GetString());
			for (unsigned int i = 0; i < this->m_data->Spy.Count(); i++)
			{
				nc_SmartGameObj *soldier = nc_GameObjManager::Find_SmartGameObj(this->m_data->Spy[i]);
				if (soldier && soldier->Player)
				{
					if (soldier->Player->PlayerType.Get() != gzData->GetPlayerData()->PlayerType.Get())
					{
						TextObj->Set_Object_Dirty_Bit(soldier->Player->PlayerId, nc_DB_CREATION, true);
						nc_cNetwork::Send_Object_Update(TextObj, soldier->Player->PlayerId);
					}
				}
				else
				{
					this->m_data->Spy.Delete(i);
					i--;
				}
			}
			TextObj->Set_Delete_Pending();
		}
	}
}
void gzManagerCrateClass::Player_Purchase(gzEventPlayerPurchase &evt)
{
	if (evt.GetPlayer()->Owner.Reference)
	{
		if (strstr(evt.GetPlayer()->Owner.Reference->obj->definition->Get_Name(), "CnC_Visceroid"))
		{
			switch (evt.GetInt())
			{
				case PURCHASE_INFANTRY:
				case PURCHASE_FREE_INFANTRY:
				case PURCHASE_SECRET_INFANTRY:
					evt.m_retCode = 1;
					evt.Skip();
					return;
			}
		}
	}
}
void gzManagerCrateClass::Object_Damaged(gzEventObjectDamage &evt)
{
	if (evt.m_damage > 0.0f)
	{
		// ChemSuit crate
		if (!strstr(evt.m_defender->definition->Get_Name(), "CnC_Visceroid"))
		{
			switch (evt.m_warhead)
			{
				case 9:
				case 10:
				case 11:
					for (unsigned int i = 0; i < this->m_data->ChemSuit.Count(); i++)
					{
						if (this->m_data->ChemSuit[i] == evt.m_defender->NetworkID)
						{
							evt.Skip();
							return;
						}
					}
			}
		}
	}
}
void gzManagerCrateClass::Object_Killed(gzEventObjectKill &evt)
{
	// Explosive Suit Crate
	for (unsigned int i = 0; i < this->m_data->ExplosiveSuit.Count(); i++)
	{
		if (this->m_data->ExplosiveSuit[i] == evt.m_defender->NetworkID)
		{
			nc_Vector3 explodePos;
			evt.m_defender->Get_Position(&explodePos);
			int damage = stRandom.Get_Int(250, 1000);

			// Explosion effects
			const char *explodeAnim = NULL, *animPreset = NULL;
			if (stRandom.Get_Int(1, 2) == 1)
			{
				animPreset	= "Beacon_Ion_Cannon_Anim_Post";
				explodeAnim	= "Explosion_IonCannonBeacon";
			}
			else
			{
				animPreset	= "Beacon_Nuke_Strike_Anim_Post";
				explodeAnim	= "Explosion_NukeBeacon";
			}
			if (explodeAnim && animPreset)
			{
				nc_PhysicalGameObj *anim = nc_ObjectLibraryManager::Create_Object(animPreset)->As_PhysicalGameObj();
				anim->Set_Position(explodePos);
				nc_ExplosionManager::Server_Explode(nc_DefinitionMgrClass::Find_Named_Definition(explodeAnim, true)->Get_ID(), explodePos, 0, NULL);
			}

			const float affectDistance = 25.0f;
			for (nc_GenericSLNode<nc_SmartGameObj> *objList = nc_GameObjManager::SmartGameObjList->HeadNode; objList != NULL; objList = objList->NodeNext)
			{
				nc_Vector3 objPos;
				objList->NodeData->As_PhysicalGameObj()->Get_Position(&objPos);
				float Distance = abs(objPos.X - explodePos.X) + abs(objPos.Y - explodePos.Y);
				if (Distance <= affectDistance)
					gzCommands->Apply_Damage(objList->NodeData, damage * ((affectDistance - Distance) / affectDistance), "Laser_NoBuilding", NULL);
			}
			stConsole::In("msg BOOM!");
			this->m_data->ExplosiveSuit.Delete(i);
			break;
		}
	}

	// Demo Truck Crate
	for (unsigned int i = 0; i < this->m_data->DemoTruck.Count(); i++)
	{
		if (this->m_data->DemoTruck[i] == evt.m_defender->NetworkID)
		{
			nc_Vector3 explodePos;
			evt.m_defender->Get_Position(&explodePos);
			int damage = stRandom.Get_Int(500, 2000);

			// Explosion effects
			const char *explodeAnim = NULL, *animPreset = NULL;
			if (stRandom.Get_Int(1, 2) == 1)
			{
				animPreset	= "Beacon_Ion_Cannon_Anim_Post";
				explodeAnim	= "Explosion_IonCannonBeacon";
			}
			else
			{
				animPreset	= "Beacon_Nuke_Strike_Anim_Post";
				explodeAnim	= "Explosion_NukeBeacon";
			}
			if (explodeAnim && animPreset)
			{
				nc_PhysicalGameObj *anim = nc_ObjectLibraryManager::Create_Object(animPreset)->As_PhysicalGameObj();
				anim->Set_Position(explodePos);
				nc_ExplosionManager::Server_Explode(nc_DefinitionMgrClass::Find_Named_Definition(explodeAnim, true)->Get_ID(), explodePos, 0, NULL);
			}

			const float affectDistance = 50.0f;
			for (nc_GenericSLNode<nc_SmartGameObj> *objList = nc_GameObjManager::SmartGameObjList->HeadNode; objList != NULL; objList = objList->NodeNext)
			{
				nc_Vector3 objPos;
				objList->NodeData->As_PhysicalGameObj()->Get_Position(&objPos);
				float Distance = abs(objPos.X - explodePos.X) + abs(objPos.Y - explodePos.Y);
				if (Distance <= affectDistance)
					gzCommands->Apply_Damage(objList->NodeData, damage * ((affectDistance - Distance) / affectDistance), "Laser_NoBuilding", NULL);
			}
			stConsole::In("msg BOOM!");
			this->m_data->DemoTruck.Delete(i);
			break;
		}
	}
}
void gzManagerCrateClass::Object_Transition(gzEventObjectTransition &evt)
{
	if (evt.m_instance->Target.Reference && evt.m_soldier->Player)
	{
		// Visceroid crate
		if (strstr(evt.m_soldier->definition->Get_Name(), "CnC_Visceroid"))
		{
			evt.Skip();
			return;
		}

		// Vehicle Anti-Enemy crate
		for (unsigned int i = 0; i < this->m_data->VehicleAntiEnemy.Count(); i++)
		{
			if (this->m_data->VehicleAntiEnemy[i] == evt.m_instance->Target.Reference->obj->NetworkID)
			{
				nc_VehicleGameObjDef *vehDef = gzStatic_Cast(nc_VehicleGameObjDef, evt.m_instance->Target.Reference->obj->definition);
				if (evt.m_soldier->PlayerType != vehDef->PlayerType)
				{
					gzCommands->Apply_Damage(evt.m_soldier, 99999.0f, "Death", NULL);
					PagePlayer(evt.m_soldier->Player->PlayerId, "[Crate]: You were discovered by Anti-Enemy system installed on the vehicle you tried to enter.");
					evt.Skip();
					return;
				}
			}
		}
	}
}
void gzManagerCrateClass::Object_SoldierSquished(gzEventObjectKill &evt)
{
	for (unsigned int i = 0; i < this->m_data->MetalGear.Count(); i++)
	{
		nc_PhysicalGameObj *soldier = nc_GameObjManager::Find_PhysicalGameObj(this->m_data->MetalGear[i]);
		if (!soldier)
		{
			this->m_data->MetalGear.Delete(i);
			i--;
			continue;
		}
		if (evt.m_defender->NetworkID == this->m_data->MetalGear[i])
		{
			for (int j = 0; j < evt.m_defender->Observers.Count(); j++)
			{
				if (evt.m_defender->Observers[j])
					evt.m_defender->Observers[j]->Custom(evt.m_defender, 306492, 0, NULL);
			}
			evt.Skip();
		}
	}
}
void gzManagerCrateClass::Object_C4Creation(gzEventObjectC4Creation &evt)
{
	if (evt.m_C4->Owner.Reference && evt.m_C4->Owner.Reference->obj->As_SmartGameObj()->Player)
	{
		nc_cPlayer *pData = evt.m_C4->Owner.Reference->obj->As_SmartGameObj()->Player;
		if (evt.m_C4->AmmoDef->AmmoType.Get() == 3)
		{
			for (unsigned int i = 0; i < this->m_data->StealthMine.Count(); i++)
			{
				if (this->m_data->StealthMine[i]->owner == pData)
				{
					if (this->m_data->StealthMine[i]->count > 0)
					{
						evt.m_C4->Set_Object_Dirty_Bit(nc_DB_CREATION, false);
						for (nc_GenericSLNode<nc_cPlayer> *pList = nc_cPlayerManager::PlayerList->HeadNode; pList != NULL; pList = pList->NodeNext)
						{
							if (pData->PlayerType.Get() == pList->NodeData->PlayerType.Get())
								evt.m_C4->Set_Object_Dirty_Bit(pList->NodeData->PlayerId, nc_DB_CREATION, true);
						}
						this->m_data->StealthMine[i]->activeList.Add(evt.m_C4->NetworkID);
						this->m_data->StealthMine[i]->count--;
						if (this->m_data->StealthMine[i]->count > 0)
							PagePlayer(evt.m_C4->Player->PlayerId, "You have %d Stealth Proximity Mine(s) left.", this->m_data->StealthMine[i]->count);
						else
							PagePlayer(evt.m_C4->Player->PlayerId, "All of your Stealth Proximity Mine has been used.");
						break;
					}
				}
			}
		}
	}
}
void gzManagerCrateClass::Object_C4Detonate(gzEventObjectC4Detonation &evt)
{
	if (evt.m_explodeBy)
	{
		for (unsigned int i = 0; i < this->m_data->Hover.Count(); i++)
		{
			if (this->m_data->Hover[i] == evt.m_explodeBy->NetworkID)
			{
				evt.Skip();
				return;
			}
		}

		// Stealth mine existence check
		for (unsigned int i = 0; i < this->m_data->StealthMine.Count(); i++)
		{
			for (unsigned int j = 0; j < this->m_data->StealthMine[i]->activeList.Count(); j++)
			{
				nc_PhysicalGameObj *c4 = nc_GameObjManager::Find_PhysicalGameObj(this->m_data->StealthMine[i]->activeList[j]);
				if (!c4 || this->m_data->StealthMine[i]->activeList[j] == evt.m_C4->NetworkID)
				{
					this->m_data->StealthMine[i]->activeList.Delete(j);
					j--;
				}
			}
		}
	}
}
void gzManagerCrateClass::Object_PowerupGrant(gzEventObjectPowerupGrant &evt)
{
	if (evt.m_powerup->NetworkID == this->m_crateId)
	{
		if (evt.m_soldier->Player == NULL)
		{
			evt.Skip();
			return;
		}

		if (!stricmp(evt.m_soldier->Physics->PhysRender->Get_Name(), "c_visceroid"))
		{
			evt.Skip();
			return;
		}

#if VERC(1, 0, 1)
		gzPlayer* gzData = gzPlayerManager::Find(evt.m_soldier->Player);
		if (gzData != NULL)
		{
			if (gzData->GetTibCrystal() != NULL)
			{
				int pId = gzData->GetPlayerData()->PlayerId;
				gzData->GetTibCrystal()->SetPickupBlock(pId, 5.0f);
				nc_GameObjManager::Find_PhysicalGameObj(gzData->GetTibCrystal()->GetCrystalId())->Attach_To_Object_Bone(NULL, "neck");
				gzData->GetTibCrystal()->RestoreWeapons();
				gzData->GetTibCrystal()->Drop();
				PagePlayer(pId, "Oh shit!! The Tiberium Crystal is too heavy and I had to drop it for the big 1337 crate...");
			}
		}
#endif

		aVector<gzCrateBase *> CrateList;
		for (aListNode<gzCrateBase>* list = this->m_list.GetHead(); list != NULL; list = list->GetNext())
		{
			if ((evt.m_soldier->Vehicle && (list->GetData()->GetType() & CRATE_VEHICLE) == CRATE_VEHICLE) || // Vehicle crate
				(!evt.m_soldier->Vehicle && (list->GetData()->GetType() & CRATE_SOLDIER) == CRATE_SOLDIER)) // Soldier crate
			{
				CrateList.Add(list->GetData());
			}
		}
		//for (unsigned int i = 0; i < this->m_list.Count(); i++)
		//{
		//	if ((evt.m_soldier->Vehicle && (this->m_list[i]->GetType() & CRATE_VEHICLE) == CRATE_VEHICLE) // Vehicle crate
		//		|| (!evt.m_soldier->Vehicle && (this->m_list[i]->GetType() & CRATE_SOLDIER) == CRATE_SOLDIER)) // Soldier crate
		//	{
		//		CrateList.Add(this->m_list[i]);
		//	}
		//}
		if (CrateList.Count() == 0)
		{
			evt.Skip();
			return;
		}

		int Percent = stRandom.Get_Int(0, 1000);
		int crateIndex = stRandom.Get_Int(0, CrateList.Count() -1);
		int pCount = 0, loopCount = 0;
#ifdef CRATEMSG
		stConsole::Out("[Crate] %s Crates: %d\n",
			evt.m_soldier->Vehicle ? "Vehicle" : "Soldier",
			CrateList.Count()
		);
		stConsole::Out("[Crate] Percent: %d\n", Percent);
		stConsole::Out("[Crate] Index: %d\n", crateIndex);
#endif
		for (unsigned int i = 0; i < CrateList.Count(); i++)
			CrateList[i]->m_selected = false;
		while (CrateList[crateIndex]->GetPercent() == 0)
			crateIndex = stRandom.Get_Int(0, CrateList.Count() -1);
		while (true)
		{
			unsigned int selectedCount = 0;
			for (unsigned int i = 0; i < CrateList.Count(); i++)
			{
				if (CrateList[i]->m_selected)
					selectedCount++;
			}
			if (selectedCount == CrateList.Count())
			{
				for (unsigned int i = 0; i < CrateList.Count(); i++)
					CrateList[i]->m_selected = false;
				pCount = 0;
				loopCount++;
			}
			while (CrateList[crateIndex]->m_selected)
				crateIndex = stRandom.Get_Int(0, CrateList.Count() -1);
			CrateList[crateIndex]->m_selected = true;
			if (CrateList[crateIndex]->GetPercent() > 0)
			{
				pCount += CrateList[crateIndex]->GetPercent();
				if (pCount >= Percent)
				{
					if (CrateList[crateIndex]->Pick(evt.m_soldier))
					{
						CrateList[crateIndex]->m_hitCount++;
						if (!CrateList[crateIndex]->SkipDefaultOutput())
						{
							gzLogger("_CRATE", "%ls picked up %s %s Crate.",
								evt.m_soldier->Player->PlayerName.m_Buffer,
								A_Or_An(CrateList[crateIndex]->GetName()),
								CrateList[crateIndex]->GetName()
							);
						}
						break;
					}
					else
					{
						for (unsigned int i = 0; i < CrateList.Count(); i++)
							CrateList[i]->m_selected = false;
						pCount = 0;
						loopCount++;
					}
				}
			}
			if (loopCount >= 5)
			{
#ifdef CRATEMSG
				stConsole::Out("[Crate] Failed to give crate to %ls after 5 tries.\n", evt.m_soldier->Player->PlayerName.m_Buffer);
#endif
				PagePlayer(evt.m_soldier->Player->PlayerId, "[Crate] BOO!!! You just wasted a crate, n00b.");
				break;
			}
		}
		this->m_crateId = 0;
		evt.m_powerup->Set_Delete_Pending();
	}
}
void gzManagerCrateClass::Settings_Loaded()
{
	if (!this->m_settings->m_enableYellowCrate)
	{
		for (int i = 0; i < nc_SpawnManager::SpawnerList.Count(); i++)
		{
			if (!stricmp(nc_SpawnManager::SpawnerList[i]->Definition->Get_Name(), "CnC_Spawner_Crate"))
				nc_SpawnManager::SpawnerList[i]->Enable = false;
		}
	}
}


/**************/
/* Crate base */
/**************/
const gzCratePercentStruct *gzCrateBase::m_percent = NULL;


/**********/
/* Crates */
/**********/
class Crate_RandomWeapon : public gzCrateBase {
	const char *GetName()
	{
		return "Random Weapon";
	}
	int GetType()
	{
		return CRATE_SOLDIER;
	}
	unsigned int GetPercent()
	{
		return this->m_percent->Weapon;
	}
	bool Pick(nc_SmartGameObj *Picker)
	{
		const char *weapons[][3] = {
			{ "GDI Auto Rifle", "Weapon_AutoRifle_Player", "m00pwar_aqob0004i1evag_snd.wav" },
			{ "Nod Auto Rifle", "Weapon_AutoRifle_Player_Nod", "m00pwar_aqob0004i1evag_snd.wav" },
			{ "Shotgun", "Weapon_Shotgun_Player", "m00pwps_aqob0004i1evag_snd.wav" },
			{ "Flamethrower", "CNC_Weapon_Flamethrower_Player", "m00pwft_aqob0001i1evag_snd.wav" },
			{ "Grenade Launcher", "Weapon_GrenadeLauncher_Player", "m00pwgl_aqob0004i1evag_snd.wav" },
			{ "Repair Gun(Weak)", "Weapon_RepairGun_Player", "m00pwrp_aqob0001i1evag_snd.wav" },
			{ "Repair Gun(Strong)", "CnC_Weapon_RepairGun_Player_Special", "m00pwrp_aqob0001i1evag_snd.wav" },
			{ "2 Remote C4", "CnC_Weapon_MineRemote_Player_2Max", "m00pac4_aqob0004i1evag_snd.wav" },
			{ "2 Timed C4", "CnC_Weapon_MineTimed_Player_2Max", "m00pact_aqob0004i1evag_snd.wav" },
			{ "Proximity C4", "Weapon_MineProximity_Player", "m00pacp_aqob0004i1evag_snd.wav" },
			{ "GDI Chain Gun", "Weapon_Chaingun_Player", "m00pwcg_aqob0004i1evag_snd.wav" },
			{ "Nod Chain Gun", "Weapon_Chaingun_Player_Nod", "m00pwcg_aqob0004i1evag_snd.wav" },
			{ "Rocket Launcher(Weak)", "Weapon_RocketLauncher_Player", "m00pwrl_aqob0004i1evag_snd.wav" },
			{ "Rocket Launcher(Strong)", "CnC_Weapon_RocketLauncher_Player", "m00pwrl_aqob0004i1evag_snd.wav" },
			{ "Chemical Sprayer", "CNC_Weapon_ChemSprayer_Player", "m00pwcs_aqob0004i1evag_snd.wav" },
			{ "Tiberium Auto Rifle", "Weapon_TiberiumAutoRifle_Player", "m00pwtr_aqob0004i1evag_snd.wav" },
			{ "GDI Sniper Rifle", "Weapon_SniperRifle_Player", "m00pwsr_aqob0004i1evag_snd.wav" },
			{ "Nod Sniper Rifle", "Weapon_SniperRifle_Player_Nod", "m00pwsr_aqob0004i1evag_snd.wav" },
			{ "Laser Chaingun", "Weapon_LaserChaingun_Player", "m00pwlc_aqob0004i1evag_snd.wav" },
			{ "Laser Rifle", "Weapon_LaserRifle_Player", "m00pwlr_aqob0004i1evag_snd.wav" },
			{ "Tiberium Flechette Gun", "Weapon_TiberiumFlechetteGun_Player", "m00pwtf_aqob0004i1evag_snd.wav" },
			{ "Personal Ion Cannon", "Weapon_PersonalIonCannon_Player", "m00pwpi_aqob0004i1evag_snd.wav" },
			{ "Railgun", "Weapon_Railgun_Player", "m00pwrg_aqob0004i1evag_snd.wav" },
			{ "Ramjet Rifle", "Weapon_RamjetRifle_Player", "m00pwrj_aqob0004i1evag_snd.wav" },
			{ "GDI Volt Auto Rifle", "Weapon_VoltAutoRifle_Player", "m00pwvr_aqob0004i1evag_snd.wav" },
			{ "Nod Volt Auto Rifle", "Weapon_VoltAutoRifle_Player_Nod", "m00pwvr_aqob0004i1evag_snd.wav" }
		};
		bool granted = false;
		int rand = -1;
		for (int count = 0; count < 10; count++)
		{
			rand = stRandom.Get_Int(0, (sizeof(weapons) / 12) - 1);
			nc_WeaponDefinitionClass *weaponDef = gzStatic_Cast(nc_WeaponDefinitionClass, nc_DefinitionMgrClass::Find_Named_Definition(weapons[rand][1], true));
			if (!weaponDef)
				continue;

			if (!gzGameMgr->GetGame()->IsPresetDisabled(weaponDef->Get_Name()))
			{
				// Weapon ban check
				bool isBanned = false;
				for (unsigned int i = 0; i < gzWeaponMgr->m_settings->Weapons.Ban.Count(); i++)
				{
					if (gzWeaponMgr->m_settings->Weapons.Ban[i]->Preset == Picker->definition->Get_Name())
					{
						for (unsigned int j = 0; j < gzWeaponMgr->m_settings->Weapons.Ban[i]->WeaponList.Count(); j++)
						{
							if (gzWeaponMgr->m_settings->Weapons.Ban[i]->WeaponList[j] == weaponDef->Get_Name())
							{
								isBanned = true;
								break;
							}
						}
						if (isBanned)
							break;
					}
				}
				if (isBanned)
					continue;

				Picker->WeaponBag->Add_Weapon(weaponDef, weaponDef->ClipSize.Get(), true);
				gzPlayer *gzData = gzPlayerManager::Find(Picker->Player);
				if (gzData)
					gzData->GetWeaponPack()->Add_Weapon(weaponDef);
				gzLogger("_CRATE", "%ls picked up a Random Weapon Crate and received %s.",
					Picker->Player->PlayerName.m_Buffer,
					weapons[rand][0]
				);
				PagePlayer(Picker->Player->PlayerId, "[Crate] You just got a %s from the Random Weapon Crate.", gzTranslator->Get(weaponDef->Get_Name()));
				gzCreate_2D_WAV_Sound_Player(Picker, weapons[rand][2]);
				granted = true;
				break;
			}
		}
		return granted;
	}
};
Crate_RandomWeapon CrateRandomWeapon;

class Crate_Money : public gzCrateBase {
	const char *GetName()
	{
		return "Money";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Money;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int	Amount = stRandom.Get_Int(1, 1000), RndGood = stRandom.Get_Int(1, 2);
		if (RndGood	== 1)
		{
			Picker->Player->Increment_Money((float)Amount);
			gzLogger("_CRATE", "%ls picked up a Money Crate.", Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Money Crate, you have gained %d credits.", Amount);
		}
		else
		{
			Picker->Player->Increment_Money((float)-Amount);
			gzLogger("_CRATE", "%ls picked up a Demoney Crate.", Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Demoney Crate, you have lost %d credits.", Amount);
		}
		return true;
	};
};
Crate_Money CrateMoney;

class Crate_Points : public gzCrateBase {
	const char *GetName()
	{
		return "Points";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Points;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int	Amount = stRandom.Get_Int(1, 500), RndGood = stRandom.Get_Int(1, 2);
		if (RndGood == 1)
		{
			Picker->Player->Increment_Score((float)Amount);
			gzLogger("_CRATE", "%ls picked up a Points Crate.", Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Points Crate, you have gained %d points.", Amount);
			stConsole::In("msg [Crate]: Enjoy those %d points from the crate god, %s.", Amount, (Picker->PlayerType == 0) ? "Nod" : "GDI");
		}
		else {
			Picker->Player->Increment_Score((float)-Amount);
			gzLogger("_CRATE", "%S picked up a Depoints Crate.",Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Depoints Crate, you lost %d points.", Amount);
			stConsole::In("msg [Crate]: Hope you won't miss those %d points, %s.", Amount, (Picker->PlayerType == 0) ? "Nod" : "GDI");
		}
		return true;
	};
};
Crate_Points CratePoints;

class Crate_RandomVehicle : public gzCrateBase {
	const char *GetName()
	{
		return "Random Vehicle";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Vehicle;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (gzGameMgr->m_settings->m_GameMode == 1)
		{
			nc_Vector3 thePos = { 0.0f, 0.0f, 0.0f };
			if (gzCrateMgr->m_settings->m_vehSpawn[1].X != 0 &&
				gzCrateMgr->m_settings->m_vehSpawn[0].X != 0 &&
				gzCrateMgr->m_settings->m_vehPSpawn[1].X != 0 &&
				gzCrateMgr->m_settings->m_vehPSpawn[0].X != 0)
			{
				Picker->Set_Position(gzCrateMgr->m_settings->m_vehPSpawn[Picker->PlayerType]);
				thePos = gzCrateMgr->m_settings->m_vehSpawn[Picker->PlayerType];
				for (nc_GenericSLNode<nc_BaseGameObj> *objList = nc_GameObjManager::GameObjList->HeadNode; objList != NULL; objList = objList->NodeNext)
				{
					if (objList->NodeData && objList->NodeData->As_PhysicalGameObj())
					{
						nc_Vector3 objPos;
						objList->NodeData->As_PhysicalGameObj()->Get_Position(&objPos);
						float Distance = (abs(objPos.X - thePos.X) + abs(objPos.Y + thePos.Y));
						if (Distance <= 2.0f && objList->NodeData->NetworkID != Picker->NetworkID)
							gzCommands->Apply_Damage(objList->NodeData->As_PhysicalGameObj(),9999.0f,"Laser_NoBuilding",NULL);
					}
				}
				int	Rnd	= stRandom.Get_Int(1, 200);
				int	Index = 0;
				if (Rnd	<= 25)
					Index = 0;
				else if	(Rnd <=	50)
					Index =	1;
				else if	(Rnd <=	70)
					Index =	2;
				else if	(Rnd <=	90)
					Index =	3;
				else if	(Rnd <=	110)
					Index = 4;
				else if	(Rnd <=	130)
					Index = 5;
				else if	(Rnd <=	145)
					Index = 6;
				else if	(Rnd <=	160)
					Index = 7;
				else if	(Rnd <=	170)
					Index = 8;
				else if	(Rnd <=	180)
					Index = 9;
				else if	(Rnd <=	190)
					Index = 10;
				else
					Index = 11;
				const char *RandomVehicle[][3] = {
					{ "GDI Humvee", "humvee", "CnC_GDI_Humm-vee" },
					{ "Nod Buggy", "buggy", "CnC_Nod_Buggy" },
					{ "GDI APC", "gdiapc", "CnC_GDI_APC" },
					{ "Nod APC", "nodapc", "CnC_Nod_APC" },
					{ "GDI MRLS", "mrls", "CnC_GDI_MRLS" },
					{ "Nod Mobile Artillery", "arty", "CnC_Nod_Mobile_Artillery"},
					{ "GDI Medium Tank", "med", "CnC_GDI_Medium_Tank" },
					{ "Nod Light Tank", "lighttank", "CnC_Nod_Light_Tank" },
					{ "Nod Flame Tank",	"flamer", "CnC_Nod_Flame_Tank" },
					{ "Nod Stealth Tank", "stank", "CnC_Nod_Stealth_Tank" },
					{ "GDI Mammoth Tank", "mammy", "CnC_GDI_Mammoth_Tank" },
					{ "Nod Recon Bike", "recon", "CnC_Nod_Recon_Bike"}
				};
				if (!gzGameMgr->GetGame()->IsPresetDisabled(RandomVehicle[Index][2])) {
					nc_PhysicalGameObj *Ghost = nc_ObjectLibraryManager::Create_Object("Invisible_Object")->As_PhysicalGameObj();
					Ghost->Set_Position(thePos);
					nc_ScriptImpClass *Observer = nc_ScriptManager::Create_Script("Test_Cinematic");
					Observer->Set_Parameters_String(aString::Format("%sRVC_%s.txt", (Picker->PlayerType == 0) ? "Nod" : "GDI", RandomVehicle[Index][1]).GetString());
					Ghost->Add_Observer(Observer);
					PagePlayer(Picker->Player->PlayerId, "[Crate] You got a %s from the Random Vehicle Crate.", RandomVehicle[Index][0]);
					stConsole::In("msg [Crate]: Looks like %s just got a random vehicle! Go them!", (Picker->PlayerType == 0) ? "Nod" : "GDI");
					return true;
				}
			}
		}
		return false;
	};
};
Crate_RandomVehicle CrateRandomVehicle;

class Crate_Death : public gzCrateBase {
	const char *GetName()
	{
		return "Death";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Death;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		gzCommands->Apply_Damage(Picker, 99999, "Death", false);
		nc_Vector3 Pos;
		Picker->Get_Position(&Pos);
		nc_ExplosionManager::Server_Explode(nc_DefinitionMgrClass::Find_Typed_Definition("Explosion_Mine_Proximity_01", 0xB003, true)->Get_ID(), Pos, 0, NULL);
		PagePlayer(Picker->Player->PlayerId, "[Crate] You just got a Death Crate, you have been killed. Sorry :(");
		stConsole::In("msg [Crate]: Some poor %s guy got pwned by the fearsome death crate!!", (Picker->PlayerType == 0) ? "Nod" : "GDI");
		return true;
	};
};
Crate_Death CrateDeath;

class Crate_Ammo : public gzCrateBase {
	const char *GetName()
	{
		return "Ammo";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Ammo;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		nc_PowerUpGameObjDef *powerup = (nc_PowerUpGameObjDef *)nc_DefinitionMgrClass::Find_Typed_Definition("CnC_POW_Ammo_ClipMax", 0x3003, true);
		powerup->Grant(Picker, NULL, true);
		powerup->Grant(Picker, NULL, true);
		powerup->Grant(Picker, NULL ,true);
		powerup->Grant(Picker, NULL, true);
		PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Full Ammo Crate, your ammo has been refilled.");
		return true;
	};
};
Crate_Ammo CrateAmmo;

class Crate_Armor : public gzCrateBase {
	const char *GetName()
	{
		return "Armor";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Armor;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int RndGood = stRandom.Get_Int(1, 2);
		if (Picker->Defense.ShieldStrength.Get() < 2.0f)
			RndGood = 1;
		if (RndGood	== 1)
		{
			float RndShield = (float)stRandom.Get_Int(5, 50);
			Picker->Defense.ShieldStrengthMax += RndShield;
			gzLogger("_CRATE", "%ls picked up an Armor Crate.", Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Armor Crate, your max armor has been increased! [+%.0f]", RndShield);
		}
		else
		{
			Picker->Defense.ShieldStrength = 1.0f;
			gzLogger("_CRATE", "%ls picked up a De-armor Crate.", Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Dearmor Crate, your armor has been set to 1.");
		}
		Picker->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL,true);
		return true;
	};
};
Crate_Armor CrateArmor;

class Crate_Health : public gzCrateBase {
	const char *GetName()
	{
		return "Health";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Health;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int RndGood = stRandom.Get_Int(1, 2);
		if (Picker->Defense.Health.Get() < 2.0f)
			RndGood = 1;
		if (RndGood	== 1)
		{
			float RndHealth = (float)stRandom.Get_Int(5, 50);
			Picker->Defense.HealthMax += RndHealth;
			gzLogger("_CRATE", "%ls picked up an Health Crate.", Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Health Crate, your max health has been increased! [+%.0f]", RndHealth);
		}
		else {
			Picker->Defense.Health = 1.0f;
			gzLogger("_CRATE", "%ls picked up a De-health Crate.", Picker->Player->PlayerName.m_Buffer);
			PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Dehealth Crate, your health has been set to 1.");
		}
		Picker->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL,true);
		return true;
	};
};
Crate_Health CrateHealth;

class Crate_RandomCharacter: public gzCrateBase {
	const char *GetName()
	{
		return "Random Character";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Character;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (gzGameMgr->m_settings->m_GameMode != GAMEMODE_SNIPER || gzGameMgr->m_settings->m_GameMode != GAMEMODE_500SNIP)
		{
			const char *chars[] = {
				"CnC_GDI_Minigunner_0",
				"CnC_GDI_RocketSoldier_0",
				"CnC_GDI_Grenadier_0",
				"CnC_GDI_Engineer_0",
				"CnC_GDI_MiniGunner_1Off",
				"CnC_GDI_RocketSoldier_1Off",
				"CnC_Sydney",
				"CnC_GDI_MiniGunner_2SF",
				"CnC_GDI_RocketSoldier_2SF",
				"CnC_GDI_Grenadier_2SF",
				"CnC_GDI_MiniGunner_3Boss",
				"CnC_Sydney_PowerSuit",
				"CnC_Ignatio_Mobius",
				"CnC_GDI_Engineer_2SF",
				"CnC_Nod_Minigunner_0",
				"CnC_Nod_RocketSoldier_0",
				"CnC_Nod_FlameThrower_0",
				"CnC_Nod_Engineer_0",
				"CnC_Nod_Minigunner_1Off",
				"CnC_Nod_RocketSoldier_1Off",
				"CnC_Nod_FlameThrower_1Off",
				"CnC_Nod_Minigunner_2SF",
				"CnC_Nod_RocketSoldier_2SF",
				"CnC_Nod_FlameThrower_2SF",
				"CnC_Nod_Minigunner_3Boss",
				"CnC_Nod_RocketSoldier_3Boss",
				"CnC_Nod_FlameThrower_3Boss",
				"CnC_Nod_Technician_0"
			};
			int Random = stRandom.Get_Int(0, (sizeof(chars) / 4) - 1);
			Picker->As_SoldierGameObj()->Re_Init(*(nc_SoldierGameObjDef *)nc_DefinitionMgrClass::Find_Named_Definition(chars[Random], true));
			Picker->As_SoldierGameObj()->Post_Re_Init();
			PagePlayer(Picker->Player->PlayerId, "[Crate] You have been transformed into a %s by the Random Character Crate.", gzTranslator->Get(chars[Random]));
			return true;
		}
		return false;
	};
};
Crate_RandomCharacter CrateRandomCharacter;

class Crate_ButterFingers : public gzCrateBase {
	const char *GetName()
	{
		return "Butter Fingers";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->ButterFingers;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		Picker->WeaponBag->Clear_Weapons();
		int	Rnd	= stRandom.Get_Int(1, 2);
		if (Rnd	== 1)
		{
			Picker->WeaponBag->Add_Weapon("Weapon_Pistol_Player", -1, true);
			Picker->WeaponBag->Select_Weapon_Name("Weapon_Pistol_Player");
		}
		else
		{
			Picker->WeaponBag->Add_Weapon("CnC_Weapon_MineTimed_Player", 1, true);
			Picker->WeaponBag->Select_Weapon_Name("CnC_Weapon_MineTimed_Player");
		}
		PagePlayer(Picker->Player->PlayerId, "[Crate] You picked up the Butter Fingers Crate, you have dropped most of your weapons.");
		gzPlayer *gzData = gzPlayerManager::Find(Picker->Player);
		if (gzData)
			gzData->GetWeaponPack()->m_list.Clear();
		return true;
	};
};
Crate_ButterFingers CrateButterFinger;

class Crate_Refill : public gzCrateBase {
	const char *GetName()
	{
		return "Refill";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Refill;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		nc_VendorClass::Grant_Supplies(Picker->As_SoldierGameObj());
		PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Refill Crate, your health, armor, and ammo have all been refilled.");
		return true;
	};
};
Crate_Refill CrateRefill;

class Crate_Beacon : public gzCrateBase {
	const char *GetName()
	{
		return "Beacon";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Beacon;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (gzWeaponMgr->m_settings->m_BeaconLimit > 0 && gzGameMgr->m_settings->m_GameMode == GAMEMODE_AOW)
		{
			int	Random = stRandom.Get_Int(1, 3);
			if (Random == 1)
			{
				Picker->WeaponBag->Add_Weapon("CnC_Weapon_IonCannonBeacon_Player", 1, true);
				gzLogger("_CRATE", "%ls picked up a Beacon Crate.", Picker->Player->PlayerName.m_Buffer);
				PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Beacon Crate, you have been given an Ion Cannon Beacon.");
			}
			else if (Random == 2)
			{
				Picker->WeaponBag->Add_Weapon("CnC_Weapon_NukeBeacon_Player", 1, true);
				gzLogger("_CRATE", "%ls picked up a Beacon Crate.", Picker->Player->PlayerName.m_Buffer);
				PagePlayer(Picker->Player->PlayerId, "[Crate] You just got the Beacon Crate, you have been given a Nuclear Strike Beacon.");
			}
			else
			{
				nc_Vector3 Pos;
				Picker->Get_Position(&Pos);
				nc_ExplosionManager::Server_Explode(nc_DefinitionMgrClass::Find_Typed_Definition("Explosion_NukeBeacon",0xB003,true)->Get_ID(),Pos,0,NULL);
				Pos.Z = 0.0f;
				for (nc_GenericSLNode<nc_BaseGameObj> *objList = nc_GameObjManager::GameObjList->HeadNode; objList != NULL; objList = objList->NodeNext)
				{
					if (objList->NodeData && objList->NodeData->As_PhysicalGameObj())
					{
						nc_Vector3 objPos;
						objList->NodeData->As_PhysicalGameObj()->Get_Position(&objPos);
						objPos.Z = 0.0f;
						float Distance = (abs(objPos.X - Pos.X) + abs(objPos.Y + Pos.Y));
						if (Distance <= 15.0f && objList->NodeData->NetworkID != gzCrateMgr->m_crateId)
							gzCommands->Apply_Damage(objList->NodeData->As_PhysicalGameObj(), 2500.0f, "None", NULL);
					}
				}
				gzLogger("_CRATE", "%ls picked up a Nuclear Bomb Crate.", Picker->Player->PlayerName.m_Buffer);
				PagePlayer(Picker->Player->PlayerId, "[Crate] You just got a Nuclear Bomb Crate, you have been killed. Sorry :(");
				stConsole::In("msg [Crate]: Looks like a %s player just got blown to bits by a Nuclear Bomb.", (Picker->PlayerType == 0) ? "Nod" : "GDI");
			}
		}
		return true;
	};
};
Crate_Beacon CrateBeacon;

class Crate_Spy : public gzCrateBase {
	const char *GetName()
	{
		return "Spy";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Spy;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (Picker->As_SoldierGameObj()->IsVisible)
		{
			for (unsigned int i = 0; i < gzCrateMgr->m_data->Spy.Count(); i++)
			{
				if (gzCrateMgr->m_data->Spy[i] == Picker->NetworkID)
					return false;
			}
		}
		Picker->As_SoldierGameObj()->Re_Init(*(nc_SoldierGameObjDef *)nc_DefinitionMgrClass::Find_Named_Definition("CnC_Nod_FlameThrower_2SF",true));
		Picker->As_SoldierGameObj()->Post_Re_Init();
		Picker->As_SoldierGameObj()->IsVisible = false;
		gzCrateMgr->m_data->Spy.Add(Picker->NetworkID);
		PagePlayer(Picker->Player->PlayerId, "[Crate] You got the Spy Crate, base defenses will ignore you until you die, destroy a building. You can also see enemy team chat!");
		stConsole::In("msg [Crate]: Oh no! A %s player just got a spy crate, better watch your base %s!", (Picker->PlayerType == 0) ? "Nod" : "GDI", (Picker->PlayerType == 0) ? "GDI" : "Nod");
		return true;
	};
};
Crate_Spy CrateSpy;

class Crate_Thief : public gzCrateBase {
	const char *GetName()
	{
		return "Thief";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Thief;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (Picker->Player->Money.Get() > 0.0f)
		{
			Picker->Player->Increment_Money(-Picker->Player->Money.Get());
			PagePlayer(Picker->Player->PlayerId, "[Crate] You got the Thief Crate, you have lost all of your credits.");
			return true;
		}
		return false;
	};
};
Crate_Thief CrateThief;

class Crate_WeaponPack : public gzCrateBase {
	const char *GetName()
	{
		return "Weapon Pack";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->WeaponPack;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		gzPlayer *gzData = gzPlayerManager::Find(Picker->Player);
		if (!gzData)
			return false;

		aStringArray weapons, name;

		// Light
		weapons.Add("Weapon_AutoRifle_Player;Weapon_AutoRifle_Player_Nod;Weapon_Chaingun_Player;" \
					"Weapon_Chaingun_Player_Nod;CNC_Weapon_ChemSprayer_Player;CNC_Weapon_Flamethrower_Player;" \
					"Weapon_GrenadeLauncher_Player;Weapon_Shotgun_Player;Weapon_TiberiumAutoRifle_Player");
		name.Add("Light");

		// Medium
		weapons.Add("Weapon_LaserChaingun_Player;Weapon_LaserRifle_Player;Weapon_RocketLauncher_Player;" \
					"CnC_Weapon_RocketLauncher_Player;Weapon_TiberiumFlechetteGun_Player");
		name.Add("Medium");

		// Heavy
		weapons.Add("Weapon_PersonalIonCannon_Player;Weapon_Railgun_Player;Weapon_VoltAutoRifle_Player;" \
					"Weapon_VoltAutoRifle_Player_Nod");
		name.Add("Heavy");

		// Sniper
		weapons.Add("Weapon_RamjetRifle_Player;Weapon_SniperRifle_Player;Weapon_SniperRifle_Player_Nod");
		name.Add("Sniper");

		// Engineer
		weapons.Add("Weapon_RepairGun_Player;CnC_Weapon_RepairGun_Player_Special;CnC_Weapon_MineRemote_Player;" \
				"CnC_Weapon_MineRemote_Player_2Max;CnC_Weapon_MineTimed_Player;CnC_Weapon_MineTimed_Player_2Max;" \
				"Weapon_MineProximity_Player");
		name.Add("Engineer");

		int packId = stRandom.Get_Int(0, weapons.Count() - 1);
		aToken list(weapons[packId].GetString());
		for (int i = 1; i <= list.numtok(';'); i++)
		{
			nc_WeaponDefinitionClass *weaponDef = gzStatic_Cast(nc_WeaponDefinitionClass, nc_DefinitionMgrClass::Find_Named_Definition(list.gettok(i, ';').GetData().GetString(), true));
			bool canGrant = true;
			for (unsigned int j = 0; j < gzWeaponMgr->m_settings->Weapons.Ban.Count(); j++)
			{
				if (gzWeaponMgr->m_settings->Weapons.Ban[j]->Preset == Picker->definition->Get_Name())
				{
					for (unsigned int k = 0; k < gzWeaponMgr->m_settings->Weapons.Ban[j]->WeaponList.Count(); k++)
					{
						if (gzWeaponMgr->m_settings->Weapons.Ban[j]->WeaponList[k] == weaponDef->Get_Name())
						{
							canGrant = false;
							break;
						}
					}
				}
			}
			if (canGrant)
				Picker->WeaponBag->Add_Weapon(weaponDef, weaponDef->ClipSize.Get() + weaponDef->MaxInventoryRounds.Get(), true);
			gzData->GetWeaponPack()->Add_Weapon(weaponDef);
		}
		PagePlayer(gzData->GetPlayerData()->PlayerId, "[Crate] You received %s %s weapon pack.", A_Or_An(name[packId].GetString()), name[packId].GetString());
		return true;
	};
};
Crate_WeaponPack CrateWeaponPack;

class Crate_Donation : public gzCrateBase {
	const char *GetName()
	{
		return "Donation";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Donation;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int amount = stRandom.Get_Int(1, 1001), team = stRandom.Get_Int(0, 1);
		if (nc_cPlayerManager::Tally_Team_Size(team) == 0)
			team = Picker->Player->PlayerType.Get();

		for (nc_GenericSLNode<nc_cPlayer> *pList = nc_cPlayerManager::PlayerList->HeadNode; pList != NULL; pList = pList->NodeNext)
		{
			if (pList->NodeData->IsActive)
			{
				if (pList->NodeData->PlayerType.Get() == team)
				{
					pList->NodeData->Increment_Money((float)amount);
					if (pList->NodeData != Picker->Player)
					{
						PagePlayer(pList->NodeData->PlayerId, "[Crate]: You've received %d credits from the Donation Crate! Thanks to %ls.",
							amount,
							Picker->Player->PlayerName.m_Buffer
						);
					}
				}
			}
		}
		if (Picker->Player->PlayerType.Get() == team)
			PagePlayer(Picker->Player->PlayerId, "[Crate]: You've picked up a Donation Crate. Your team have received %d credits.", amount);
		else
			PagePlayer(Picker->Player->PlayerId, "[Crate]: Enemies have received %d credits from your Donation Crate!", amount);
		return true;
	};
};
Crate_Donation CrateDonation;

class Crate_MetalGear : public gzCrateBase {
	const char *GetName()
	{
		return "Metal Gear";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->MetalGear;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		for (unsigned int i = 0; i < gzCrateMgr->m_data->MetalGear.Count(); i++)
		{
			if (gzCrateMgr->m_data->MetalGear[i] == Picker->NetworkID)
				return false;
		}

		gzCrateMgr->m_data->MetalGear.Add(Picker->NetworkID);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: You've received a Metal Gear from the Crate God. You can't be run over by vehicles until you die.");
		return true;
	};
};
Crate_MetalGear CrateMetalGear;

class Crate_EnemyThief : public gzCrateBase {
	const char *GetName()
	{
		return "Enemy Thief";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->EnemyThief;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int enemyTeamPlayer = nc_cPlayerManager::Tally_Team_Size(Picker->Player->PlayerType.Get() ^ 1);
		if (enemyTeamPlayer > 0 && Picker->Player->Money.Get() > (float)enemyTeamPlayer) {
			float Money = Picker->Player->Money.Get() / enemyTeamPlayer;
			for (nc_GenericSLNode<nc_cPlayer> *pList = nc_cPlayerManager::PlayerList->HeadNode; pList != NULL; pList = pList->NodeNext)
			{
				if (pList->NodeData && pList->NodeData->IsActive && pList->NodeData->PlayerType.Get() != Picker->Player->PlayerType.Get())
				{
					pList->NodeData->Increment_Money(Money);
					pList->NodeData->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL,true);
					PagePlayer(pList->NodeData->PlayerId, "[Crate]: You have received %.0f credit(s) from %ls.",
						Money,
						Picker->Player->PlayerName.m_Buffer
					);
				}
			}
			Picker->Player->Money = 0.0f;
			Picker->Player->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL,true);
			PagePlayer(Picker->Player->PlayerId, "[Crate]: Congratulations! The enemy has stolen all of your money and distributed it among themselves.");
			return true;
		}
		return false;
	};
};
Crate_EnemyThief CrateEnemyThief;

class Crate_Medic : public gzCrateBase {
	const char *GetName()
	{
		return "Medic";
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Medic;
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		nc_Vector3 SoldierPos;
		Picker->Get_Position(&SoldierPos);
		int Heal = -stRandom.Get_Int(10, 100);
		float maxDistance = 50.0f;
		for (nc_GenericSLNode<nc_SoldierGameObj> *objList = nc_GameObjManager::StarGameObjList->HeadNode; objList != NULL; objList = objList->NodeNext)
		{
			if (objList->NodeData)
			{
				nc_Vector3 objPos;
				objList->NodeData->As_PhysicalGameObj()->Get_Position(&objPos);
				if ((abs(objPos.X - SoldierPos.X) + abs(objPos.Y - SoldierPos.Y)) <= maxDistance)
					gzCommands->Apply_Damage(objList->NodeData, (float)Heal, "Repair", NULL);
			}
		}
		PagePlayer(Picker->Player->PlayerId, "[Crate]: You've received a Medic Crate. You and any soldiers within the distance of %.0f ren-feet from you will be given a health of %d.", maxDistance, Heal * -1);
		//gzLogger("_CRATE", "%ls picked up a Medic Crate.", Picker->Player->PlayerName.m_Buffer);
		return true;
	};
};
Crate_Medic CrateMedic;

class Crate_ExplosiveSuit : public gzCrateBase {
	const char *GetName()
	{
		return "Explosive Suit";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->ExplosiveSuit;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		for (unsigned int i = 0; i < gzCrateMgr->m_data->ExplosiveSuit.Count(); i++)
		{
			if (gzCrateMgr->m_data->ExplosiveSuit[i] == Picker->NetworkID)
				return false;
		}
		gzCrateMgr->m_data->ExplosiveSuit.Add(Picker->NetworkID);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: Explosives have been attached to your soldier. Once you die, they will explode and make a BIG BOOM with random damage!!!!! Good luck!");
		return true;
	};
};
Crate_ExplosiveSuit CrateExplosiveSuit;

class Crate_ChemSuit : public gzCrateBase {
	const char *GetName()
	{
		return "Chemical Suit";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->ChemSuit;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		for (unsigned int i = 0; i < gzCrateMgr->m_data->ChemSuit.Count(); i++)
		{
			if (gzCrateMgr->m_data->ChemSuit[i] == Picker->NetworkID)
				return false;
		}
		gzCrateMgr->m_data->ChemSuit.Add(Picker->NetworkID);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: You've received a Chemical Suit from the Crate and will be protected by it while walking on Tiberium, until you die. (You will be revealed with SBH while walking on Tiberium)");
		return true;
	};
};
Crate_ChemSuit CrateChemSuit;

class Crate_Hover : public gzCrateBase {
	const char *GetName()
	{
		return "Hover";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Hover;
	};
	bool SkipDefaultOutput()
	{
		return false;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		for (unsigned int i = 0; i < gzCrateMgr->m_data->Hover.Count(); i++)
		{
			if (gzCrateMgr->m_data->Hover[i] == Picker->NetworkID)
				return false;
		}
		gzCrateMgr->m_data->Hover.Add(Picker->NetworkID);
		PagePlayer(Picker->Player->PlayerId, "You've picked up a Hover Crate. You're now invisible to enemy proximity mines until you die.");
		return true;
	};
};
Crate_Hover CrateHover;

class Crate_StealthMine : public gzCrateBase {
	const char *GetName()
	{
		return "Stealth Mine";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->StealthMine;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int pIndex = -1;
		for (unsigned int i = 0; i < gzCrateMgr->m_data->StealthMine.Count(); i++)
		{
			if (gzCrateMgr->m_data->StealthMine[i]->owner == Picker->Player)
			{
				gzCrateMgr->m_data->StealthMine[i]->count += 6;
				pIndex = i;
				break;
			}
		}
		if (pIndex == -1)
		{
			pIndex = gzCrateMgr->m_data->StealthMine.Count();
			gzCrateStealthMineStruct *stealthMine = new gzCrateStealthMineStruct;
			stealthMine->count = 6;
			stealthMine->owner = Picker->Player;
			gzCrateMgr->m_data->StealthMine.Add(stealthMine);

			gzPlayer *gzData = gzPlayerManager::Find(Picker->Player);
			if (gzData)
				gzData->GetWeaponPack()->Add_Weapon("Weapon_MineProximity_Player");
		}
		Picker->WeaponBag->Add_Weapon("Weapon_MineProximity_Player", 6, true);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: You've picked up a Stealth Mine Crate. You can place %d proximity mine(s) which enemy can NOT see.", gzCrateMgr->m_data->StealthMine[pIndex]->count);
		return true;
	};
};
Crate_StealthMine CrateStealthMine;

class Crate_Veteran : public gzCrateBase {
	const char *GetName()
	{
		return "Veteran";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Veteran;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		gzPlayer *gzData = gzPlayerManager::Find(Picker->Player);
		if (gzData)
		{
			if (gzData->GetVeteran()->GetNextLevel())
			{
				if (!gzData->GetVeteran()->GetNextLevel()->CanPromoteByCrate)
					return false;
			}

			if (!gzData->GetVeteran()->Promote())
				return false;

			gzLogger("_CRATE", "%ls picked up a Veteran Crate.", Picker->Player->PlayerName.m_Buffer);
			return true;
		}
		return false;
	};
};
Crate_Veteran CrateVeteran;

class Crate_Visceroid : public gzCrateBase {
	const char *GetName()
	{
		return "Visceroid";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->Visceroid;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (!strstr(Picker->definition->Get_Name(), "CnC_Visceroid"))
		{
			// Add 0.1 to Z or player will stuck after character change
			nc_Vector3 pos;
			Picker->Get_Position(&pos);
			pos.Z += 0.1f;

			// Change character
			Picker->As_SoldierGameObj()->Re_Init(*(nc_SoldierGameObjDef *)nc_DefinitionMgrClass::Find_Named_Definition("CnC_Visceroid", true));
			Picker->As_SoldierGameObj()->Post_Re_Init();

			// Set adjusted position
			Picker->As_PhysicalGameObj()->Set_Position(pos);

			// Health stuff...
			Picker->As_DamageableGameObj()->Defense.HealthMax			= 500.0f;
			Picker->As_DamageableGameObj()->Defense.Health				= 500.0f;
			Picker->As_DamageableGameObj()->Defense.ShieldStrengthMax	= 0.0f;
			Picker->As_DamageableGameObj()->Defense.ShieldStrength		= 0.0f;

			// Tell clients
			Picker->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);

			PagePlayer(Picker->Player->PlayerId, "[Crate]: You just got a Visceroid crate and have been turned into a Visceroid!");
			return true;
		}
		return false;
	}
};
Crate_Visceroid CrateVisceroid;

class Crate_BasePower : public gzCrateBase {
	const char *GetName()
	{
		return "Base Defense Power";
	};
	int GetType()
	{
		return CRATE_SOLDIER;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->BaseDefensePower;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (!gzCrateMgr->m_data->BasePower.enable)
		{
			gzCrateMgr->m_data->BasePower.team    = stRandom.GetInt(0, 1);
			gzCrateMgr->m_data->BasePower.counter = (float)stRandom.GetInt(60, 300);
			nc_BuildingGameObj *BaseDef = nc_BaseControllerClass::Find_Base(gzCrateMgr->m_data->BasePower.team)->Find_Building(nc_BASEDEF);
			if (BaseDef && !BaseDef->Destroyed)
			{
				gzCrateMgr->m_data->BasePower.building = BaseDef;
				gzCrateMgr->m_data->BasePower.enable   = true;
				gzCrateMgr->m_data->BasePower.powered  = BaseDef->IsPowered;
				BaseDef->IsPowered ^= 1;
				BaseDef->Set_Object_Dirty_Bit(nc_DB_RARE, true);
				if (BaseDef->IsPowered)
					stConsole::In("msg [Crate]: %s found an extra power cell in their Power Plant. They have returned power to their main base defense for another %.0f seconds.", (gzCrateMgr->m_data->BasePower.team == 1 ? "GDI" : "Nod"), gzCrateMgr->m_data->BasePower.counter);
				else
					stConsole::In("msg [Crate]: %s just had a power surge and the power flowing to the main base defense has stopped for %.0f seconds.", (gzCrateMgr->m_data->BasePower.team == 1 ? "GDI" : "Nod"), gzCrateMgr->m_data->BasePower.counter);
			}
			return true;
		}
		return false;
	};
};
Crate_BasePower CrateBasePower;


/******************/
/* Vehicle crates */
/******************/
class Crate_VehicleMaxArmor : public gzCrateBase {
	const char *GetName()
	{
		return "Vehicle Max Armor";
	};
	int GetType()
	{
		return CRATE_VEHICLE;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->vMaxArmor;
	};
	bool SkipDefaultOutput()
	{
		return true;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int armor = stRandom.Get_Int(5, 50), rnd = stRandom.Get_Int(0, 1);
		if (rnd == 0)
			Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrengthMax += (float)armor;
		else
		{
			Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrengthMax -= (float)armor;
			if (Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrength.Get() > Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrengthMax.Get())
				Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrength = Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrengthMax.Get();
		}
		Picker->As_SoldierGameObj()->Vehicle->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
		PagePlayer(Picker->Player->PlayerId, "Crate God has %s your current vehicle armor to %0.f!", rnd == 0 ? "increased" : "decreased", Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrengthMax.Get());
		gzLogger("_CRATE", "%ls picked up a Vehicle %s Crate.", Picker->Player->PlayerName.m_Buffer, rnd == 0 ? "Armor" : "De-Armor");
		return true;
	};
};
Crate_VehicleMaxArmor CrateVehicleMaxArmor;

class Crate_VehicleMaxHealth : public gzCrateBase {
	const char *GetName()
	{
		return "Vehicle Max Health";
	};
	int GetType()
	{
		return CRATE_VEHICLE;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->vMaxHealth;
	};
	bool SkipDefaultOutput()
	{
		return true;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		int health = stRandom.Get_Int(5, 50), rnd = stRandom.Get_Int(0, 1);
		if (rnd == 0)
			Picker->As_SoldierGameObj()->Vehicle->Defense.HealthMax += (float)health;
		else
		{
			Picker->As_SoldierGameObj()->Vehicle->Defense.HealthMax -= (float)health;
			if (Picker->As_SoldierGameObj()->Vehicle->Defense.Health.Get() > Picker->As_SoldierGameObj()->Vehicle->Defense.HealthMax.Get())
				Picker->As_SoldierGameObj()->Vehicle->Defense.Health = Picker->As_SoldierGameObj()->Vehicle->Defense.HealthMax.Get();
		}
		Picker->As_SoldierGameObj()->Vehicle->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
		PagePlayer(Picker->Player->PlayerId, "Crate God has %s your current vehicle health to %0.f!", rnd == 0 ? "increased" : "decreased", Picker->As_SoldierGameObj()->Vehicle->Defense.HealthMax.Get());
		gzLogger("_CRATE", "%ls picked up a Vehicle %s Crate.", Picker->Player->PlayerName.m_Buffer, rnd == 0 ? "Health" : "De-Health");
		return true;
	};
};
Crate_VehicleMaxHealth CrateVehicleMaxHealth;

class Crate_VehicleRepair : public gzCrateBase {
	const char *GetName()
	{
		return "Vehicle Repair";
	};
	int GetType()
	{
		return CRATE_VEHICLE;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->vRepair;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		// Full health and armor doesn't need to repair
		if (Picker->As_SoldierGameObj()->Vehicle->Defense.Health.Get() == Picker->As_SoldierGameObj()->Vehicle->Defense.HealthMax.Get()
			&& Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrength.Get() == Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrengthMax.Get())
		{
			return false;
		}

		Picker->As_SoldierGameObj()->Vehicle->Defense.Health = Picker->As_SoldierGameObj()->Vehicle->Defense.HealthMax.Get();
		Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrength = Picker->As_SoldierGameObj()->Vehicle->Defense.ShieldStrengthMax.Get();
		Picker->As_SoldierGameObj()->Vehicle->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: Crate God has fully repaired your current vehicle.");
		return true;
	};
};
Crate_VehicleRepair CrateVehicleRepair;

class Crate_VehicleAutoRepair : public gzCrateBase {
	const char *GetName()
	{
		return "Vehicle Auto Repair";
	};
	int GetType()
	{
		return CRATE_VEHICLE;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->vAutoRepair;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		for (unsigned int i = 0; i < gzCrateMgr->m_data->VehicleAutoRepair.Count(); i++)
		{
			if (gzCrateMgr->m_data->VehicleAutoRepair[i].vehId == Picker->As_SoldierGameObj()->Vehicle->NetworkID)
				return false;
		}
		gzCrateVehicleAutoRepairStruct repair;
		repair.vehId = Picker->As_SoldierGameObj()->Vehicle->NetworkID;
		repair.counter = 1.0f;
		gzCrateMgr->m_data->VehicleAutoRepair.Add(repair);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: Crate God has installed a self-repair component to your current vehicle.");
		return true;
	};
};
Crate_VehicleAutoRepair CrateVehicleAutoRepair;

class Crate_VehicleDemoTruck : public gzCrateBase {
	const char *GetName()
	{
		return "Demo Truck";
	};
	int GetType()
	{
		return CRATE_VEHICLE;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->vDemoTruck;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		for (unsigned int i = 0; i < gzCrateMgr->m_data->DemoTruck.Count(); i++)
		{
			if (gzCrateMgr->m_data->DemoTruck[i] == Picker->As_SoldierGameObj()->Vehicle->NetworkID)
				return false;
		}
		gzCrateMgr->m_data->DemoTruck.Add(Picker->As_SoldierGameObj()->Vehicle->NetworkID);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: Explosives have been attached to your current vehicle. Once it dies, they will explode and make a BIG BOOM with random damage!!!!! Good luck!");
		return true;
	};
};
Crate_VehicleDemoTruck CrateVehicleDemoTruck;

class Crate_VehicleAntiEnemy : public gzCrateBase {
	const char *GetName()
	{
		return "Vehicle Anti Enemy";
	};
	int GetType()
	{
		return CRATE_VEHICLE;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->vAntiEnemy;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		for (unsigned int i = 0; i < gzCrateMgr->m_data->VehicleAntiEnemy.Count(); i++)
		{
			if (gzCrateMgr->m_data->VehicleAntiEnemy[i] == Picker->As_SoldierGameObj()->Vehicle->NetworkID)
				return false;
		}
		
		// Check for players in the vehicle
		nc_VehicleGameObj *vehicle			= gzStatic_Cast(nc_VehicleGameObj, Picker->As_SoldierGameObj()->Vehicle);
		nc_VehicleGameObjDef *vehicleDef	= gzStatic_Cast(nc_VehicleGameObjDef, Picker->As_SoldierGameObj()->Vehicle->definition);
		for (int i = 0; i < vehicle->SeatsList.Length(); i++)
		{
			if (vehicle->SeatsList[i] != NULL)
			{
				if (vehicleDef->PlayerType != vehicle->SeatsList[i]->PlayerType)
					nc_TransitionManager::Check(vehicle->SeatsList[i], true);
			}
		}

		gzCrateMgr->m_data->VehicleAntiEnemy.Add(vehicle->NetworkID);
		PagePlayer(Picker->Player->PlayerId, "[Crate]: Crate God has installed an Anti-Enemy(based on the default side of the vehicle) component to your current vehicle. Therefore they will be killed when trying to enter.");
		return true;
	};
};
Crate_VehicleAntiEnemy CrateVehicleAntiEnemy;

class Crate_VehicleSpy : public gzCrateBase {
	const char *GetName()
	{
		return "Vehicle Spy";
	};
	int GetType()
	{
		return CRATE_VEHICLE;
	};
	unsigned int GetPercent()
	{
		return this->m_percent->vSpy;
	};
	bool Pick(nc_SmartGameObj *Picker)
	{
		if (!Picker->As_SoldierGameObj()->Vehicle->VehicleCanEnemySeen)
			return false;

		Picker->As_SoldierGameObj()->Vehicle->VehicleCanEnemySeen = false;
		PagePlayer(Picker->Player->PlayerId, "[Crate]: [Crate]: Crate God has installed a spy compoment to the vehicle. Base defenses will no longer shoot at your current vehicle.");
		stConsole::In("msg [Crate]: Oh no! A %s player just got a Vehicle Spy crate, better watch your base %s!", Picker->PlayerType == 0 ? "Nod" : "GDI", Picker->PlayerType == 0 ? "GDI" : "Nod");
		return true;
	};
};
Crate_VehicleSpy CrateVehicleSpy;


/*************************/
/* Yellow crate override */
/*************************/
void M00_CNC_Crate::Custom(nc_ScriptableGameObj *obj, int message, int param, nc_ScriptableGameObj *sender)
{
	if (message == CUSTOM_EVENT_POWERUP)
	{
		switch (stRandom.Get_Int(1, 3))
		{
			case 1:
				if (sender && sender->As_SmartGameObj() && sender->As_SmartGameObj()->Player)
					sender->As_SmartGameObj()->Player->Increment_Money(100.0f);
				break;
			case 2:
				if (sender && sender->As_SmartGameObj() && sender->As_SmartGameObj()->Player)
					sender->As_SmartGameObj()->Player->Increment_Money(200.0f);
				break;
			case 3:
				gzCommands->Give_Powerup(sender, "CnC_POW_Ammo_ClipMax", false);
				break;
		}
	}
}
nc_ScriptRegistrant<M00_CNC_Crate> M00_CNC_Crate_Registrant("M00_CNC_Crate", "");


/********************/
/* Console commands */
/********************/
class GiveCrateConsoleFunction : public gzConsoleCommand {
public:
	char *Get_Name(void)
	{
		return "givecrate";
	}
	char *Get_Help(void)
	{
		return "";
	}
	char *Get_Alias(void)
	{
		return "gc";
	}
	void Activate(char *text)
	{
		unsigned long id = 0;
		char *token;
		token = strtok(text," ");
		if (!token) return;
		id = atoi(token);
		if (!nc_GameObjManager::Find_PhysicalGameObj(gzCrateMgr->m_crateId))
			return;
		nc_cPlayer *pData = nc_cPlayerManager::Find_Player(id);
		if (!pData || !pData->Owner.Reference)
			return;
		gzEventObjectPowerupGrant evt;
		evt.m_powerup = nc_GameObjManager::Find_PhysicalGameObj(gzCrateMgr->m_crateId)->As_PowerUpGameObj();
		evt.m_soldier = pData->Owner.Reference->obj->As_SoldierGameObj();
		gzCrateMgr->Object_PowerupGrant(evt);
	}
};
GiveCrateConsoleFunction givecrate;
