// Debug switch
define DEBUG = 0

// Platoon sizes.
define TINY = 10
define SMALL = 20
define MEDIUM = 40
define LARGE = 80
define LEGION = 120
//define FIRE_COST = 2000

// Platoons will attack enemies within this distance.
define ATTACK_DISTANCE = 125.0

// Platoons will capture enemy towns within this distance once all enemies in area have been killed.
define CAPTURE_TOWN_DISTANCE = 100.0

// The actual number of towns on the map.
//define NUMBER_OF_TOWNS = 7

// Max number of platoons in an army.
define MAX_NUMBER_OF_PLATOONS = 20
define MAX_NUMBER_OF_SIEGE_WEAPONS = 2 // Doesn't seem to work for some reason.

// The max size of the army compared to the size of the town.
define MAX_ARMY_SIZE = 50 // percent

// This array holds all town objects on the map.
global oTownArray[NUMBER_OF_TOWNS]

// MAX_NUMBER_OF_PLATOONS * NUMBER_OF_TOWNS
global oPlatoonArray[140]
global oArchersArray[140]
global oSiegeArray[140]

// Holds the mana values for each town.
global MANA[NUMBER_OF_TOWNS]

begin script RecruitArmy(nTown, nAttackSize, fDelay, fDelayVariation)
	oTown = get town with id nTown

	nArmySize = 0
	nMaxArmySize = 0
	nTownSize = 0
	nRecruitSize = 0
	nDiff = 0
	nCount = 0

	nAttacker = 0
	nUnderAttack = 0

	nTownRangeMin = nTown * MAX_NUMBER_OF_PLATOONS
	nTownRangeMax = (nTown * MAX_NUMBER_OF_PLATOONS) + MAX_NUMBER_OF_PLATOONS
start
	begin loop
		nAttacker = 0
		nUnderAttack = 0
		force while nAttacker < 7
			if town oTown is under takeover from player nAttacker
				nUnderAttack = 1
			end if
			nAttacker++
		end while

		if get oTown player != 0 and nUnderAttack == 0
			begin loop
				nArmySize = get army size in town oTown
				nTownSize = adult size of oTown
				nTownSize -= nArmySize
				nMaxArmySize = (nTownSize/100) * nAttackSize

				nDiff = nMaxArmySize - nArmySize

				if nDiff >= LEGION
					nRecruitSize = LEGION
				elsif nDiff >= LARGE
					nRecruitSize = LARGE
				elsif nDiff >= MEDIUM
					nRecruitSize = MEDIUM
				elsif nDiff >= SMALL
					nRecruitSize = SMALL
				elsif nDiff >= TINY
					nRecruitSize = TINY
				else
					nRecruitSize = 0
				end if

				until nRecruitSize > 0
			end loop

			// Recruit army of nRecruitSize.
			nCount = nTownRangeMin
			force while nCount < nTownRangeMax
				if oPlatoonArray[nCount] not exists
					oPlatoonArray[nCount] = recruit ARMY_UNIT_TYPE_MELEE_1 town oTown platoon of size nRecruitSize

					if oArchersArray[nCount] not exists
						oArchersArray[nCount] = recruit ARMY_UNIT_TYPE_RANGED_1 town oTown platoon of size (nRecruitSize / 4)
					end if
					add action PLATOON_AGENDA_ACTION_LINK_TO_ARMY_MEMBER using oPlatoonArray[nCount] to oArchersArray[nCount] action queue
					nCount = nTownRangeMax
				end if
				nCount++
			end while

			fDelay = number from (fDelay - fDelayVariation) to (fDelay + fDelayVariation)
			wait fDelay seconds
		end if
	end loop

end script RecruitArmy

begin script MoveToValidPosition(oObject, fLimit)
	lTarget = 0
	lFocus = 0
	fDistance = 1.0
start
	force while fDistance <= fLimit
		if not oObject can navigate to {oObject}
			lFocus = marker at focus position of oObject
			lTarget = marker at get target from {lFocus} to {oObject} distance fDistance angle 180
			set oObject position to {lTarget}
		else
			fDistance = fLimit
		end if
		fDistance++
	end while

	// Delete object if it's still stuck.
	if not oObject can navigate to {oObject}
		delete oObject
	end if

end script MoveToValidPosition

