/*GuildWars˛ EverQuest Modification by Image (Dominic Micale)
**GuildWars˛ project began on September 24th, 2003
**Original ideas from GuildWars project which was released Feb of 2003
*/
#include "GuildWars.h"
#include "zone.h"
#include <math.h>
#include "worldserver.h"
#include "entity.h"

extern Zone* zone;
extern WorldServer worldserver;
extern EntityList entity_list;

GuildLocationList location_list;
GuildWars guildwars;

void GuildWars::Construct()
{
SetZoneStatus(1);
database.UpdateZoneOnlineStatus(GetCurrentZone(),GetZoneStatus());
database.GetZSStats(GetCurrentZone());
}

void GuildWars::Deconstruct()
{
SetCurrentUsers(0);
SetZoneStatus(0);
database.UpdateZoneOnlineStatus(GetCurrentZone(),GetZoneStatus());
database.UpdateZoneServerStats(GetCurrentZone(),GetCurrentUsers(),GetMaxUsers(),GetCampsCount(),GetTownsCount(),GetCitiesCount());
//Do final update of database, set status to be down, set current users = 0.
}

void GuildWars::Update()
{
database.UpdateZoneServerStats(GetCurrentZone(),GetCurrentUsers(),GetMaxUsers(),GetCampsCount(),GetTownsCount(),GetCitiesCount());
Construct();
}

sint32 GuildWars::CalculateFactionDecrease(Mob* killed,Mob* killedby)
{
if(!killed || !killedby)
return 0;

sint32 calc = 0;
calc += killed->GetLevel()*2;
if(killed->IsClient())
{
if(killed->CastToClient()->GuildRank() == 0)
calc += killed->GetLevel()*2;
else if(killed->CastToClient()->GuildRank() < 5)
calc += killed->GetLevel();
else
calc += killed->GetLevel()/2;
}
calc *= -1;

return calc;
}

sint32 GuildWars::CalculateFactionIncrease(Mob* killed,Mob* killedby)
{
if(!killed || !killedby)
return 0;

sint32 calc = 0;
calc += killed->GetLevel()/4;
if(killed->IsClient())
{
if(killed->CastToClient()->GuildRank() == 0)
calc += killed->GetLevel()/2;
else if(killed->CastToClient()->GuildRank() < 5)
calc += killed->GetLevel()/4;
else
calc += killed->GetLevel()/6;
}

return calc;
}

char* GuildWars::GetRandomName()
{
	char	guardfirstnames[40][64] = { "Nolan", "Hue", "Marcel", "Marlin", "Edyth", "Jackson", "Hal", "Alvaro", "Jeffry", "Marvis", "Elwood", "Darius", "Franklyn", "Jerrell", "Joseph", "Garret", "Thu", "Leigh", "Del", "Wes", "Jeramy", "Waylon", "Gayle", "Colton", "Willian", "Mose", "Frerrann", "Edenad", "Drilawan", "Chardo", "Woadus", "Cilakath", "Gwerin", "Abeagan" };
	char	guardlastnames[40][64] = { "Zarlengo", "Humbird", "Schwieterman", "Dekay", "Blasingim", "Deuser", "Dalphonse", "Briston", "Lindel", "Chay", "Singson", "Cotterman", "Lisboa", "McCloud", "Moure", "Cossin", "Halprin", "Kachmar", "Primas", "Rieffenberg", "Kenning", "Donerson", "Consuegra", "Hennesy", "Ahles", "Maltez", "Hilderman", "Kermes", "Mcleskey", "Malinak" };

int8 first = MakeRandomInt(0,33);
int8 last = MakeRandomInt(0,30);
#ifdef GWDEBUG
printf("First: %i Last: %i\n",first,last);
#endif
char name[64];
snprintf(name,64,"%s %s",guardfirstnames[first],guardlastnames[last]);
#ifdef GWDEBUG
printf("RandomName: %s\n",name);
#endif
return name;
}

