#include "stinc.h"
#include "stmisc.h"
#include "stgame.h"
#include "stplayer.h"
#include "stchat.h"
#include "stveteran.h"

// Forward
class gzChat_VetInfo;

void gzSettingsVeteranClass::Delete()
{
	for (unsigned int i = 0; i < this->m_presets.Count(); i++)
		delete this->m_presets[i];
	this->m_presets.Clear();

	for (unsigned int i = 0; i < this->m_level.Count(); i++)
		delete this->m_level[i];
	this->m_level.Clear();

	for (unsigned int i = 0; i < this->m_points.Count(); i++)
		delete this->m_points[i];
	this->m_points.Clear();
	
	this->m_enable = false;
}
void gzSettingsVeteranClass::Load()
{
	this->Delete();

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

	this->m_enable			= this->Load_Bool("General", "Enable", false);
	this->m_c4Points[0][0]	= this->Load_Float("Points", "C4_RemoteDamage", 0.0f);
	this->m_c4Points[0][1]	= this->Load_Float("Points", "C4_RemoteDestroy", 0.0f);
	this->m_c4Points[1][0]	= this->Load_Float("Points", "C4_TimedDamage", 0.0f);
	this->m_c4Points[1][1]	= this->Load_Float("Points", "C4_TimedDestroy", 0.0f);
	this->m_c4Points[2][0]	= this->Load_Float("Points", "C4_ProxyDamage", 0.0f);
	this->m_c4Points[2][1]	= this->Load_Float("Points", "C4_ProxyDestroy", 0.0f);

	if (this->m_enable)
	{
		// Level definitions
		for (int i = 0; ; i++)
		{
			aString entry = this->Load_String("LevelEntry", aString::Format("%d", i), "");
			if (entry.IsEmpty())
			{
				if (i == 0)
				{
					gzVeteranLevelDefStruct *data			= new gzVeteranLevelDefStruct;
					data->AddArmor							= 0.0f;
					data->AddHealth							= 0.0f;
					data->Discount							= 0.0f;
					data->Level								= 0;
					data->Name								= "Recruit";
					data->RequiredGameTime					= 0;
					data->RequiredPoints					= 0.0f;
					data->RequiredScore						= 0.0f;
					data->SoldierHealthRegeneration			= 0.0f;
					data->VehicleHealthRegeneration			= 0.0f;
					data->IncreaseRequirementStart			= 0;
					data->IncreaseRequirementInterval		= 0;
					data->IncreaseRequirementPercent		= 0.0f;
					data->IncreaseRequirementUsePromoteTime	= false;
					data->CanPromoteByCrate					= true;
					this->m_level.Add(data);
				}
				break;
			}
			else
			{
				if (this->m_cfg->GetSubCount(entry) == 0)
				{
					stConsole::Out("[Veteran] Error - Level definition \"%s\" not found. Halting level definition collection...\n", entry.GetString());
					entry.Free_String();
					break;
				}
			}

			gzVeteranLevelDefStruct *data			= new gzVeteranLevelDefStruct;
			data->Name								= entry;
			data->Level								= i;
			data->AddArmor							= this->Load_Float(entry.GetString(), "Armor", 0.0f);
			data->AddHealth							= this->Load_Float(entry.GetString(), "Health", 0.0f);
			data->Discount							= this->Load_Float(entry.GetString(), "Discount", 0.0f);
			data->SoldierHealthRegeneration			= this->Load_Float(entry.GetString(), "SoldierHealthRegenerate", 0.0f);
			data->VehicleHealthRegeneration			= this->Load_Float(entry.GetString(), "VehicleHealthRegenerate", 0.0f);
			data->RequiredPoints					= this->Load_Float(entry.GetString(), "RequiredPoints", 0.0f);
			data->RequiredScore						= this->Load_Float(entry.GetString(), "RequiredScore", 0.0f);
			data->RequiredGameTime					= this->Load_Int(entry.GetString(), "RequiredGameTime", 0) * 60;
			data->IncreaseRequirementStart			= this->Load_Int(entry.GetString(), "IncreaseRequirementStart", 0) * 60;
			data->IncreaseRequirementInterval		= this->Load_Int(entry.GetString(), "IncreaseRequirementInterval", 1) * 60;
			data->IncreaseRequirementPercent		= this->Load_Float(entry.GetString(), "IncreaseRequirementPercent", 0.0f);
			data->IncreaseRequirementUsePromoteTime	= this->Load_Bool(entry.GetString(), "IncreaseRequirementUsePromoteTime", true);
			data->CanPromoteByCrate					= this->Load_Bool(entry.GetString(), "CanPromoteByCrate", true);
			if (i > 0) // Not the start level
			{
				if (data->IncreaseRequirementStart > 0 && data->IncreaseRequirementStart < data->RequiredGameTime)
				{
					data->IncreaseRequirementStart = data->RequiredGameTime;
					stConsole::Out("[Veteran] Error - IncreaseRequirementStart can't be lower than RequiredGameTime. It's now set to %d minute(s).\n",data->IncreaseRequirementStart);
				}
				if (data->IncreaseRequirementInterval < 1)
				{
					data->IncreaseRequirementInterval = 1;
					stConsole::Out("[Veteran] Error - IncreaseRequirementInterval must be larger than zero. It's now been set to 1 minute.\n");
				}
				if (data->IncreaseRequirementPercent < 0.0f)
				{
					data->IncreaseRequirementPercent = 0.0f;
					stConsole::Out("[Veteran] Error - IncreaseRequirementPercent must be larger than zero. It's now being disabled.\n");
				}
			}
			this->m_level.Add(data);
		}

		// Preset definitions
		unsigned long Type[] = { 0x3001, 0x3010, 0x3016, 0xD001, 0xD002, 0xD003, 0xD004, 0xD005, 0xD006, 0xD007, 0xD008, 0xD009 };
		const char *ObserverName[] = { "gzObserverVeteranSoldier", "gzObserverVeteranVehicle", "gzObserverVeteranBeacon", 0, 0, 0, 0, 0, 0, 0, 0, 0 };
		for (int i = 0; i < sizeof(Type) / sizeof(long); i++)
		{
			nc_DefinitionClass *Definition = nc_DefinitionMgrClass::Get_First(Type[i], 1);
			while (Definition)
			{
				aString entry = this->Load_String("Preset", Definition->Get_Name(), "");
				if (!entry.IsEmpty())
				{
					gzVeteranPresetDefStruct *preset = new gzVeteranPresetDefStruct;
					preset->Definition = Definition;
					preset->Points = NULL;
					for (unsigned int j = 0; j < this->m_points.Count(); j++)
					{
						if (this->m_points[j]->name == entry)
						{
							preset->Points = this->m_points[j];
							goto pointsFound;
						}
					}
					gzVeteranPointsDefStruct *points = new gzVeteranPointsDefStruct;
					points->damage                   = this->Load_Float(entry.GetString(), "DamagePoints", 0.0f);
					points->destroy                  = this->Load_Float(entry.GetString(), "DestroyPoints", 0.0f);
					points->repair                   = this->Load_Float(entry.GetString(), "RepairPoints", 0.0f);
					points->suicide                  = this->Load_Float(entry.GetString(), "SuicidePoints", 0.0f);
					points->stealByEnemy             = this->Load_Float(entry.GetString(), "StealPoints", 0.0f);
					points->name                     = entry;
					this->m_points.Add(points);

					preset->Points                   = points;
pointsFound:;
					this->m_presets.Add(preset);
				}
				Definition = nc_DefinitionMgrClass::Get_Next(Definition, Type[i], 1);
			}
		}
	}
	this->Close();
}

