#pragma semicolon 1

#include <sourcemod>

#if defined INFO_INCLUDES
	#include "../info/constants.sp"
	#include "../info/enums.sp"
	#include "../info/variables.sp"
#endif

/**
 * Called when a buy-tower button is being shot.
 *
 * @param iTowerId		The tower id.
 * @param iButton		The button entity.
 * @param iActivator	The activator entity.
 * @noreturn
 */

stock void Tower_OnButtonBuy(TDTowerId iTowerId, int iButton, int iActivator) {
	if (!g_bEnabled) {
		return;
	}

	if (!IsDefender(iActivator)) {
		return;
	}

	if (!g_bTowerBought[view_as<int>(iTowerId)]) {
		int	 iPrice = Tower_GetPrice(iTowerId);
		char sName[MAX_NAME_LENGTH];
		Tower_GetName(iTowerId, sName, sizeof(sName));
		
		int iClients = GetRealClientCount(true);

		if (iClients <= 0) {
			iClients = 1;
		}

		iPrice /= iClients;
		
		if (!CanAfford(iPrice, true)) {
			PrintToChatAll("%s %t", PLUGIN_PREFIX, "towerBuyAttempt", GetClientNameShort(iActivator), sName, iPrice);
		}

		if (CanAfford(iPrice, false)) {
			Tower_Spawn(iTowerId);

			for (int iClient = 1; iClient <= MaxClients; iClient++) {
				if (IsDefender(iClient)) {
					AddClientMetal(iClient, -iPrice);

					Player_CAddValue(iClient, PLAYER_TOWERS_BOUGHT, 1);
				}
			}
			PrintToChatAll("%s %t", PLUGIN_PREFIX, "towerBought", GetClientNameShort(iActivator), sName);
			
			g_bTowerBought[view_as<int>(iTowerId)] = true;
			AcceptEntityInput(iButton, "Break");
		}
	}
}

/**
 * Called when a teleport-tower button is being shot.
 *
 * @param iTowerId		The tower id.
 * @param iButton		The button entity.
 * @param iActivator	The activator entity.
 * @noreturn
 */

stock void Tower_OnButtonTeleport(TDTowerId iTowerId, int iButton, int iActivator) {
	if (!g_bEnabled) {
		return;
	}

	int iTower = GetTower(iTowerId);

	if (!IsTower(iTower)) {
		return;
	}

	if (!IsTowerAttached(iTower)) {
		Tower_TeleportToSpawn(iTower);
	}
}

/**
 * Called when a tower spawned.
 *
 * @param iTower		The tower.
 * @param iTowerId		The tower id.
 * @noreturn
 */

stock void Tower_OnSpawn(int iTower, TDTowerId iTowerId) {
	if (!g_bEnabled) {
		return;
	}

	g_iUpgradeMetal[iTower] = 0;
	g_iUpgradeLevel[iTower] = 1;
	g_iLastMover[iTower]	= 0;

	SetEntProp(iTower, Prop_Data, "m_bloodColor", view_as<int>(TDBlood_None));
	SetRobotModel(iTower);

	// Change weapon
	Tower_ChangeWeapon(iTower, Tower_GetWeapon(iTowerId));

	// Teleport
	Tower_TeleportToSpawn(iTower);

	// Check for AoE
	Tower_AoeSetup(iTower, iTowerId);
}

/**
 * Check if Tower is AoE and execute timer if this is true
 *
 * @param iTower		The tower.
 * @param iTowerId		The tower id.
 * @noreturn
 */

stock void Tower_AoeSetup(int iTower, TDTowerId iTowerId) {
	if (!IsTower(iTower)) {
		return;
	}
	char sDamagetype[20];
	if (Tower_GetDamagetype(iTowerId, sDamagetype, sizeof(sDamagetype))) {
		if (StrEqual(sDamagetype, "AoE")) {
			if (hAoETimer == null)
				hAoETimer = CreateTimer(0.2, Timer_ClientNearAoETower, _, TIMER_REPEAT);
		}
	}
}

/**
 * Called when a tower touches a func_nobuild brush.
 *
 * @param iTower		The tower.
 * @param iTowerId		The tower id.
 * @noreturn
 */