sint32 GuildWars::GetCurrentGuildFaction(Mob* mobone,Mob* mobtwo)
{

	int32 targetguildid,otherguildid;

	if(!mobone || !mobtwo)
		return GW_FACTIONNOTEXIST;
	if(mobone->GetOwner() != 0)
		mobone = mobone->GetOwner();
	if(mobtwo->GetOwner() != 0)
		mobtwo = mobtwo->GetOwner();

	if(mobone->IsClient())
		targetguildid = mobone->CastToClient()->GuildDBID();
	else
		targetguildid = mobone->CastToNPC()->GetGuildID();

	if(mobtwo->IsClient())
		otherguildid = mobtwo->CastToClient()->GuildDBID();
	else
		otherguildid = mobtwo->CastToNPC()->GetGuildID();

	if(targetguildid == 0 || otherguildid == 0) // Somethings wrong, make sure they can't attack each other
		return GW_FACTIONNOTEXIST;
	sint32 factionreturn = database.GetGuildFaction(targetguildid,otherguildid);

	return factionreturn;
}

void GuildWars::UpdateGuildWarsPoints(int32 charid,sint32 points)
{
sint32	guildcolumns[6] = { 0,0,0,0,0,0 };
sint32 purchase[4] = { 0,0,0,0 };
if(points < 0)
purchase[2] = points*-1;
else
purchase[1] = points;

database.UpdatePointsTable(charid,0,points,guildcolumns,purchase);
}

void GuildWars::DeathPointUpdate(int32 attackerid,int32 defenderid)
{
				sint32	guildcolumns[6] = { 0,0,0,0,0,0 };
				sint32 playattacker[4] = { 1,0,0,0 }; //Adds 1 kill to their kills
				database.UpdatePointsTable(attackerid,0,0,guildcolumns,playattacker);
				sint32 playdefender[4] = { 0,0,0,1 }; // Adds 1 death to their deaths
				database.UpdatePointsTable(defenderid,0,0,guildcolumns,playdefender);
}

bool GuildWars::PurchaseNPC(Client* clientloc,int32 npcid)
{
#ifdef GWDEBUG
					printf("PurchaseNPC %i\n",npcid);
#endif
	//First, find the guildlocation that the client is currently located in (If one is even in range), then assign the NPC to it bleh bleh
	if(clientloc->GuildDBID() == 0 || clientloc->GuildRank() > 1)
	{
		clientloc->Message(0,"Only guild officers and leaders can create guards.");
		return false;
	}
#ifdef GWDEBUG
					printf("FindClosestLocation\n");
#endif
	GuildLocation* gl = location_list.FindClosestLocationByClient(clientloc);
	if(gl != 0)
	{
		const NPCType* tmp = 0;
#ifdef GWDEBUG
					printf("Check for Remaining GuardSpots & Permittable Guard Distance\n",npcid);
#endif
		if(gl->GuardsSpotsRemaining() == 0)
		{
			clientloc->Message(0,"This location has the max guards it can support.");
			return false;
		}
		else if(!gl->PermittableGuardDistance(clientloc))
		{
			clientloc->Message(0,"You are too close to another guard to spawn the NPC.");
			return false;
		}
		else if (tmp = database.GetNPCType(npcid)) {
#ifdef GWDEBUG
					printf("Setting up NPC\n",npcid);
#endif
			NPC* npc = new NPC(tmp, 0, clientloc->GetX(), clientloc->GetY(), clientloc->GetZ(), clientloc->GetHeading());
			int32 spawnid = database.NPCSpawnDB(1,zone->GetShortName(),npc,0);  //Insert it into the database
			database.InsertPurchasedNPC(clientloc->GuildDBID(),gl->GetLocationID(),spawnid);
			delete npc;
			return true;
		}
		else
		{
			clientloc->Message(0,"Unable to spawn guard, unknown reason.");
			return false;
		}
	}
	clientloc->Message(0,"Unable to find a location in this area which is controlled by your guild.");
	return false;
}

GuildWars::GuildWars(void)
{

}

GuildWars::~GuildWars(void)
{

}

GuildLocationList::GuildLocationList(void)
{
}

GuildLocationList::~GuildLocationList(void)
{
}

int32 GuildLocationList::GetGuildProfit(int32 guildid)
{
	LinkedListIterator<GuildLocation*> iterator(list);
	int32 profit = 0;
	iterator.Reset();
	while(iterator.MoreElements())
	{
	if(iterator.GetData()->GetGuildOwner() == guildid && iterator.GetData()->GetProfit() != 0)
	profit += iterator.GetData()->GetProfit();
	iterator.Advance();
	}
return 0;
}

