define script AIStuff
define script RecruitArmy(nTown, nAttackSize, fDelay, fDelayVariation)
define script MoveToValidPosition(oObject, fLimit)
define script AttackTest(nTown)
define script DefendTest(nTown, nDefendSize)
define script TownManagement(nTown)
define script CreateTownArray
define script RunTownAIForAll
// Debug switch
define DEBUG = 0
// Platoon sizes.
define TINY = 10
define SMALL = 20
define MEDIUM = 80
define LARGE = 240
define LEGION = 5000
// Platoons will attack enemies within this distance.
define ATTACK_DISTANCE = 70.0
// Platoons will capture enemy towns within this distance once all enemies in area have been killed.
define CAPTURE_TOWN_DISTANCE = 60.0
// The actual number of towns on the map.
define NUMBER_OF_TOWNS = 2
// 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 = 30 // 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]
run script AIStuff
run script RecruitArmy(nTown, nAttackSize, fDelay, fDelayVariation)
run script MoveToValidPosition(oObject, fLimit)
run script AttackTest(nTown)
run script DefendTest(nTown, nDefendSize)
run script TownManagement(nTown)
run script CreateTownArray
run script RunTownAIForAll
begin script AIStuff
start
run script CreateTownArray
run background script RunTownAIForAll
end script AIStuff
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
oGate2 = 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 ARTEFACT_ABODE_NUMBER_TOWN_CENTRE in oEnemyTown min built 1.0
oFriendlyTownCentre = get building ARTEFACT_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.
// 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 20000
set oStore resource RESOURCE_TYPE_WOOD to 20000
set oStore resource RESOURCE_TYPE_ORE to 20000
end if
end loop
end script TownManagement
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)
nCount++
end while
end script RunTownAIForAll