gzVeteranManagerClass *gzVeteranMgr = NULL;
void gzVeteranManagerClass::Delete()
{
}
void gzVeteranManagerClass::Settings_Loaded()
{
}
gzVeteranManagerClass::gzVeteranManagerClass()
{
	this->RegisterEvent(EVT_GAME_LEVEL_LOADED);
	this->RegisterEvent(EVT_OBJECT_C4_CREATE);

	gzGameMgr->AddObserver(0x3001, "gzObserverVeteranSoldier");
	gzGameMgr->AddObserver(0x3010, "gzObserverVeteranVehicle");
	gzGameMgr->AddObserver(0x3016, "gzObserverVeteranBeacon");

	gzChatCommand_Reg<gzChat_VetInfo> gzChat_VetInfo_Reg("!vstats,!vstatus,!vetstats,!vetstatus", (MSG_ALL | MSG_TEAM), "VetInfo");

	this->m_settings = new gzSettingsVeteranClass;
	this->m_settings->SetOwner(this);
}
void gzVeteranManagerClass::Level_Loaded()
{
	if (this->m_settings->m_enable)
	{
		for (nc_GenericSLNode<nc_BaseGameObj> *objList = nc_GameObjManager::GameObjList->HeadNode; objList != NULL; objList = objList->NodeNext)
		{
			if (objList->NodeData->As_ScriptableGameObj())
			{
				if (objList->NodeData->As_ScriptableGameObj()->As_BuildingGameObj())
					objList->NodeData->As_ScriptableGameObj()->Insert_Observer(nc_ScriptManager::Create_Script("gzObserverVeteranBuilding"));

				// Turrets
				else if (objList->NodeData->As_VehicleGameObj())
					objList->NodeData->As_ScriptableGameObj()->Insert_Observer(nc_ScriptManager::Create_Script("gzObserverVeteranVehicle"));
			}
		}
	}
}
void gzVeteranManagerClass::Object_C4Creation(gzEventObjectC4Creation &evt)
{
	if (this->m_settings->m_enable)
	{
		evt.m_C4->Add_Observer(nc_ScriptManager::Create_Script("gzObserverVeteranC4"));
	}
}
gzSettingsVeteranClass *gzVeteranManagerClass::GetSettings()
{
	return this->m_settings;
}
const gzVeteranPresetDefStruct *gzVeteranManagerClass::FindPresetSetting(const char *preset)
{
	for (unsigned int i = 0; i < this->m_settings->m_presets.Count(); i++)
	{
		if (!stricmp(this->m_settings->m_presets[i]->Definition->Get_Name(), preset))
			return this->m_settings->m_presets[i];
	}
	return NULL;
}