float GuildLocationList::GetMeleeAttackBonus(int32 guildid)
{
#ifdef GWDEBUG
	printf("GetMeleeAttackBonus: %i\n",guildid);
#endif
	LinkedListIterator<GuildLocation*> iterator(list);

	iterator.Reset();
	float bonus = 0.0;
	while(iterator.MoreElements())
	{
		if(iterator.GetData()->GetGuildOwner() == guildid)
		{
			switch(iterator.GetData()->GetLocationType())
			{
			case CITY:
				{
					bonus += MELEECITYBONUS;
					break;
				}
			case CAMP:
				{
					bonus += MELEECAMPBONUS;
					break;
				}
			case GUILDHOUSE:
				{
					bonus += MELEEGUILDHOUSEBONUS;
					break;
				}
			case TOWER:
				{
					bonus += MELEETOWERBONUS;
					break;
				}
			}
		}
		iterator.Advance();
	}
	return bonus;
}

float GuildLocationList::GetCasterAttackBonus(int32 guildid)
{
#ifdef GWDEBUG
	printf("GetCasterAttackBonus: %i\n",guildid);
#endif
	LinkedListIterator<GuildLocation*> iterator(list);

	iterator.Reset();
	float bonus = 0.0;
	while(iterator.MoreElements())
	{
		if(iterator.GetData()->GetGuildOwner() == guildid)
		{
			switch(iterator.GetData()->GetLocationType())
			{
			case CITY:
				{
					bonus += CASTERCITYBONUS;
					break;
				}
			case CAMP:
				{
					bonus += CASTERCAMPBONUS;
					break;
				}
			case GUILDHOUSE:
				{
					bonus += CASTERGUILDHOUSEBONUS;
					break;
				}
			case TOWER:
				{
					bonus += CASTERTOWERBONUS;
					break;
				}
			}
		}
		iterator.Advance();
	}
	return bonus;
}

void GuildLocationList::ProcessLocations()
{
	LinkedListIterator<GuildLocation*> iterator(list);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		if(!iterator.GetData()->Process())
			iterator.RemoveCurrent();
		iterator.Advance();
	}
}

GuildLocation* GuildLocationList::FindLocationByID(int32 locid)
{
	LinkedListIterator<GuildLocation*> iterator(list);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		if (iterator.GetData()->GetLocationID() == locid)
		{
			return iterator.GetData();
		}
		iterator.Advance();
	}
	return 0;
}

GuildLocation* GuildLocationList::FindClosestLocationByClient(Client* client)
{
	LinkedListIterator<GuildLocation*> iterator(list);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		if(zone->GetZoneID() == iterator.GetData()->GetZoneID() && client->GuildDBID() == iterator.GetData()->GetGuildOwner())
		{
			int8 locationtype = iterator.GetData()->GetLocationType();
			float dist = 0;
			if(locationtype != CITY)
				dist = GetDistance(client->GetX(),client->GetY(),client->GetZ(),iterator.GetData()->GetX(),iterator.GetData()->GetY(),iterator.GetData()->GetZ());
			switch(locationtype)
			{
			case CITY:
				{
					//If you own a city in the zone, you can't have other property in that zone
					return iterator.GetData();
					break;
				}
			case CAMP:
				{
					if(dist >= OUTTERCAMPRADIUS && dist <= INNERCAMPRADIUS)
						return iterator.GetData();
					break;
				}
			case GUILDHOUSE:
				{
					if(dist >= OUTTERGUILDHOUSERADIUS && dist <= INNERGUILDHOUSERADIUS)
						return iterator.GetData();
					break;
				}
			case TOWER:
				{
					if(dist >= OUTTERTOWERRADIUS && dist <= INNERTOWERRADIUS)
						return iterator.GetData();
					break;
				}
			}
		}
		iterator.Advance();
	}
	return 0;
}