begin script AttackTest(nTown)
	oTown = get town with id nTown
	oTownCentre = get building ABODE_NUMBER_TOWN_CENTRE in oTown min built 1.0
	nTownPlayer = 0
	nPlatoonPlayer = 0
	nPlayer = 0

	oEnemyTown = 0
	oEnemyTownCentre = 0
	oFriendlyTown = 0
	oFriendlyTownCentre = 0
	oTownMisc = 0
	oTownCentreMisc = 0

	oPlatoon = 0
	oSiegeWeapon = 0
	oEnemyPlatoon = 0
	oNextEnemyPlatoon = 0
	oEnemySiegeWeapon = 0
	oNextEnemySiegeWeapon = 0

	oWall = 0
	oGate = 0

	fNewDistance = 0
	fLastDistance = 0
	fFriendlyNewDistance = 0
	fFriendlyLastDistance = 0

	nTownRangeMin = nTown * MAX_NUMBER_OF_PLATOONS
	nTownRangeMax = (nTown * MAX_NUMBER_OF_PLATOONS) + MAX_NUMBER_OF_PLATOONS

	nCount = 0
	nCountMisc = 0
	nSiegeCount = 0
	nAttacker = 0
	nUnderAttack = 0
	nFriendlyTownUnderAttack = 0
	nIsCapturingTown = 0

	lPos = 0
	lTarget = 0

	eEffect = 0