gzVeteranClass::gzVeteranClass(gzPlayer *owner)
{
	this->RegisterEvent(EVT_GAME_LEVEL_LOADED);
	this->RegisterEvent(EVT_PLAYER_PURCHASE);

	this->m_owner		= owner;
	this->m_points		= 0.0f;
	if (gzVeteranMgr->GetSettings()->m_enable)
	{
		this->m_currLevel	= gzVeteranMgr->GetSettings()->m_level[0];
		this->m_nextLevel	= (gzVeteranMgr->GetSettings()->m_level.Count() > 1 ? gzVeteranMgr->GetSettings()->m_level[1] : NULL);
	}
	else
	{
		this->m_currLevel = NULL;
		this->m_nextLevel = NULL;
	}
	this->m_promoteTime	= 0.0f;
}
void gzVeteranClass::Level_Loaded()
{
	this->m_points		= 0.0f;
	if (gzVeteranMgr->GetSettings()->m_enable)
	{
		this->m_currLevel	= gzVeteranMgr->GetSettings()->m_level[0];
		this->m_nextLevel	= (gzVeteranMgr->GetSettings()->m_level.Count() > 1 ? gzVeteranMgr->GetSettings()->m_level[1] : NULL);
	}
	else
	{
		this->m_currLevel = NULL;
		this->m_nextLevel = NULL;
	}
	this->m_promoteTime	= 0.0f;
}
void gzVeteranClass::Player_Purchase(gzEventPlayerPurchase &evt)
{
	if (this->m_currLevel != NULL)
	{
		if (evt.GetPlayer() == this->m_owner->GetPlayerData())
		{
			if (this->m_currLevel->Discount > 0.0f)
				evt.m_cost -= (int)((float)evt.m_originalCost / 100.0f * this->m_currLevel->Discount);
		}
	}
}
void gzVeteranClass::AddPoints(float points)
{
	if (gzVeteranMgr->GetSettings()->m_enable)
	{
		this->m_points += points;
	
		aString pointMsg;
		if (points <= -0.01f)
			pointMsg.Printf(" Vet-Points %.2f", points);
		else if (points >= 0.01)
			pointMsg.Printf(" Vet-Points +%.2f", points);
		if (!pointMsg.IsEmpty() && this->m_owner->GetPlayerData()->Owner.Reference)
			gzDisplay_Int_Player(this->m_owner->GetPlayerData()->Owner.Reference->obj, 0, pointMsg.GetString());

		if (this->m_nextLevel)
		{
			float multipler = this->GetMultipler();
			if (this->m_points > (this->m_nextLevel->RequiredPoints * multipler) && this->m_owner->GetPlayerData()->Score.Get() > (this->m_nextLevel->RequiredScore * multipler) && this->m_owner->GetPlayerData()->GameTime > this->m_nextLevel->RequiredGameTime)
				this->Promote();
		}
	}
}
bool gzVeteranClass::Promote()
{
#if VERC(1, 1, 0)
	if (!cGame->Is_Gameplay_Permitted())
		return false;
#endif

	if (!gzVeteranMgr->GetSettings()->m_enable || this->m_nextLevel == NULL)
		return false;

	if (this->GetOwner()->GetSoldier() && !stricmp(this->GetOwner()->GetSoldier()->definition->Get_Name(), "CnC_Visceroid"))
		return false;

	this->m_currLevel = this->m_nextLevel;
	this->m_nextLevel = ((gzVeteranMgr->GetSettings()->m_level.Count() - 1) > this->m_currLevel->Level ? gzVeteranMgr->GetSettings()->m_level[this->m_currLevel->Level + 1] : NULL);
	this->m_promoteTime = this->m_owner->GetPlayerData()->GameTime;
	if (this->m_owner->GetPlayerData()->Owner.Reference)
	{
		nc_SoldierGameObj *soldier = this->m_owner->GetPlayerData()->Owner.Reference->obj->As_SoldierGameObj();

#if VERC(1, 1, 0)
		float addArmor  = (gzStatic_Cast(nc_DamageableGameObjDef, soldier->definition)->DefenseDef.ShieldStrengthMax.Get() / 100.0f * this->m_currLevel->AddArmor);
		float addHealth = (gzStatic_Cast(nc_DamageableGameObjDef, soldier->definition)->DefenseDef.HealthMax.Get() / 100.0f * this->m_currLevel->AddHealth);
		soldier->Defense.Health            += addHealth;
		soldier->Defense.HealthMax         += addHealth;
		soldier->Defense.ShieldStrength    += addArmor;
		soldier->Defense.ShieldStrengthMax += addArmor;
#else
		float maxArmor = gzStatic_Cast(nc_DamageableGameObjDef, soldier->definition)->DefenseDef.ShieldStrengthMax.Get() / 100.0f * (100.0f + this->m_currLevel->AddArmor);
		float maxHealth = gzStatic_Cast(nc_DamageableGameObjDef, soldier->definition)->DefenseDef.HealthMax.Get() / 100.0f * (100.0f + this->m_currLevel->AddHealth);
		soldier->Defense.ShieldStrengthMax = maxArmor;
		soldier->Defense.HealthMax = maxHealth;
#endif
		soldier->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
	}
	stConsole::In(
		"msg [Veteran]: %ls has been promoted to %s Status.",
		this->m_owner->GetPlayerName().GetString(),
		this->m_currLevel->Name.GetString()
	);
	PagePlayer(
		this->m_owner->GetPlayerData()->PlayerId,
		"[Veteran]: Congratulations! You've been promoted to %s.",
		this->m_currLevel->Name.GetString()
	);
	PagePlayer(
		this->m_owner->GetPlayerData()->PlayerId,
		"[Veteran]: Your status provide %.0f%% and %.0f%% extra of max armor and health for you and your recently purchased vehicle(s), and %.0f%% discount to all purchases.",
		this->m_currLevel->AddArmor,
		this->m_currLevel->AddHealth,
		this->m_currLevel->Discount
	);
	return true;
}
float gzVeteranClass::GetMultipler()
{
	if (this->m_nextLevel != NULL)
	{
		if (this->m_nextLevel->IncreaseRequirementUsePromoteTime)
		{
			if ((this->m_owner->GetPlayerData()->GameTime - this->m_promoteTime) > this->m_nextLevel->IncreaseRequirementStart)
				return pow(1.0f + (this->m_nextLevel->IncreaseRequirementPercent / 100), floor(((this->m_owner->GetPlayerData()->GameTime - this->m_promoteTime) - this->m_nextLevel->IncreaseRequirementStart) / this->m_nextLevel->IncreaseRequirementInterval));
		}
		else
		{
			if (this->m_owner->GetPlayerData()->GameTime > this->m_nextLevel->IncreaseRequirementStart)
				return pow(1.0f + (this->m_nextLevel->IncreaseRequirementPercent / 100), floor((this->m_owner->GetPlayerData()->GameTime - (float)this->m_nextLevel->IncreaseRequirementStart) / this->m_nextLevel->IncreaseRequirementInterval));
		}
	}
	return 1.0f;
}
gzPlayer *gzVeteranClass::GetOwner()
{
	return this->m_owner;
}
gzVeteranLevelDefStruct *gzVeteranClass::GetCurrentLevel()
{
	return this->m_currLevel;
}
gzVeteranLevelDefStruct *gzVeteranClass::GetNextLevel()
{
	return this->m_nextLevel;
}
float gzVeteranClass::GetPoints()
{
	return this->m_points;
}