float GuildLocationList::GetDistance(float x1,float y1, float z1, float x2, float y2, float z2)
{
#ifdef GWDEBUG
	printf("GetDistance: %f %f %f / %f %f %f\n",x1,y1,z1,x2,y2,z2);
#endif
	double xDiff = x2 - x1;
	double yDiff = y2 - y1;
	double zDiff = z2 - z1;

	return sqrt( (xDiff * xDiff) 
	           + (yDiff * yDiff) 
		       + (zDiff * zDiff) );
}

void GuildLocationList::AddLocation(GuildLocation* gl)
{
	list.Insert(gl);
}

bool GuildLocationList::RemoveLocation(int32 locid)
{
	if (locid == 0)
		return 0;
	LinkedListIterator<GuildLocation*> iterator(list);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		if (iterator.GetData()->GetLocationID() == locid)
		{
			iterator.RemoveCurrent();
			return true;
		}
		iterator.Advance();
	}
	return false;
}

void GuildLocationList::ClearLocations()
{
	LinkedListIterator<GuildLocation*> iterator(list);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		iterator.RemoveCurrent();
		iterator.Advance();
	}
}

GuildLocation::GuildLocation(int32 locid, float xcoord, float ycoord, float zcoord,int32 zone,int8 type,int32 guild)
{
	location_id = locid;
	x = xcoord;
	y = ycoord;
	z = zcoord;
	zoneid = zone;
	locationtype = type;
	guildid = guild;
	continueprocess = true;
	takeover_delay = new Timer(1800000); //1800000
	takeover_delay->Start();
	takeover_check = new Timer(300000);
	takeover_check->Start();
	profit_timer = new Timer(1800000);
	profit_timer->Start();
	takeoverprocess = false;
}

GuildLocation::~GuildLocation()
{
	ResetNPCs();
	ResetObjects();
	delete takeover_delay;
	delete takeover_check;
	delete profit_timer;
}

bool GuildLocation::Process()
{
	if(!continueprocess)
		return false;

	if(GetZoneID() != zone->GetZoneID())
		return true;

	if(takeover_delay->Check())
	{
#ifdef GWDEBUG
		printf("TakeOverTimer disabled.\n");
#endif
		takeover_delay->Disable();
		profit_timer->Start();
	}
	else if(takeover_delay->Enabled())
		return true;

	if(profit_timer->Check())
	{
		switch(GetLocationType())
		{
		case CITY:
			{
				profit += CITYPROFIT;
				break;
			}
		case CAMP:
			{
				profit += CAMPPROFIT;
				break;
			}
		case GUILDHOUSE:
			{
				profit += GUILDHOUSEPROFIT;
				break;
			}
		case TOWER:
			{
				profit += TOWERPROFIT;
				break;
			}
		}
		database.SetLocationProfit(GetLocationID(),profit);
		//Update database with new profit information
	}
	if(takeover_check->Check() && !GuardsStillStanding())
	{
		int8 guardspots = GuardsSpotsRemaining();
		switch(GetLocationType())
		{
	case CITY:
		{
			if(MAXCITYNPCS == guardspots)
				return true;
			break;
		}
	case CAMP:
		{
			if(MAXCAMPNPCS == guardspots)
				return true;
			break;
		}
	case GUILDHOUSE:
		{
			if(MAXGUILDHOUSENPCS == guardspots)
				return true;
			break;
		}
	case TOWER:
		{
			if(MAXTOWERNPCS == guardspots)
				return true;
			break;
		}
		}
		TakeOverProcess();
	}
	return true;
}

bool GuildLocation::TakeOverProcess()
{
#ifdef GWDEBUG
	printf("TakeOverProcess() called.\n");
#endif
	//First need to see who killed the most guards, since there may be multiple guilds fighting for this city this determines who gets the land
	int8 guardskilled[512];
	memset(guardskilled,0,sizeof(guardskilled));

	LinkedListIterator<GuildNPCs*> iterator(guildnpcs);
	iterator.Reset();
	while(iterator.MoreElements())
	{
		if(!iterator.GetData()->IsAlive())
		{
			int32 guildid = iterator.GetData()->GetKilledByLastGuildID();
			if(guildid != 0)
			{
#ifdef GWDEBUG
			printf("KilledByGuildID: %i\n",guildid);
#endif
			guardskilled[guildid] += 1;
			}
		}
		iterator.Advance();
	}
	int32 highestguild = 0;
	for(int i=0;i<512;i++)
	{
		if((guardskilled[i] > 0 && highestguild == 0) || (guardskilled[i] > guardskilled[highestguild]))
			highestguild = i;
	}

	if(highestguild == 0 || guardskilled[highestguild] == 0)
		return false;
	else
		TakeOverLocation(highestguild);

	return true;
}