start
	begin loop
		//increment tribute by 1
		nTownPlayer = get oTown player
		if nTownPlayer != 0

		// Get the nearest hostile and friendly towns.
		oEnemyTown = 0
		oFriendlyTown = 0
		nCount = 0
		force while nCount < NUMBER_OF_TOWNS
			oTownMisc = oTownArray[nCount]
			if get oTownMisc player != nTownPlayer
				if oEnemyTown not exists and not town oTownMisc is under takeover from player nTownPlayer
					oEnemyTown = oTownMisc
					fLastDistance = get distance from {oTown} to {oEnemyTown}
				else
					fNewDistance = get distance from {oTown} to {oTownMisc}
					if fNewDistance < fLastDistance and not town oTownMisc is under takeover from player nTownPlayer
						fLastDistance = fNewDistance
						oEnemyTown = oTownMisc
					end if
				end if
			elsif get oTownMisc player == nTownPlayer
				if oFriendlyTown not exists
					if oTownMisc != oTown
						oFriendlyTown = oTownMisc
						fFriendlyLastDistance = get distance from {oTown} to {oFriendlyTown}
					end if
				else
					fFriendlyNewDistance = get distance from {oTown} to {oTownMisc}
					if fFriendlyNewDistance < fFriendlyLastDistance
						fFriendlyLastDistance = fFriendlyNewDistance
						oFriendlyTown = oTownMisc
					end if
				end if
			end if
			nCount++
		end while

		nCount = nTownRangeMin
		force while nCount < nTownRangeMax
			oPlatoon = oPlatoonArray[nCount]
			nPlatoonPlayer = get oPlatoon player

			// Get if the platoon is currently capturing a town. Isn't there an easier way to do this?
			nIsCapturingTown = 0
			nCountMisc = 0
			force while nCountMisc < NUMBER_OF_TOWNS
				oTownMisc = get town with id nCountMisc
				oTownCentreMisc = get building ABODE_NUMBER_TOWN_CENTRE in oTownMisc min built 1.0
				if platoon oPlatoon current action is PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oTownCentreMisc
					nIsCapturingTown = 1
					nCountMisc = NUMBER_OF_TOWNS
				end if
				nCountMisc++
			end while

			// Get town gate. Needs improvement.
			oGate = get building ABODE_NUMBER_GATEHOUSE in oTown min built 1.0
			if oGate not exists
				oGate = get building ABODE_NUMBER_GATEHOUSE_F in oTown min built 1.0
			end if

			if oEnemyTown exists and oPlatoon exists
				oEnemyTownCentre = get building ABODE_NUMBER_TOWN_CENTRE in oEnemyTown min built 1.0
				oFriendlyTownCentre = get building ABODE_NUMBER_TOWN_CENTRE in oFriendlyTown min built 1.0

				// Check if the platoon's town is under attack.
				nAttacker = 0
				nUnderAttack = 0
				force while nAttacker < 7
					if town oTown is under takeover from player nAttacker
						nUnderAttack = 1
					end if
					nAttacker++
				end while

				// Check if the nearest friendly town is under attack.
				nAttacker = 0
				nFriendlyTownUnderAttack = 0
				force while nAttacker < 7
					if town oFriendlyTown is under takeover from player nAttacker
						nFriendlyTownUnderAttack = 1
					end if
					nAttacker++
				end while

				// Get nearest hostile platoon and siege weapon.
				oEnemyPlatoon = 0
				nPlayer = 0
				force while nPlayer < 7
					if nPlayer != nPlatoonPlayer
						if oEnemyPlatoon not exists
							oEnemyPlatoon = get platoon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fLastDistance = get distance from {oPlatoon} to {oEnemyPlatoon}
						else
							oNextEnemyPlatoon = get platoon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fNewDistance = get distance from {oPlatoon} to {oNextEnemyPlatoon}
							if fNewDistance < fLastDistance
								oEnemyPlatoon = oNextEnemyPlatoon
								fLastDistance = fNewDistance
							end if
						end if
					end if
					nPlayer++
				end while
				oEnemySiegeWeapon = 0
				nPlayer = 0
				force while nPlayer < 7
					if nPlayer != nPlatoonPlayer
						if oEnemySiegeWeapon not exists
							oEnemySiegeWeapon = get siege weapon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fLastDistance = get distance from {oPlatoon} to {oEnemySiegeWeapon}
						else
							oNextEnemySiegeWeapon = get siege weapon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fNewDistance = get distance from {oPlatoon} to {oNextEnemySiegeWeapon}
							if fNewDistance < fLastDistance
								oEnemySiegeWeapon = oNextEnemySiegeWeapon
								fLastDistance = fNewDistance
							end if
						end if
					end if
					nPlayer++
				end while


				// This is a list of actions ordered after priority. First on the list gets the highest priority.

				// Clear action queue and attack oEnemyPlatoon if there's a hostile platoon within attacking distance.
				if oEnemyPlatoon exists
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_MELEE_ATTACK_PLATOON using oEnemyPlatoon
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_MELEE_ATTACK_PLATOON using oEnemyPlatoon to oPlatoon action queue
					end if
				// Clear action queue and attack oEnemySiegeWeapon if there's a hostile siege weapon within attacking distance.
				elsif oEnemySiegeWeapon exists
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_MELEE_ATTACK_MOVING_OBJECT using oEnemySiegeWeapon
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_MELEE_ATTACK_MOVING_OBJECT using oEnemySiegeWeapon to oPlatoon action queue
					end if
				// Capture hometown if it has been lost to enemy forces.
				elsif nTownPlayer != nPlatoonPlayer 
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oTownCentre
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oTownCentre to oPlatoon action queue
					end if
				// Protect hometown if it is under attack.
				elsif nUnderAttack == 1
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre to oPlatoon action queue
					end if
				// Protect nearest friendly town if it is under attack.
				elsif nFriendlyTownUnderAttack == 1
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oFriendlyTownCentre
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oFriendlyTownCentre to oPlatoon action queue
					end if
				// Clear action queue if the platoon is protecting its hometown without reason.
				elsif nTownPlayer == nPlatoonPlayer and platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre
					clear oPlatoon action queue
				// Hehehehe... This should be gooood.
				elsif nIsCapturingTown == 1
					// Do nothing! Didn't see that one coming, now did ye?
					// Note to self: Makes the platoon ignore towns that are already being captured... I think.
					
					
					/*if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_ATTACK_ENEMY_TOWN using oEnemyTown
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_ATTACK_ENEMY_TOWN using oEnemyTown to oPlatoon action queue
					end if*/
					
				// Move to nearest enemy town. Create siege weapons if target town is protected by walls.
				elsif get distance from {oPlatoon} to {oEnemyTown} > 60.0
					if oPlatoon can navigate to object oEnemyTownCentre
						if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_MOVE_TO_OBJECT using oEnemyTownCentre
							clear oPlatoon action queue
							add action PLATOON_AGENDA_ACTION_MOVE_TO_OBJECT using oEnemyTownCentre to oPlatoon action queue
						end if
					else
						if oGate exists
							add action PLATOON_AGENDA_ACTION_MOVE_TO_OBJECT using oGate to oPlatoon action queue
						else
							nSiegeCount = 0
							nCountMisc = nTownRangeMin
							force while nCountMisc < nTownRangeMax
								if oSiegeArray[nCountMisc] exists
									nSiegeCount++
								end if
								nCountMisc++
							end while

							oSiegeWeapon = oSiegeArray[nCount]
							if oSiegeWeapon not exists
								if nSiegeCount <= MAX_NUMBER_OF_SIEGE_WEAPONS
									oSiegeArray[nCount] = recruit SIEGE_BALANCE_TYPE_CATAPULT_LEVEL_1 town oTown siege weapon
								end if
							elsif oSiegeWeapon exists
								oSiegeWeapon = oSiegeArray[nCount]

								lTarget = marker at get target from {oTown} to {oEnemyTown} distance 200 angle 180
								if DEBUG == 1 eEffect = create visual effect VISUAL_EFFECT_MULTI_PICKUP at {lTarget} time -1 end if
								oWall = get wall segment nearest {lTarget} radius 400
								lTarget = marker at get target from {oSiegeWeapon} to {oWall} distance 10 angle 180

								if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oSiegeWeapon
									add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oSiegeWeapon to oPlatoon action queue
								elsif not {oWall} is in range of oSiegeWeapon
									if not siege weapon oSiegeWeapon current action is SIEGEWEAPON_AGENDA_ACTION_MOVE_TO_POS using {lTarget}
										//clear siege weapon oSiegeArray[nCount] action queue
										add action SIEGEWEAPON_AGENDA_ACTION_MOVE_TO_POS using {lTarget} to siege weapon oSiegeWeapon action queue
									end if
								elsif not siege weapon oSiegeWeapon current action is SIEGEWEAPON_AGENDA_ACTION_ATTACK_THING using oWall
									clear siege weapon oSiegeWeapon action queue
									add action SIEGEWEAPON_AGENDA_ACTION_ATTACK_THING using oWall to siege weapon oSiegeWeapon action queue
								end if
							end if
						end if
					end if
				// Attack enemy town if there is nothing else to do.
				elsif not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oEnemyTownCentre
					clear oPlatoon action queue
					add action PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oEnemyTownCentre to oPlatoon action queue
				end if
			else
				// Just go back and defend town centre if there are no enemy towns left.
				if nIsCapturingTown == 0 and not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre
					clear oPlatoon action queue
					add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre to oPlatoon action queue
				end if
			end if

			// We don't like platoons that get stuck.
			// EDIT: Lags the script and is generally more trouble than it's worth.
			// EDIT2: Activate by holding down the 'K' button.
			if key KB_K down
				if not oPlatoon can navigate to {oPlatoon}
					run script MoveToValidPosition(oPlatoon, 20.0)
				end if
			end if

			nCount++
		end while

		end if
	end loop