/*************/
/* Observers */
/*************/
void gzObserverVeteranBeacon::Created(nc_ScriptableGameObj *obj)
{
	if (!gzVeteranMgr->GetSettings()->m_enable || obj->definition->Get_Class_ID() != 0x3016)
	{
		this->Destroy_Script();
		return;
	}

	this->m_data = gzVeteranMgr->FindPresetSetting(obj->definition->Get_Name());
	if (this->m_data == NULL)
	{
#ifdef VETMSG
		stConsole::Out("[Veteran] Error - Unable to find class definition for preset: %s\n", obj->definition->Get_Name());
#endif
		this->Destroy_Script();
	}
	else
	{
		this->m_maxHealth = obj->As_DamageableGameObj()->Defense.Health.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrength.Get();
		for (int i = 1; i <= 127; i++)
			this->m_damage[i] = 0.0f;
	}
}
void gzObserverVeteranBeacon::Custom(nc_ScriptableGameObj *obj, int message, int param, nc_ScriptableGameObj *sender)
{
	if (message == 10001)
		this->m_damage[param] = 0.0f;
}
void gzObserverVeteranBeacon::Damaged(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *damager, float damage)
{
	if (damager && damager->As_SoldierGameObj() && damager->As_SoldierGameObj()->Player)
	{
		if (damage > 0.0f)
		{
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
				this->m_damage[damager->As_SoldierGameObj()->Player->PlayerId] -= damage;
			else
				this->m_damage[damager->As_SoldierGameObj()->Player->PlayerId] += damage;
		}
	}
}
void gzObserverVeteranBeacon::Killed(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *shooter)
{
	if (this->m_data)
	{
		float damagePoints	= this->m_data->Points->damage;
		float mostDamage	= 0.0f;
		aVector<int> mostDamager;

		for (int i = 1; i <= 127; i++)
		{
			if (this->m_damage[i] > 0.0f)
			{
				if (this->m_damage[i] >= mostDamage)
				{
					if (this->m_damage[i] == mostDamage)
						mostDamager.Add(i);
					else
					{
						mostDamager.Clear();
						mostDamager.Add(i);
					}
					mostDamage = this->m_damage[i];
				}
				gzPlayer *gzData = gzPlayerManager::Find(i);
				if (gzData)
					gzData->GetVeteran()->AddPoints(damagePoints / this->m_maxHealth * this->m_damage[i]);
			}
		}

		if (mostDamager.Count() == 0)
			return;

		for (unsigned int i = 0; i < mostDamager.Count(); i++)
		{
			gzPlayer *gzData = gzPlayerManager::Find(mostDamager[i]);
			if (gzData)
				gzData->GetVeteran()->AddPoints(this->m_data->Points->destroy / mostDamager.Count());
		}
	}
}
nc_ScriptRegistrant<gzObserverVeteranBeacon> gzObserverVeteranBeacon_Reg("gzObserverVeteranBeacon", "");

void gzObserverVeteranC4::Created(nc_ScriptableGameObj *obj)
{
	if (!gzVeteranMgr->GetSettings()->m_enable || obj->definition->Get_Class_ID() != 0x3006)
	{
		this->Destroy_Script();
		return;
	}

	this->m_maxHealth = obj->As_DamageableGameObj()->Defense.Health.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrength.Get();
	for (int i = 1; i <= 127; i++)
		this->m_damage[i] = 0.0f;
	this->m_c4 = gzStatic_Cast(nc_C4GameObj, obj);
}
void gzObserverVeteranC4::Custom(nc_ScriptableGameObj *obj, int message, int param, nc_ScriptableGameObj *sender)
{
	if (message == 10001)
		this->m_damage[param] = 0.0f;
}
void gzObserverVeteranC4::Damaged(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *damager, float damage)
{
	if (damager && damager->As_SoldierGameObj() && damager->As_SoldierGameObj()->Player)
	{
		if (damage > 0.0f)
		{
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
				this->m_damage[damager->As_SoldierGameObj()->Player->PlayerId] -= damage;
			else
				this->m_damage[damager->As_SoldierGameObj()->Player->PlayerId] += damage;
		}
	}
}
void gzObserverVeteranC4::Killed(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *shooter)
{
	float mostDamage = 0.0f;
	aVector<int> mostDamager;

	for (int i = 1; i <= 127; i++)
	{
		if (this->m_damage[i] > 0.0f)
		{
			if (this->m_damage[i] >= mostDamage)
			{
				if (this->m_damage[i] == mostDamage)
					mostDamager.Add(i);
				else
				{
					mostDamager.Clear();
					mostDamager.Add(i);
				}
				mostDamage = this->m_damage[i];
			}
			gzPlayer *gzData = gzPlayerManager::Find(i);
			if (gzData && this->m_damage[i] > 0.0f)
				gzData->GetVeteran()->AddPoints(gzVeteranMgr->GetSettings()->m_c4Points[this->m_c4->AmmoDef->AmmoType.Get() - 1][0] / this->m_maxHealth * this->m_damage[i]);
		}
	}

	if (mostDamager.Count() == 0)
		return;

	for (unsigned int i = 0; i < mostDamager.Count(); i++)
	{
		gzPlayer *gzData = gzPlayerManager::Find(mostDamager[i]);
		if (gzData)
			gzData->GetVeteran()->AddPoints(gzVeteranMgr->GetSettings()->m_c4Points[this->m_c4->AmmoDef->AmmoType.Get() - 1][1] / mostDamager.Count());
	}
}
nc_ScriptRegistrant<gzObserverVeteranC4> gzObserverVeteranC4_Reg("gzObserverVeteranC4", "");