stock void Tower_OnTouchNobuild(int iTower) {
	if (!g_bEnabled) {
		return;
	}
	
	Forbid(g_iLastMover[iTower], true, "%s %t", PLUGIN_PREFIX, "forbidCantPlaceTowerThere");
	Tower_TeleportToSpawn(iTower);
}

/**
 * Called when a tower is upgraded by a client.
 *
 * @param iTower		The tower.
 * @param iClient		The client upgrading.
 * @noreturn
 */

stock void Tower_OnUpgrade(int iTower, int iClient) {
	if (!g_bEnabled) {
		return;
	}

	TDTowerId iTowerId	= GetTowerId(iTower);
	int		  iMaxLevel = Tower_GetMaxLevel(iTowerId);

	if (g_iUpgradeLevel[iTower] < iMaxLevel) {
		if (!AddClientMetal(iClient, -50)) {
			return;
		}

		g_iUpgradeMetal[iTower] += 50;

		if (g_iUpgradeMetal[iTower] >= Tower_GetMetal(iTowerId)) {
			int iWeapon				= Tower_GetWeapon(iTowerId);
			g_iUpgradeMetal[iTower] = 0;
			g_iUpgradeLevel[iTower]++;

			if (iWeapon != Tower_GetWeapon(iTowerId)) {
				Tower_ChangeWeapon(iTower, Tower_GetWeapon(iTowerId));
			} else {
				Tower_SetLevelAttributes(iTower, iTowerId);
			}
			
			PrintToChatAll("%s %t", PLUGIN_PREFIX, "towerUpgradeLevel", GetClientNameShort(iTower), g_iUpgradeLevel[iTower]);
			
			float fDamageScale = Tower_GetDamageScale(iTowerId);

			PrintToChatAll("%s %t", PLUGIN_PREFIX, "towerUpgradeDamageBonus", GetClientNameShort(iTower),RoundFloat(fDamageScale * 100 - ((fDamageScale > 1.0) ? 100 : 0)));
			PrintToChatAll("%s %t", PLUGIN_PREFIX, "towerUpgradeAttackSpeed", GetClientNameShort(iTower),RoundFloat(fDamageScale * 100 - ((fDamageScale > 1.0) ? 100 : 0)));

			if (Tower_GetRotate(iTowerId)) {
				PrintToChatAll("%s %t", PLUGIN_PREFIX, "towerUpgradeCanRotate", GetClientNameShort(iTower));
			}
		}
	}

	HideAnnotation(iTower);
	HideAdvancedAnnotation(iClient, iTower);

	if (g_iUpgradeLevel[iTower] == iMaxLevel) {
		AttachAnnotation(iTower, 2.0, "%t", "towerUpgradeMaxLevel", GetClientNameShort(iTower), g_iUpgradeLevel[iTower]);
	} else {
		AttachAnnotation(iTower, 2.0, "%t", "towerUpgradeProgress", GetClientNameShort(iTower), g_iUpgradeLevel[iTower], g_iUpgradeMetal[iTower], Tower_GetMetal(iTowerId));
	}
}

/**
 * Called when a tower changes weapons.
 *
 * @param iTower					The tower.
 * @param iTowerId					The tower id.
 * @param iItemDefinitionIndex		The items index.
 * @param iSlot						The weapons slot.
 * @param iWeapon					The weapon entity.
 * @noreturn
 */

stock void Tower_OnWeaponChanged(int iTower, TDTowerId iTowerId, int iItemDefinitionIndex, int iSlot, int iWeapon) {
	SetEntPropEnt(iTower, Prop_Send, "m_hActiveWeapon", GetPlayerWeaponSlot(iTower, iSlot));

	Tower_SetLevelAttributes(iTower, iTowerId);
}

/**
 * Called when a carrier of a tower disconnects or has a timeout.
 *
 * @param iTower		The tower.
 * @param iCarrier		The carrier.
 * @noreturn
 */

stock void Tower_OnCarrierDisconnected(int iTower, int iCarrier) {
	SetEntityMoveType(iTower, MOVETYPE_WALK);
	HideAnnotation(iCarrier);

	g_iLastMover[iTower]		= iCarrier;
	g_bCarryingObject[iCarrier] = false;
	g_iAttachedTower[iCarrier]	= 0;

	Log(TDLogLevel_Debug, "%N dropped tower (%N)", iCarrier, iTower);

	Tower_TeleportToSpawn(iTower);
}