end script AttackTest


// Will be used for archers on walls and other town defenses.
begin script DefendTest(nTown, nDefendSize)
start
end script DefendTest

begin script TownManagement(nTown)
	oTown = get town with id nTown
	oStore = get building ABODE_NUMBER_STORAGE_PIT in oTown min built 1.0
	oGate = 0
	oPlatoon = 0
	nPlayer = 0
start
	begin loop
		nPlayer = get oTown player
		if nPlayer != 0
			oGate = get building ABODE_NUMBER_GATEHOUSE in oTown min built 1.0
			if oGate not exists
				oGate = get building ABODE_NUMBER_GATEHOUSE_F in oTown min built 1.0
			end if

			if oGate exists
				oPlatoon = get platoon of player nPlayer nearest {oGate} radius 50.0
				if oPlatoon exists
					set gate oGate open
				else
					set gate oGate close
				end if
			end if

			// Store management. Needs improvement.
			set oStore resource RESOURCE_TYPE_FOOD to 10000
			set oStore resource RESOURCE_TYPE_WOOD to 10000
			set oStore resource RESOURCE_TYPE_ORE to 10000
		end if
	end loop

end script TownManagement

// AI controlled towns can only cast miracles if they have enough mana.
begin script ManaControl(nTown)
	nMultiplier = 0
	nBase = 1
	nCount = 0
	nPlayer = 0
	nPlayerMisc = 0
	oTownMisc = 0
	oTown = get town with id nTown