void gzObserverVeteranBuilding::Created(nc_ScriptableGameObj *obj)
{
	if (!gzVeteranMgr->GetSettings()->m_enable || !obj->As_BuildingGameObj())
	{
		this->Destroy_Script();
		return;
	}

	this->m_data = gzVeteranMgr->FindPresetSetting(obj->definition->Get_Name());
	if (this->m_data == NULL)
	{
#ifdef VETMSG
		stConsole::Out("[Veteran] Error - Unable to find class definition for preset: %s\n", obj->definition->Get_Name());
#endif
		this->Destroy_Script();
	}
	else
	{
		for (int i = 1; i <= 127; i++)
		{
			this->m_damage[i] = 0.0f;
			this->m_repair[i] = 0.0f;
		}
		this->m_allowRepair = 0.0f;
		this->m_totalDamage = 0.0f;

		obj->Start_Observer_Timer(this->GetID(), 60.0f, 2);
	}
}
void gzObserverVeteranBuilding::Custom(nc_ScriptableGameObj *obj, int message, int param, nc_ScriptableGameObj *sender)
{
	if (message == 10001)
	{
		gzPlayer *gzData = gzPlayerManager::Find(param);
		if (gzData && this->m_repair[param] > 0.0f)
			gzData->GetVeteran()->AddPoints(this->m_repair[param] * this->m_data->Points->repair);
		this->m_totalDamage -= this->m_damage[param];
		this->m_damage[param] = 0.0f;
		this->m_repair[param] = 0.0f;
	}
}
void gzObserverVeteranBuilding::Damaged(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *damager, float damage)
{
	if (damager && damager->As_SmartGameObj() && damager->As_SmartGameObj()->Player)
	{
		if (damage > 0.0f)
		{
			this->m_totalDamage += damage;
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
				this->m_damage[damager->As_SmartGameObj()->Player->PlayerId] -= damage;
			else
			{
				this->m_damage[damager->As_SmartGameObj()->Player->PlayerId] += damage;
				this->m_allowRepair += damage;
			}
		}
		else if (damage < 0.0f)
		{
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
			{
				if (this->m_allowRepair > 0.0f)
				{
					this->m_repair[damager->As_SmartGameObj()->Player->PlayerId] += (damage * -1);
					this->m_allowRepair -= (damage * -1);
				}
				if (this->m_allowRepair < 0.0f)
					this->m_allowRepair = 0.0f;
			}
			else
				this->m_repair[damager->As_SmartGameObj()->Player->PlayerId] += damage;
		}
	}
}
void gzObserverVeteranBuilding::Killed(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *shooter)
{
	if (this->m_data)
	{
		// Repair points
		for (int i = 1; i <= 127; i++)
		{
			gzPlayer *gzData = gzPlayerManager::Find(i);
			if (gzData && this->m_repair[i] > 0.0f)
				gzData->GetVeteran()->AddPoints(this->m_repair[i] * this->m_data->Points->repair);
			this->m_repair[i] = 0.0f;
		}

		// Damage points
		if (this->m_totalDamage == 0.0f)
			return;

		float damagePoints	= this->m_data->Points->damage;
		float mostDamage	= 0.0f;
		aVector<int> mostDamager;

		if ((obj->As_DamageableGameObj()->Defense.HealthMax.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrengthMax.Get()) > this->m_totalDamage)
			damagePoints *= this->m_totalDamage / (obj->As_DamageableGameObj()->Defense.HealthMax.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrengthMax.Get());

		for (int i = 1; i <= 127; i++)
		{
			if (this->m_damage[i] > 0.0f)
			{
				if (this->m_damage[i] >= mostDamage)
				{
					if (this->m_damage[i] == mostDamage)
						mostDamager.Add(i);
					else
					{
						mostDamager.Clear();
						mostDamager.Add(i);
					}
					mostDamage = this->m_damage[i];
				}
				gzPlayer *gzData = gzPlayerManager::Find(i);
				if (gzData)
					gzData->GetVeteran()->AddPoints(damagePoints / this->m_totalDamage * this->m_damage[i]);
			}
		}

		if (mostDamager.Count() == 0)
			return;

		for (unsigned int i = 0; i < mostDamager.Count(); i++)
		{
			gzPlayer *gzData = gzPlayerManager::Find(mostDamager[i]);
			if (gzData)
				gzData->GetVeteran()->AddPoints(this->m_data->Points->destroy / mostDamager.Count());
		}
	}
}
void gzObserverVeteranBuilding::Timer_Expired(nc_ScriptableGameObj *obj, int number)
{
	if (number == 2)
	{
		for (int i = 1; i <= 127; i++)
		{
			gzPlayer *gzData = gzPlayerManager::Find(i);
			if (gzData && this->m_repair[i] > 0.0f)
				gzData->GetVeteran()->AddPoints(this->m_repair[i] * this->m_data->Points->repair);
			this->m_repair[i] = 0.0f;
		}
		obj->Start_Observer_Timer(this->GetID(), 60.0f, 2);
	}
}
nc_ScriptRegistrant<gzObserverVeteranBuilding> gzObserverVeteranBuilding_Reg("gzObserverVeteranBuilding", "");