/**
 * Called when a carrier of a tower dies.
 *
 * @param iTower		The tower.
 * @param iCarrier		The carrier.
 * @noreturn
 */

stock void Tower_OnCarrierDeath(int iTower, int iCarrier) {
	SetEntityMoveType(iTower, MOVETYPE_WALK);
	HideAnnotation(iCarrier);

	g_iLastMover[iTower]		= iCarrier;
	g_bCarryingObject[iCarrier] = false;
	g_iAttachedTower[iCarrier]	= 0;

	Log(TDLogLevel_Debug, "%N dropped tower (%N)", iCarrier, iTower);

	Tower_TeleportToSpawn(iTower);
}

/**
 * Spawns a tower.
 *
 * @param iTowerId		The towers id.
 * @return				True on success, false otherwise.
 */

stock bool Tower_Spawn(TDTowerId iTowerId) {
	if (!g_bEnabled) {
		return false;
	}

	char sName[MAX_NAME_LENGTH], sClass[32];

	if (Tower_GetName(iTowerId, sName, sizeof(sName))) {
		if (Tower_GetClassString(iTowerId, sClass, sizeof(sClass))) {
			ServerCommand("bot -team blue -class %s -name %s", sClass, sName);

			Log(TDLogLevel_Info, "Tower (%s) spawned", sName);
			return true;
		}
	}

	return false;
}

/**
 * Teleports a tower to his spawn location.
 *
 * @param iTower		The tower.
 * @return				True on success, false otherwise.
 */

stock bool Tower_TeleportToSpawn(int iTower) {
	if (!g_bEnabled) {
		return false;
	}

	TDTowerId iTowerId = GetTowerId(iTower);

	if (iTowerId == TDTower_Invalid) {
		return false;
	}

	float fLocation[3], fAngles[3];

	if (Tower_GetLocation(iTowerId, fLocation)) {
		if (Tower_GetAngles(iTowerId, fAngles)) {
			TeleportEntity(iTower, fLocation, fAngles, view_as<float>({ 0.0, 0.0, 0.0 }));

			char sName[MAX_NAME_LENGTH];
			if (Tower_GetName(iTowerId, sName, sizeof(sName))) {
				Log(TDLogLevel_Debug, "Tower (%s) teleported", sName);
			}

			return true;
		}
	}

	return false;
}

/**
 * Changes a towers weapon.
 *
 * @param iTower		The tower.
 * @param iWeaponId		The weapons id.
 * @return				True on success, false otherwise.
 */

stock bool Tower_ChangeWeapon(int iTower, int iWeaponId) {
	if (!g_bEnabled) {
		return false;
	}

	if (IsTower(iTower)) {
		int iIndex = -1;
		if ((iIndex = Weapon_GetIndex(iWeaponId)) != -1) {
			int iSlot = -1;
			if ((iSlot = Weapon_GetSlot(iWeaponId)) != -1) {
				int iLevel = -1;
				if ((iLevel = Weapon_GetLevel(iWeaponId)) != -1) {
					int iQuality = -1;
					if ((iQuality = Weapon_GetQuality(iWeaponId)) != -1) {
						char sClassname[64];
						if (Weapon_GetClassname(iWeaponId, sClassname, sizeof(sClassname))) {
							bool bPreserveAttributes = Weapon_GetPreserveAttributes(iWeaponId);

							for (int i = 0; i < 3; i++) {
								if (i != iSlot) {
									TF2_RemoveWeaponSlot(iTower, i);
								}
							}

							TF2Items_GiveWeapon(iTower, iIndex, iSlot, iLevel, iQuality, bPreserveAttributes, sClassname, "");

							return true;
						}
					}
				}
			}
		}
	}

	return false;
}

/**
 * Sets a towers level attributes.
 *
 * @param iTower		The tower.
 * @param iTowerId		The towers id.
 * @noreturn
 */