start
	begin loop
		nPlayer = get oTown player
		nMultiplier = 0
		nCount = 0
		force while nCount < NUMBER_OF_TOWNS
			oTownMisc = get town with id nCount
			nPlayerMisc = get oTownMisc player
			if nPlayer == nPlayerMisc
				nMultiplier++
			end if
			nCount++
		end while

		MANA[nTown] += (nBase * nMultiplier)
		wait 0.1 seconds
	end loop

end script ManaControl

begin script MiracleAI(nTown)
	oTown = get town with id nTown
	nPlayer = 0
	fVelocity = 0.0
	fDistance = 0.0
	fLastDistance = 0.0
	fNewDistance = 0.0
	lPos = 0
	lFoc = 0
	lInfluenceRing = 0
	oMiracle = 0
	oTarget = 0
	oNextTarget = 0
	fRange = 0.0
	fRangeBonus = 50.0
	nCount = 0
	eVis = 0
	fHandAltitude = 40.0
start
	MANA[nTown] = 0
	run background script ManaControl(nTown)
	lPos = marker at {oTown}

	begin loop
		nPlayer = get oTown player
		if nPlayer != 0
			// Create Protector visual.
			if eVis not exists
				eVis = create visual effect VISUAL_EFFECT_HAND at {lPos}+{0,fHandAltitude,0} time -1
			end if

			fRange = 500.0

			// Get nearest target within range.
			oTarget = 0
			nCount = 0
			force while nCount < 7
				if nPlayer != nCount
					if oTarget not exists
						oTarget = get platoon of player nCount nearest {oTown} radius fRange
						fLastDistance = get distance from {oTown} to {oTarget}
					else
						oNextTarget = get platoon of player nCount nearest {oTown} radius fRange
						fNewDistance = get distance from {oTown} to {oNextTarget}
						if fNewDistance < fLastDistance
							oTarget = oNextTarget
							fLastDistance = fNewDistance
						end if
					end if
				end if
				nCount++
			end while

			lInfluenceRing = marker at get nearest position at oTown influence ring from {oTarget}
			fRange = get distance from {lInfluenceRing} to {oTown}
			fRange += fRangeBonus

			// Throw a fireball at the nearest hostile platoon within range.
			if oTarget exists and MANA[nTown] >= FIRE_COST and fLastDistance <= fRange
				fDistance = get distance from {oTarget} to {lPos}
				lFoc = marker at get target from {oTarget} to {lPos} distance 1.0 angle 180
				fVelocity = fDistance * 0.7

				oMiracle = make player nPlayer throw miracle MIRACLE_TYPE_FIRE from {lPos}+{0,fHandAltitude,0} heading fVelocity * ({lFoc} - {lPos})
				MANA[nTown] -= FIRE_COST

				wait 5.0 seconds
			end if
		// Stop Protector visual if the town is owned by a human player.
		elsif eVis exists
			stop visual effect eVis
		end if
	end loop

end script MiracleAI

begin script CreateTownArray
	//oTown = 0
	nCount = 0
start
	force while nCount < townCount
		oTownArray[nCount] = Town[nCount]
		nCount++
	end while
end script CreateTownArray

/*
begin script CreateTownArray
	oTown = 0
	nCount = 0
start
	// Creates a town object array.
	force while nCount < NUMBER_OF_TOWNS
		oTown = get town with id nCount
		if oTown exists
			oTownArray[nCount] = oTown
		else
			nCount = NUMBER_OF_TOWNS
		end if
		nCount++
	end while

end script CreateTownArray
*/


// This function runs all the AI scripts for all towns on the map. There's checks in each individual script to make sure human controlled towns are ignored.
begin script RunTownAIForAll
	nAttackSize = 100
	nDefendSize = 0

	fDelay = 90.0
	fDelayVariation = 30.0

	nCount = 0
start
	nAttackSize = (MAX_ARMY_SIZE/100) * nAttackSize
	nDefendSize = (MAX_ARMY_SIZE/100) * (100 - nAttackSize)

	force while nCount < NUMBER_OF_TOWNS
		run background script RecruitArmy(nCount, nAttackSize, fDelay, fDelayVariation)
		run background script AttackTest(nCount)
		run background script DefendTest(nCount, nDefendSize)
		run background script TownManagement(nCount)
		run background script MiracleAI(nCount)
		nCount++
	end while

end script RunTownAIForAll