void gzObserverVeteranSoldier::Created(nc_ScriptableGameObj *obj)
{
	if (!gzVeteranMgr->GetSettings()->m_enable || obj->definition->Get_Class_ID() != 0x3001 || !obj->As_SoldierGameObj()->Player)
	{
		this->Destroy_Script();
		return;
	}

	this->m_data = gzVeteranMgr->FindPresetSetting(obj->definition->Get_Name());
	if (this->m_data == NULL)
	{
#ifdef VETMSG
		stConsole::Out("[Veteran] Error - Unable to find class definition for preset: %s\n", obj->definition->Get_Name());
#endif
		this->Destroy_Script();
	}
	else
	{
		for (int i = 1; i <= 127; i++)
		{
			this->m_damage[i] = 0.0f;
			this->m_repair[i] = 0.0f;
		}
		this->m_allowRepair		= 0.0f;
		this->m_totalDamage		= 0.0f;
		this->m_soldier			= obj->As_SoldierGameObj();
		gzPlayer *gzData		= gzPlayerManager::Find(this->m_soldier->Player);
		if (gzData)
		{
			float newArmor = (gzStatic_Cast(nc_SoldierGameObjDef, this->m_soldier->definition)->DefenseDef.ShieldStrengthMax.Get() / 100.0f * (100.0f + gzData->GetVeteran()->GetCurrentLevel()->AddArmor));
			float newHealth = (gzStatic_Cast(nc_SoldierGameObjDef, this->m_soldier->definition)->DefenseDef.HealthMax.Get() / 100.0f * (100.0f + gzData->GetVeteran()->GetCurrentLevel()->AddHealth));
			this->m_soldier->Defense.Health = newHealth;
			this->m_soldier->Defense.HealthMax = newHealth;
			this->m_soldier->Defense.ShieldStrength = newArmor;
			this->m_soldier->Defense.ShieldStrengthMax = newArmor;
			this->m_soldier->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
		}
		obj->Start_Observer_Timer(this->GetID(), 1.0f, 1);
		obj->Start_Observer_Timer(this->GetID(), 60.0f, 2);
	}
}
void gzObserverVeteranSoldier::Custom(nc_ScriptableGameObj *obj, int message, int param, nc_ScriptableGameObj *sender)
{
	if (message == 10001)
	{
		gzPlayer *gzData = gzPlayerManager::Find(param);
		if (gzData)
		{
			if (this->m_repair[param] > 0.0f)
				gzData->GetVeteran();
			this->m_totalDamage -= this->m_damage[param];
			this->m_damage[param] = 0.0f;
			this->m_repair[param] = 0.0f;
		}
	}
}
void gzObserverVeteranSoldier::Damaged(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *damager, float damage)
{
	if (damager && damager->As_SmartGameObj() && damager->As_SmartGameObj()->Player)
	{
		if (damage > 0.0f && obj != damager)
		{
			this->m_totalDamage += damage;
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
				this->m_damage[damager->As_SmartGameObj()->Player->PlayerId] -= damage;
			else
			{
				this->m_allowRepair += damage;
				this->m_damage[damager->As_SmartGameObj()->Player->PlayerId] += damage;
			}
		}
		else if (damage < 0.0f && obj != damager)
		{
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
			{
				if (this->m_allowRepair > 0.0f)
				{
					this->m_repair[damager->As_SmartGameObj()->Player->PlayerId] += damage * -1;
					this->m_allowRepair += damage;
				}
				if (this->m_allowRepair < 0.0f)
					this->m_allowRepair = 0.0f;
			}
			else
				this->m_repair[damager->As_SmartGameObj()->Player->PlayerId] += damage;
		}
	}
}
void gzObserverVeteranSoldier::Killed(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *shooter)
{
	if (this->m_data)
	{
		// Repair points
		for (int i = 1; i <= 127; i++)
		{
			gzPlayer *gzData = gzPlayerManager::Find(i);
			if (gzData && i != this->m_soldier->Player->PlayerId && this->m_repair[i] != 0.0f)
				gzData->GetVeteran()->AddPoints(this->m_repair[i] * this->m_data->Points->repair);
			this->m_repair[i] = 0.0f;
		}

		// Suicide
		if (obj == shooter)
		{
			gzPlayer *gzData = gzPlayerManager::Find(this->m_soldier->Player);
			if (gzData)
				gzData->GetVeteran()->AddPoints(this->m_data->Points->suicide);
		}

		// Damage points
		if (this->m_totalDamage == 0.0f)
			return;

		float damagePoints = this->m_data->Points->destroy;
		float mostDamage = 0.0f;
		aVector<int> mostDamager;

		if ((obj->As_DamageableGameObj()->Defense.HealthMax.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrengthMax.Get()) > this->m_totalDamage)
			damagePoints *= this->m_totalDamage / (obj->As_DamageableGameObj()->Defense.HealthMax.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrengthMax.Get());

		for (int i = 1; i <= 127; i++)
		{
			if (i != this->m_soldier->Player->PlayerId)
			{
				if (this->m_damage[i] > 0.0f)
				{
					if (this->m_damage[i] >= mostDamage)
					{
						if (this->m_damage[i] == mostDamage)
							mostDamager.Add(i);
						else
						{
							mostDamager.Clear();
							mostDamager.Add(i);
						}
						mostDamage = this->m_damage[i];
					}

					gzPlayer *gzData = gzPlayerManager::Find(i);
					if (gzData)
						gzData->GetVeteran()->AddPoints(damagePoints / this->m_totalDamage * this->m_damage[i]);
				}
			}
		}

		if (mostDamager.Count() == 0)
			return;

		for (unsigned int i = 0; i < mostDamager.Count(); i++)
		{
			gzPlayer *gzData = gzPlayerManager::Find(mostDamager[i]);
			if (gzData)
				gzData->GetVeteran()->AddPoints(this->m_data->Points->destroy / mostDamager.Count());
		}
	}
}
void gzObserverVeteranSoldier::Timer_Expired(nc_ScriptableGameObj *obj, int number)
{
	if (number == 1)
	{
		gzPlayer *gzData = gzPlayerManager::Find(this->m_soldier->ControlOwner);
		if (gzData->GetVeteran()->GetCurrentLevel()->SoldierHealthRegeneration > 0.0f)
			gzCommands->Apply_Damage(obj, -gzData->GetVeteran()->GetCurrentLevel()->SoldierHealthRegeneration, "Repair", 0);
		obj->Start_Observer_Timer(this->GetID(), 1.0f, 1);
	}
	else if (number == 2)
	{
		for (int i = 1; i <= 127; i++)
		{
			if (this->m_repair[i] != 0.0f)
			{
				gzPlayer *gzData = gzPlayerManager::Find(i);
				if (gzData)
					gzData->GetVeteran()->AddPoints(this->m_repair[i] * this->m_data->Points->repair);
				this->m_repair[i] = 0.0f;
			}
		}
		obj->Start_Observer_Timer(this->GetID(), 60.0f, 2);
	}
}
nc_ScriptRegistrant<gzObserverVeteranSoldier> gzObserverVeteranSoldier_Reg("gzObserverVeteranSoldier", "");