stock void Tower_SetLevelAttributes(int iTower, TDTowerId iTowerId) {
	int iWeapon = GetEntPropEnt(iTower, Prop_Send, "m_hActiveWeapon");

	if (TF2Attrib_SetByDefIndex(iWeapon, 2, Tower_GetDamageScale(iTowerId))) {
		Log(TDLogLevel_Trace, "Successfully set attribute %d on %N to %f", 2, iTower, Tower_GetDamageScale(iTowerId));
	}

	if (TF2Attrib_SetByDefIndex(iTower, 6, 1.0 / Tower_GetAttackspeedScale(iTowerId))) {
		Log(TDLogLevel_Trace, "Successfully set attribute %d on %N to %f", 6, iTower, 1.0 / Tower_GetAttackspeedScale(iTowerId));
	}
}

/**
 * Attaches a tower to a client.
 *
 * @param iClient		The client.
 * @noreturn
 */

stock void Tower_Pickup(int iClient) {
	if (!g_bEnabled) {
		return;
	}

	int iTower = GetAimTarget(iClient);

	if (IsTower(iTower)) {
		if (IsTowerAttached(iTower)) {
			Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidTowerAlreadyCarried", GetClientNameShort(iTower));
			return;
		}
	
		if(g_bTowersLocked){
			Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidTowerLockedMidwave");
			return;
		}

		TF2Attrib_SetByName(iClient, "cannot pick up buildings", 1.0);

		SetEntityMoveType(iTower, MOVETYPE_NOCLIP);

		TeleportEntity(iTower, view_as<float>({ 0.0, 0.0, -8192.0 }), NULL_VECTOR, NULL_VECTOR);	// Teleport out of the map

		HideAnnotation(iTower);
		AttachAnnotation(iClient, 86400.0, "%t", "towerMoving", GetClientNameShort(iTower));
		
		g_bCarryingObject[iClient] = true;
		g_iAttachedTower[iClient] = iTower;
		

		TDTowerId iTowerId = GetTowerId(iTower);
		char sName[MAX_NAME_LENGTH];
		Tower_GetName(iTowerId, sName, sizeof(sName));
		
		CPrintToChat(iClient, "%t", "towerPickedUp", sName);
		Log(TDLogLevel_Debug, "%N picked up tower (%N)", iClient, iTower);
	}
}

/**
 * Detaches a tower from a client.
 *
 * @param iClient		The client.
 * @noreturn
 */

stock void Tower_Drop(int iClient) {
	if (!g_bEnabled) {
		return;
	}

	if (g_bInsideNobuild[iClient]) {
		Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidCantPlaceTowerOnPath");
		return;
	}
	
	if(g_bTowersLocked){
			Forbid(iClient, true, "%s %t", PLUGIN_PREFIX, "forbidCantPlaceTowerMidwave");
			return;
		}
	
	int iTower = g_iAttachedTower[iClient];
	float fLocation[3], fAngles[3];

	SetEntityMoveType(iTower, MOVETYPE_WALK);

	GetClientAbsOrigin(iClient, fLocation);
	GetClientAbsAngles(iClient, fAngles);

	fAngles[0] = Tower_GetPitch(GetTowerId(iTower));

	TeleportEntity(iTower, fLocation, fAngles, NULL_VECTOR);

	HideAnnotation(iClient);

	TF2Attrib_RemoveByName(iClient, "cannot pick up buildings");

	g_iLastMover[iTower]	   = iClient;
	g_bCarryingObject[iClient] = false;
	g_iAttachedTower[iClient]  = 0;
	Log(TDLogLevel_Debug, "%N dropped tower (%N)", iClient, iTower);
}

/**
 * Shows tower info to a client.
 *
 * @param iClient		The client.
 * @noreturn
 */

stock void Tower_ShowInfo(int iClient) {
	if (!g_bEnabled) {
		return;
	}

	int iTower = GetAimTarget(iClient);

	if (IsTower(iTower)) {
		TDTowerId iTowerId = GetTowerId(iTower);

		if (g_iUpgradeLevel[iTower] == Tower_GetMaxLevel(iTowerId)) {
			AttachAdvancedAnnotation(iClient, iTower, 4.0, "%N\nCurrent Level: %d\nMax. Level Reached", iTower, g_iUpgradeLevel[iTower]);
		} else {
			AttachAdvancedAnnotation(iClient, iTower, 4.0, "%N\nCurrent Level: %d\nUpgrade Progress: %d/%d", iTower, g_iUpgradeLevel[iTower], g_iUpgradeMetal[iTower], Tower_GetMetal(iTowerId));
		}
	}
}