void GuildLocation::TakeOverLocation(int32 guildid)
{
#ifdef GWDEBUG
	printf("TakeOverLocation(%i) called. (LocationType: %i LocationID: %i)\n",guildid,GetLocationType(),GetLocationID());
#endif
	takeoverprocess = true;
	//If a city does not have any NPCs protecting it, then it can be openly claimed through a shout (this still needs to be programmed)
	//If location is a camp, tower or a guild house, it is destroyed.
	// Camps, etc. do not destroy, new method
/*	if(GetLocationType() == CAMP || GetLocationType() == GUILDHOUSE || GetLocationType() == TOWER)
	{
#ifdef GWDEBUG
	printf("LocationType() == CAMP,GUILDHOUSE,TOWER\n");
#endif
		ResetObjects();
		ResetNPCs();
		database.RemovePurchasedNPCsByLocation(GetLocationID());
		database.RemoveLocation(GetLocationID());
		//Need to send a packet to the other zone servers telling them to remove the location
		ServerPacket* outpack = new ServerPacket(ServerOP_GWLocation, sizeof(GuildWarsLocationUpdate_Struct));
		GuildWarsLocationUpdate_Struct* gwl = (GuildWarsLocationUpdate_Struct*) outpack->pBuffer;
		gwl->updatetype = 0; // Remove = 0
		gwl->target_locationid = GetLocationID();
		gwl->current_zone = zone->GetZoneID();
		worldserver.SendPacket(outpack);
		safe_delete(outpack);
		takeoverprocess = false;
		continueprocess = false;
		return;
	}*/
	//Finds a guild officer/leader of this guild in the zone and gives him the guild slips to start the new city, if it can't find a guild officer/leader, the city is not handed over
	//Need to send a packet updating the new guild id
#ifdef GWDEBUG
	printf("FindRankingOfficialCall(%i)\n",guildid);
#endif
	Client* client = 0;
	client = entity_list.FindRankingOfficial(guildid);
	if(client != 0)
	{
#ifdef GWDEBUG
		printf("Found Ranking Official: %s\n",client->GetName());
#endif
	ServerPacket* outpack = new ServerPacket(ServerOP_GWLocation, sizeof(GuildWarsLocationUpdate_Struct));
	GuildWarsLocationUpdate_Struct* gwl = (GuildWarsLocationUpdate_Struct*) outpack->pBuffer;
	gwl->updatetype = 2; // Update Guild Owner = 0
	gwl->target_locationid = GetLocationID();
	gwl->current_zone = zone->GetZoneID();
	gwl->player_guildid = guildid;
	worldserver.SendPacket(outpack);
	safe_delete(outpack);
	//City just deletes NPCs and objects through database and location,updates database with new guildid information,tells other zones to update the guildid on this location
	database.RemovePurchasedNPCsByLocation(GetLocationID());
	database.SetLocationOwner(guildid,GetLocationID());
	ResetObjects();
	ResetNPCs();
	profit = 0;
	database.SetLocationProfit(GetLocationID(),0);
	//Update database with reset profit
	//City also resets the location timer (which allows it to be taken over, set a 30 minute delay)
	takeover_delay->Start();
#ifdef GWDEBUG
	printf("TakeOverProcess() complete.\n");
#endif
		worldserver.SendChannelMessage(0, 0, 0, guildid, 0, "Your guild has successfully taken over %s",zone->GetLongName());
	}
		worldserver.SendChannelMessage(0, 0, 0, guildid, 0, "Your guild is unable to take over %s because there is no officer or leader present.",zone->GetLongName());
	takeoverprocess = false;
}