void gzObserverVeteranVehicle::Created(nc_ScriptableGameObj *obj)
{
	if (!gzVeteranMgr->GetSettings()->m_enable || obj->definition->Get_Class_ID() != 0x3010)
	{
		this->Destroy_Script();
		return;
	}

	this->m_data = gzVeteranMgr->FindPresetSetting(obj->definition->Get_Name());
	if (this->m_data == NULL)
	{
#ifdef VETMSG
		stConsole::Out("[Veteran] Error - Unable to find class definition for preset: %s\n", obj->definition->Get_Name());
#endif
		this->Destroy_Script();
	}
	else
	{
		for (int i = 1; i <= 127; i++)
		{
			this->m_damage[i] = 0.0f;
			this->m_repair[i] = 0.0f;
		}
		this->m_allowRepair		= 0.0f;
		this->m_totalDamage		= 0.0f;
		this->m_vehicle			= obj->As_VehicleGameObj();
		this->m_stolen			= false;
		this->m_purchaser		= NULL;

		obj->Start_Observer_Timer(this->GetID(), 60.0f, 2);

		if (this->m_vehicle->SeatsList.Length() > 0)
			obj->Start_Observer_Timer(this->GetID(), 0.01f, 10);
	}
}
void gzObserverVeteranVehicle::Custom(nc_ScriptableGameObj *obj, int message,int param, nc_ScriptableGameObj *sender)
{
	if (message == 10001)
	{
		gzPlayer *gzData = gzPlayerManager::Find(param);
		if (gzData)
		{
			if (this->m_repair[param] > 0.0f)
				gzData->GetVeteran();
			this->m_totalDamage -= this->m_damage[param];
			this->m_damage[param] = 0.0f;
			this->m_repair[param] = 0.0f;
		}
	}
	else if (message == CUSTOM_EVENT_VEHICLE_ENTER)
	{
		if (this->m_purchaser && !this->m_stolen)
		{
			if (sender->As_SoldierGameObj() && sender->As_SoldierGameObj()->Player)
			{
				if (sender->As_DamageableGameObj()->Get_Player_Type() != gzStatic_Cast(nc_VehicleGameObjDef, obj->definition)->PlayerType)
				{
					gzPlayer *gzData = gzPlayerManager::Find(sender->As_SoldierGameObj()->Player);
					if (gzData)
					{
						gzData->GetVeteran()->AddPoints(this->m_data->Points->stealByEnemy);
						this->m_stolen = true;
					}
				}
			}
		}
	}
}
void gzObserverVeteranVehicle::Damaged(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *damager, float damage)
{
	if (obj->As_DamageableGameObj()->Get_Player_Type() != -2 && damager && damager->As_SmartGameObj() && damager->As_SmartGameObj()->Player)
	{
		if (damage > 0.0f && obj != damager)
		{
			this->m_totalDamage += damage;
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
				this->m_damage[damager->As_SmartGameObj()->Player->PlayerId] -= damage;
			else
			{
				this->m_allowRepair += damage;
				this->m_damage[damager->As_SmartGameObj()->Player->PlayerId] += damage;
			}
		}
		else if (damage < 0.0f && obj != damager)
		{
			if (obj->As_DamageableGameObj()->Get_Player_Type() == damager->As_DamageableGameObj()->Get_Player_Type())
			{
				if (this->m_allowRepair > 0.0f)
				{
					this->m_repair[damager->As_SmartGameObj()->Player->PlayerId] += damage * -1;
					this->m_allowRepair += damage;
				}
				if (this->m_allowRepair < 0.0f)
					this->m_allowRepair = 0.0f;
			}
			else
				this->m_repair[damager->As_SmartGameObj()->Player->PlayerId] += damage;
		}
	}
}
void gzObserverVeteranVehicle::Killed(nc_ScriptableGameObj *obj, nc_ScriptableGameObj *shooter)
{
	if (this->m_data)
	{
		// Repair points
		for (int i = 1; i <= 127; i++)
		{
			gzPlayer *gzData = gzPlayerManager::Find(i);
			if (gzData && this->m_repair[i] != 0.0f)
				gzData->GetVeteran()->AddPoints(this->m_repair[i] * this->m_data->Points->repair);
			this->m_repair[i] = 0.0f;
		}

		// Damage points
		if (this->m_totalDamage == 0.0f)
			return;

		float damagePoints = this->m_data->Points->destroy;
		float mostDamage = 0.0f;
		aVector<int> mostDamager;

		if ((obj->As_DamageableGameObj()->Defense.HealthMax.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrengthMax.Get()) > this->m_totalDamage)
			damagePoints *= this->m_totalDamage / (obj->As_DamageableGameObj()->Defense.HealthMax.Get() + obj->As_DamageableGameObj()->Defense.ShieldStrengthMax.Get());

		for (int i = 1; i <= 127; i++)
		{
			if (this->m_damage[i] > 0.0f)
			{
				if (this->m_damage[i] >= mostDamage)
				{
					if (this->m_damage[i] == mostDamage)
						mostDamager.Add(i);
					else
					{
						mostDamager.Clear();
						mostDamager.Add(i);
					}
					mostDamage = this->m_damage[i];
				}

				gzPlayer *vData = gzPlayerManager::Find(i);
				if (vData)
					vData->GetVeteran()->AddPoints(damagePoints / this->m_totalDamage * this->m_damage[i]);
			}
		}

		if (mostDamager.Count() == 0)
			return;

		// Doesn't give most damage vet-points if none is in vehicle and vehicle has seat(s)
		if (this->m_vehicle->SeatsList.Length() == 0 || (this->m_vehicle->SeatsList.Length() > 0 && this->m_vehicle->Occupant_Count > 0))
		{
			for (unsigned int i = 0; i < mostDamager.Count(); i++)
			{
				gzPlayer *gzData = gzPlayerManager::Find(mostDamager[i]);
				if (gzData)
					gzData->GetVeteran()->AddPoints(this->m_data->Points->destroy / mostDamager.Count());
			}
		}
	}
}
void gzObserverVeteranVehicle::Timer_Expired(nc_ScriptableGameObj *obj, int number)
{
	if (number == 2)
	{
		for (int i = 1; i <= 127; i++)
		{
			if (this->m_repair[i] != 0.0f)
			{
				gzPlayer *gzData = gzPlayerManager::Find(i);
				if (gzData)
					gzData->GetVeteran()->AddPoints(this->m_repair[i] * this->m_data->Points->repair);
				this->m_repair[i] = 0.0f;
			}
		}
		obj->Start_Observer_Timer(this->GetID(), 60.0f, 2);
	}
	else if (number == 10)
	{
		if (this->m_vehicle->Owner.Reference)
		{
			this->m_purchaser = gzPlayerManager::Find(this->m_vehicle->Owner.Reference->obj->As_SoldierGameObj()->Player);
			if (this->m_purchaser)
			{
				nc_VehicleGameObjDef *def					= gzStatic_Cast(nc_VehicleGameObjDef, obj->definition);
				float newArmor								= (gzStatic_Cast(nc_VehicleGameObjDef, obj->definition)->DefenseDef.ShieldStrengthMax.Get() / 100.0f * (100.0f + this->m_purchaser->GetVeteran()->GetCurrentLevel()->AddArmor));
				float newHealth								= (gzStatic_Cast(nc_VehicleGameObjDef, obj->definition)->DefenseDef.HealthMax.Get() / 100.0f * (100.0f + this->m_purchaser->GetVeteran()->GetCurrentLevel()->AddHealth));
				this->m_vehicle->Defense.ShieldStrength		= newArmor;
				this->m_vehicle->Defense.ShieldStrengthMax	= newArmor;
				this->m_vehicle->Defense.Health				= newHealth;
				this->m_vehicle->Defense.HealthMax			= newHealth;
				this->m_vehicle->Set_Object_Dirty_Bit(nc_DB_OCCASIONAL, true);
				obj->Start_Observer_Timer(this->GetID(), 1.0f, 11);
			}
		}
	}
	else if (number == 11)
	{
		if (this->m_vehicle->SeatsList[0])
		{
			gzPlayer *gzData = gzPlayerManager::Find(this->m_vehicle->SeatsList[0]->Player);
			if (gzData)
			{
				if (gzData->GetVeteran()->GetCurrentLevel()->VehicleHealthRegeneration > 0.0f)
					gzCommands->Apply_Damage(obj, -gzData->GetVeteran()->GetCurrentLevel()->VehicleHealthRegeneration, "Repair", NULL);
			}
		}
		obj->Start_Observer_Timer(this->GetID(), 1.0f, number);
	}
}
nc_ScriptRegistrant<gzObserverVeteranVehicle> gzObserverVeteranVehicle_Reg("gzObserverVeteranVehicle", "");