/*=========================================
=            Utility Functions            =
=========================================*/

/**
 * Gets a visibility bitfield which determines if a player should see an annotation.
 *
 * @param iClient		The client who should see it.
 * @return				The bitfield.
 */

stock int GetVisibilityBitfield(int iClient) {
	if (!IsDefender(iClient)) {
		return 1;
	}

	int iBitField = 1;
	iBitField |= RoundFloat(Pow(2.0, float(iClient)));
	return iBitField;
}

/**
 * Checks if a tower is attached to a client.
 *
 * @param iTower		The tower.
 * @return				True if tower is attached, false otherwise.
 */

stock bool IsTowerAttached(int iTower) {
	for (int iClient = 1; iClient <= MaxClients; iClient++) {
		if (g_iAttachedTower[iClient] == iTower) {
			return true;
		}
	}

	return false;
}

/**
 * Gets a towers client index.
 *
 * @param iTowerId		The towers id.
 * @return				The towers client index, or -1 on failure.
 */

stock int GetTower(TDTowerId iTowerId) {
	char sName2[MAX_NAME_LENGTH];
	
	for (int iClient = 1; iClient <= MaxClients; iClient++) {
		if (IsTower(iClient)) {
			Tower_GetName(iTowerId, sName2, sizeof(sName2));
			
			if (StrEqual(GetClientNameShort(iClient), sName2)) {
				return iClient;
			}
		}
	}

	return -1;
}

/**
 * Gets the towers id.
 *
 * @param iTower		The tower.
 * @return				The towers id, or -1 on error.
 */

stock TDTowerId GetTowerId(int iTower) {
	if (IsValidClient(iTower)) {
		char sName[MAX_NAME_LENGTH]; 
		char sName2[MAX_NAME_LENGTH];
		GetClientName(iTower, sName, sizeof(sName));

		int iClient, iTowerId;
		for (iClient = 1; iClient <= MaxClients; iClient++) {
			if (IsClientInGame(iClient)) {
				for (iTowerId = 0; iTowerId < view_as<int>(TDTower_Quantity); iTowerId++) {
					Tower_GetName(view_as<TDTowerId>(iTowerId), sName2, sizeof(sName2));
					if (StrEqual(sName, sName2)) {
						return view_as<TDTowerId>(iTowerId);
					}
				}
			}
		}
	}
	return TDTower_Invalid;
}

/*======================================
=            Data Functions            =
======================================*/

/**
 * Gets the name of a tower.
 *
 * @param iTowerId 		The towers id.
 * @param sBuffer		The destination string buffer.
 * @param iMaxLength	The maximum length of the output string buffer.
 * @return				True on success, false if tower was not found.
 */

stock bool Tower_GetName(TDTowerId iTowerId, char[] sBuffer, int iMaxLength) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_name", view_as<int>(iTowerId));

	return GetTrieString(g_hMapTowers, sKey, sBuffer, iMaxLength);
}

/**
 * Gets the class of a tower.
 *
 * @param iTowerId 		The towers id.
 * @param sBuffer		The destination string buffer.
 * @param iMaxLength	The maximum length of the output string buffer.
 * @return				True on success, false if tower was not found.
 */

stock bool Tower_GetClassString(TDTowerId iTowerId, char[] sBuffer, int iMaxLength) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_class", view_as<int>(iTowerId));

	return GetTrieString(g_hMapTowers, sKey, sBuffer, iMaxLength);
}

/**
 * Gets the class of a tower.
 *
 * @param iTowerId 		The towers id.
 * @return				The towers class type, or TFClass_Unknown on error.
 */