void GuildLocation::AddNPC(NPC* npc)
{
	GuildNPCs* tempref = FindGuildNPCBySpawnID(npc->respawn2->GetID()); // Check if the NPC was earlier inserted into the listing, if true update its pointer
	if(tempref != 0)
		tempref->UpdateReferences(npc);
	else
	{
		GuildNPCs* guildnpc = new GuildNPCs(npc);
		guildnpcs.Insert(guildnpc);
	}
}

int8 GuildLocation::GuardsSpotsRemaining()
{
	LinkedListIterator<GuildNPCs*> iterator(guildnpcs);

	iterator.Reset();
	int8 total = 0;
	while(iterator.MoreElements())
	{
		if(iterator.GetData())
			total++;
		iterator.Advance();
	}
	switch(GetLocationType())
	{
	case CITY:
		{
			total = MAXCITYNPCS - total;
			break;
		}
	case CAMP:
		{
			total = MAXCAMPNPCS - total;
			break;
		}
	case GUILDHOUSE:
		{
			total = MAXGUILDHOUSENPCS - total;
			break;
		}
	case TOWER:
		{
			total = MAXTOWERNPCS - total;
			break;
		}
	default:
		total = 0;
	}
	return total;
}

bool GuildLocation::PermittableGuardDistance(Client* clientloc)
{
	LinkedListIterator<GuildNPCs*> iterator(guildnpcs);

	iterator.Reset();
	int8 total = 0;
	while(iterator.MoreElements())
	{
		float dist = location_list.GetDistance(clientloc->GetX(),clientloc->GetY(),clientloc->GetZ(),iterator.GetData()->GetStartX(),iterator.GetData()->GetStartY(),iterator.GetData()->GetStartZ());
#ifdef GWDEBUG
		printf("PermittableGuardDistance: %f (%i)\n",dist,iterator.GetData()->NPCSpawnID());
#endif
		if(dist < NPCTONPCRADIUS)
			return false;
		iterator.Advance();
	}
	return true;
}

bool GuildLocation::GuardsStillStanding()
{
#ifdef GWDEBUG
printf("GuardsStillstanding()\n");
#endif
	LinkedListIterator<GuildNPCs*> iterator(guildnpcs);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		if(iterator.GetData()->IsAlive())
		{
#ifdef GWDEBUG
			printf("GuardStillStanding(): %i\n",iterator.GetData()->NPCSpawnID());
#endif
			return true;
		}
		iterator.Advance();
	}
#ifdef GWDEBUG
	printf("GuardsStillStanding(): No guards remaining.\n");
#endif
	return false;
}

GuildNPCs* GuildLocation::FindGuildNPCBySpawnID(int32 spawn_id)
{
	LinkedListIterator<GuildNPCs*> iterator(guildnpcs);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		if(iterator.GetData()->NPCSpawnID() == spawn_id)
			return iterator.GetData();
		iterator.Advance();
	}
	return 0;
}

void GuildLocation::ResetNPCs()
{
	LinkedListIterator<GuildNPCs*> iterator(guildnpcs);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		if(takeoverprocess)
		{
			zone->RemoveSpawnGroup(iterator.GetData()->NPCSpawnID());
			if(iterator.GetData()->IsAlive())
				entity_list.RemoveEntity(iterator.GetData()->ToNPC()->GetID());
		}
		iterator.RemoveCurrent();
		iterator.Advance();
	}
}

void GuildLocation::ResetObjects()
{
	LinkedListIterator<GuildObjects*> iterator(guildobjects);

	iterator.Reset();
	while(iterator.MoreElements())
	{
		iterator.RemoveCurrent();
		iterator.Advance();
	}
}

GuildNPCs::GuildNPCs(NPC* npc)
{
	npcpointer = npc;
	spawn_id = npc->respawn2->GetID();
	start_x = npc->GetX();
	start_y = npc->GetY();
	start_z = npc->GetZ();
	alive = true; // Well since we are constructing it, it must be alive!
	KilledByGuildID(0);
}

void GuildNPCs::UpdateReferences(NPC* npc) // When an NPC dies, it respawns with a new NPC construct
{
	npcpointer = npc;
	spawn_id = npc->respawn2->GetID();
	start_x = npc->GetX();
	start_y = npc->GetY();
	start_z = npc->GetZ();
	alive = true; // Since we are updating reference it must be alive.
	KilledByGuildID(0);
}