/************/
/* Commands */
/************/
class gzChat_VetInfo : public gzChatCommandBase {
	void Activate(const wchar_t *msg, int type, int sender, int receiver, aWideString &ret) {
		if (!*msg)
		{
			if (!gzVeteranMgr->GetSettings()->m_enable)
				return;

			gzPlayer *gzData = gzPlayerManager::Find(sender);
			if (!gzData)
				return;
			if (gzData->GetVeteran()->GetNextLevel() == NULL)
			{
				PagePlayer(
					sender,
					"[Veteran] Your current level: %d(%s) - Vet-Points: %.2f",
					gzData->GetVeteran()->GetCurrentLevel()->Level,
					gzData->GetVeteran()->GetCurrentLevel()->Name.GetString(),
					gzData->GetVeteran()->GetPoints()
				);
			}
			else
			{
				float multipler = gzData->GetVeteran()->GetMultipler();
				PagePlayer(
					sender,
					"[Veteran] Your current level: %d(%s) - Your Vet-Points: %.2f - Required Vet-Points/score/game-time for next promotion: %.2f/%.0f/%s",
					gzData->GetVeteran()->GetCurrentLevel()->Level,
					gzData->GetVeteran()->GetCurrentLevel()->Name.GetString(),
					gzData->GetVeteran()->GetPoints(),
					((gzData->GetVeteran()->GetNextLevel()->RequiredPoints * multipler) > gzData->GetVeteran()->GetPoints()) ? ((gzData->GetVeteran()->GetNextLevel()->RequiredPoints * multipler) - gzData->GetVeteran()->GetPoints()) : 0.0f,
					((gzData->GetVeteran()->GetNextLevel()->RequiredScore * multipler) > gzData->GetPlayerData()->Score.Get()) ? ((gzData->GetVeteran()->GetNextLevel()->RequiredScore * multipler) - gzData->GetPlayerData()->Score.Get()) : 0.0f,
					SecsToAscTime((gzData->GetPlayerData()->GameTime >= gzData->GetVeteran()->GetNextLevel()->RequiredGameTime) ? 0 : (unsigned int)(gzData->GetVeteran()->GetNextLevel()->RequiredGameTime - (unsigned long)gzData->GetPlayerData()->GameTime)).GetString()
				);
			}
		}
		else
		{
			// Nickname unique check
			int nickCount = gzPlayerManager::FindByPartNameCount(msg);
			if (nickCount >= 2 && !gzPlayerManager::Find(msg))
			{
				PagePlayer(sender, "Found %d result(s) for \"%s\". Please specify a unique string for the nickname.", nickCount, msg);
				return;
			}

			gzPlayer *gzData = gzPlayerManager::FindByPartName(msg);
			if (!gzData)
				PagePlayer(sender, "%ls not found.", msg);
			else
			{
				PagePlayer(
					sender,
					"[Veteran]: Current level for %ls: %d(%s) - Vet-Points: %.2f",
					gzData->GetPlayerName().GetString(),
					gzData->GetVeteran()->GetCurrentLevel()->Level,
					gzData->GetVeteran()->GetCurrentLevel()->Name.GetString(),
					gzData->GetVeteran()->GetPoints()
				);
			}
		}
	}
};