stock TFClassType Tower_GetClass(TDTowerId iTowerId) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_class", view_as<int>(iTowerId));

	char sClass[32];
	GetTrieString(g_hMapTowers, sKey, sClass, sizeof(sClass));

	if (StrEqual(sClass, "Scout")) {
		return TFClass_Scout;
	} else if (StrEqual(sClass, "Sniper")) {
		return TFClass_Sniper;
	} else if (StrEqual(sClass, "Soldier")) {
		return TFClass_Soldier;
	} else if (StrEqual(sClass, "Demoman")) {
		return TFClass_DemoMan;
	} else if (StrEqual(sClass, "Medic")) {
		return TFClass_Medic;
	} else if (StrEqual(sClass, "Heavy")) {
		return TFClass_Heavy;
	} else if (StrEqual(sClass, "Pyro")) {
		return TFClass_Pyro;
	} else if (StrEqual(sClass, "Spy")) {
		return TFClass_Spy;
	} else if (StrEqual(sClass, "Spy")) {
		return TFClass_Engineer;
	}

	return TFClass_Unknown;
}

/**
 * Gets the price of a tower.
 *
 * @param iTowerId 		The towers id.
 * @return				The towers price, or -1 on failure.
 */

stock int Tower_GetPrice(TDTowerId iTowerId) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_price", view_as<int>(iTowerId));

	int iPrice = 0;
	if (!GetTrieValue(g_hMapTowers, sKey, iPrice)) {
		return -1;
	}

	return iPrice;
}

/**
 * Gets the location of a tower.
 *
 * @param iTowerId 		The towers id.
 * @param fLocation 	The location vector.
 * @return				True on success, false if tower was not found.
 */

stock bool Tower_GetLocation(TDTowerId iTowerId, float fLocation[3]) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_location", view_as<int>(iTowerId));

	char sLocation[64];
	if (GetTrieString(g_hMapTowers, sKey, sLocation, sizeof(sLocation))) {
		char sLocationParts[6][16];
		ExplodeString(sLocation, " ", sLocationParts, sizeof(sLocationParts), sizeof(sLocationParts[]));

		fLocation[0] = StringToFloat(sLocationParts[0]);
		fLocation[1] = StringToFloat(sLocationParts[1]);
		fLocation[2] = StringToFloat(sLocationParts[2]);

		return true;
	}

	return false;
}

/**
 * Gets the angles of a tower.
 *
 * @param iTowerId 		The towers id.
 * @param iTowerLevel 	The towers level.
 * @param fAngles 		The angles vector.
 * @return				True on success, false if tower was not found.
 */

stock bool Tower_GetAngles(TDTowerId iTowerId, float fAngles[3]) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_location", view_as<int>(iTowerId));

	char sAngles[64];
	if (GetTrieString(g_hMapTowers, sKey, sAngles, sizeof(sAngles))) {
		char sAnglesParts[6][16];
		ExplodeString(sAngles, " ", sAnglesParts, sizeof(sAnglesParts), sizeof(sAnglesParts[]));

		fAngles[0] = Tower_GetPitch(iTowerId);
		fAngles[1] = StringToFloat(sAnglesParts[4]);
		fAngles[2] = StringToFloat(sAnglesParts[5]);

		return true;
	}

	return false;
}

/**
 * Gets the metal to upgrade the tower to the next level.
 *
 * @param iTowerId 		The towers id.
 * @return				The towers upgrade metal, or -1 on failure.
 */

stock int Tower_GetMetal(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_metal", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		int iMetal = 0;
		if (!GetTrieValue(g_hMapTowers, sKey, iMetal)) {
			return -1;
		}

		return iMetal;
	}

	return -1;
}

/**
 * Gets the weapon of a tower.
 *
 * @param iTowerId 		The towers id.
 * @return				The towers weapon id, or -1 on failure.
 */

stock int Tower_GetWeapon(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_weapon", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		int iWeapon = 0;
		if (!GetTrieValue(g_hMapTowers, sKey, iWeapon)) {
			return -1;
		}
		return iWeapon;
	}

	return -1;
}

/**
 * Checks if a tower should do primary attacks.
 *
 * @param iTowerId 		The towers id.
 * @return				True if should attack, false otherwise.
 */

stock bool Tower_GetAttackPrimary(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_attack", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		char sAttackMode[64];
		if (!GetTrieString(g_hMapTowers, sKey, sAttackMode, sizeof(sAttackMode))) {
			return false;
		}

		return StrEqual(sAttackMode, "Primary");
	}

	return false;
}

/**
 * Checks if a tower should do secondary attacks.
 *
 * @param iTowerId 		The towers id.
 * @return				True if should attack, false otherwise.
 */

stock bool Tower_GetAttackSecondary(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_attack", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		char sAttackMode[64];
		if (!GetTrieString(g_hMapTowers, sKey, sAttackMode, sizeof(sAttackMode))) {
			return false;
		}

		return StrEqual(sAttackMode, "Secondary");
	}

	return false;
}

/**
 * Checks if a tower should rotate towards enemies.
 *
 * @param iTowerId 		The towers id.
 * @return				True if should rotate, false otherwise.
 */

stock bool Tower_GetRotate(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_rotate", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		int iRotate = 0;
		if (!GetTrieValue(g_hMapTowers, sKey, iRotate)) {
			return false;
		}
		if (iRotate != 0)
			return true;
	}

	return false;
}

/**
 * Gets the towers pitch.
 *
 * @param iTowerId 		The towers id.
 * @return				The pitch, or 10.0 on failure.
 */

stock float Tower_GetPitch(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_pitch", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		int iPitch = 0;
		if (!GetTrieValue(g_hMapTowers, sKey, iPitch)) {
			return 0.0;
		}

		return float(iPitch);
	}

	return 0.0;
}

/**
 * Gets the towers damage scale.
 *
 * @param iTowerId 		The towers id.
 * @return				The damage scale, or 1.0 on failure.
 */

stock float Tower_GetDamageScale(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_damage", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		float fDamage = 1.0;
		if (!GetTrieValue(g_hMapTowers, sKey, fDamage)) {
			return 1.0;
		}

		return fDamage;
	}

	return 1.0;
}

/**
 * Gets the towers attackspeed scale.
 *
 * @param iTowerId 		The towers id.
 * @return				The attackspeed scale, or 1.0 on failure.
 */

stock float Tower_GetAttackspeedScale(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_attackspeed", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		float fAttackspeed = 1.0;
		if (!GetTrieValue(g_hMapTowers, sKey, fAttackspeed)) {
			return 1.0;
		}

		return fAttackspeed;
	}

	return 1.0;
}

/**
 * Gets the towers area scale.
 *
 * @param iTowerId 		The towers id.
 * @return				The area scale, or 1.0 on failure.
 */

stock float Tower_GetAreaScale(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_%d_area", view_as<int>(iTowerId), g_iUpgradeLevel[iTower]);

		float fArea = 1.0;
		if (!GetTrieValue(g_hMapTowers, sKey, fArea)) {
			return 1.0;
		}

		return fArea;
	}

	return 1.0;
}

/**
 * Gets the towers max level.
 *
 * @param iTowerId 		The towers id.
 * @return				The max level, or 1 on failure.
 */

stock int Tower_GetMaxLevel(TDTowerId iTowerId) {
	int iTower = GetTower(iTowerId);

	if (IsTower(iTower)) {
		char sKey[32];
		Format(sKey, sizeof(sKey), "%d_1_metal", view_as<int>(iTowerId));

		int iLevel = 1, iMetal = 0;
		while (GetTrieValue(g_hMapTowers, sKey, iMetal)) {
			iLevel++;
			Format(sKey, sizeof(sKey), "%d_%d_metal", view_as<int>(iTowerId), iLevel);
		}

		return iLevel - 1;
	}

	return 1;
}

/**
 * Gets the description of a tower.
 *
 * @param iTowerId 		The towers id.
 * @param sBuffer		The destination string buffer.
 * @param iMaxLength	The maximum length of the output string buffer.
 * @return				True on success, false if tower was not found.
 */

stock bool Tower_GetDescription(TDTowerId iTowerId, char[] sBuffer, int iMaxLength) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_description", view_as<int>(iTowerId));

	return GetTrieString(g_hMapTowers, sKey, sBuffer, iMaxLength);
}

/**
 * Gets the damagetype of a tower.
 *
 * @param iTowerId 		The towers id.
 * @param sBuffer		The destination string buffer.
 * @param iMaxLength	The maximum length of the output string buffer.
 * @return				True on success, false if tower was not found.
 */

stock bool Tower_GetDamagetype(TDTowerId iTowerId, char[] sBuffer, int iMaxLength) {
	char sKey[32];
	Format(sKey, sizeof(sKey), "%d_damagetype", view_as<int>(iTowerId));

	return GetTrieString(g_hMapTowers, sKey, sBuffer, iMaxLength);
}