- Joined
- Jun 15, 2016
- Messages
- 472
Sorry for the down time,
I've created an ai script which works like Illidan's ai from the last mission of the frozen throne, and it finally reached the debugging stage. If you have some spare time, play the map for a bit and see if you find anything strange in the blue player's behavior (and you will, there are plenty of problems).
Both you and the blue player (disregard teal and purple player) start with a fully built base. You also have control of Cenarius in the bottom left corner of the map.
The ai will gather units and move to capture 4 different circles of power around the map. Once the enemy hero steps on a circle of power, it will stay put for 15 seconds to channel, with an effect indicating starting and finishing the channeling. Once the hero stop channeling a siege golem will spawn near Cenarius and attack him. After the hero finishes channeling he will get back to the group and move on to the next circle of power.
Note: the enemy hero cannot start channeling when there are enemy units surrounding the circle of power. Also, the enemy will not address any attack mounted on it's base, so don't try it, it'll do nothing.
just for clarification: the enemy hero can summon siege golems indefinitely. This is not like the last TFT map where you lose if the enemy steps on all circles of power.
There are two additional functionalities to the ai:
1. there is a multiboard displaying which player has the most units surrounding each circle of power (C - for blue player, F - for red player, N - for neutral). For any circle of power controlled by red player (you), the blue player will deploy more units.
2. Once you type "DONE" (without the " ) in the game, the ai player should stop trying to capture the circles of power. Instead it will send all of it's attackers to your base, then proceed to act like a normal ai, sending every 2-3 minutes a few footmen to attack your base.
There are a few peoblems which might manifest during your playing time:
1. The enemy units can bodyblock the enemy paladin from reaching the circle of power.
2. The enemy paladin can simply not move towards the circle of power once he is in its vicinity.
3. The enemy paladin can stand in the middle of the circle of power and not start channeling.
4. The enemy paladin can stay put after finishing channeling once, continuing to channel and spawn siege golems constantly.
If any of these bugs, or any other bug occurs when you attempt to play, please post it here.
If you want to check the script for errors, or just see how it works, there you go:
Helpful comments will be repped.
Thanks in advance.
I've created an ai script which works like Illidan's ai from the last mission of the frozen throne, and it finally reached the debugging stage. If you have some spare time, play the map for a bit and see if you find anything strange in the blue player's behavior (and you will, there are plenty of problems).
Both you and the blue player (disregard teal and purple player) start with a fully built base. You also have control of Cenarius in the bottom left corner of the map.
The ai will gather units and move to capture 4 different circles of power around the map. Once the enemy hero steps on a circle of power, it will stay put for 15 seconds to channel, with an effect indicating starting and finishing the channeling. Once the hero stop channeling a siege golem will spawn near Cenarius and attack him. After the hero finishes channeling he will get back to the group and move on to the next circle of power.
Note: the enemy hero cannot start channeling when there are enemy units surrounding the circle of power. Also, the enemy will not address any attack mounted on it's base, so don't try it, it'll do nothing.
just for clarification: the enemy hero can summon siege golems indefinitely. This is not like the last TFT map where you lose if the enemy steps on all circles of power.
There are two additional functionalities to the ai:
1. there is a multiboard displaying which player has the most units surrounding each circle of power (C - for blue player, F - for red player, N - for neutral). For any circle of power controlled by red player (you), the blue player will deploy more units.
2. Once you type "DONE" (without the " ) in the game, the ai player should stop trying to capture the circles of power. Instead it will send all of it's attackers to your base, then proceed to act like a normal ai, sending every 2-3 minutes a few footmen to attack your base.
There are a few peoblems which might manifest during your playing time:
1. The enemy units can bodyblock the enemy paladin from reaching the circle of power.
2. The enemy paladin can simply not move towards the circle of power once he is in its vicinity.
3. The enemy paladin can stand in the middle of the circle of power and not start channeling.
4. The enemy paladin can stay put after finishing channeling once, continuing to channel and spawn siege golems constantly.
If any of these bugs, or any other bug occurs when you attempt to play, please post it here.
If you want to check the script for errors, or just see how it works, there you go:
JASS:
//=================================================================================================
// CONTROL POINTS CAPTURING AI - CHASING THE DAWN BONUS MISSION
// Version: 1.0 || Created by: Nowow
//=================================================================================================
// GENERAL EXPLENATION AND FUNCTION
//-------------------------------------------------------------------------------------------------
// This script is meant to act in a manner similar to the enemy AI in the last mission of TFT:
// Once the attack group has enough units, the AI captain is assigned to a target circle of power,
// called "COP" in the script itself, which he will try to capture. COPs can be captured indefinitely,
// and are not a victory requirement for this script. Furthermore, this script does not include any
// action including expansions/having its town attacked, etc...
//
// In addition to the captain actions, this AI script supports two additional functionalities:
// 1. Once prompted with the proper command, the AI script goes to the "Fuck This Shit I'm Out" stage,
// aptly called "FTSIO". In this stage the AI reverts to your boring, run on the mill campaign AI,
// with attack waves and a defense group.
// 2. The size of the attack group is customizable via the proper commands. The script keeps track of
// "COP owners" - who has the most units around a certain COP, and changes the composition of the
// attack group accordingly.
//
// For easy navigation, the script is split into several sections, with indicative IDs:
// 1. Global variable decleration & utility functions - {GVUF}
// 2. Building and unit preferences - {BADP}
// 3. Command handling section - {CMON}
// 4. Attack captain actions - {ATKC}
// 5. Main function & script start point - {MFEP}
// 6. Default campaign AI configuration - {DCAI}
//
//
// For technical info read below. Changing the code out of certain parts in {BADP} and the entire
// {DCAI} sections are NOT RECOMMENDED if you do not know what you are doing, and will probably
// break the script.
//
// This AI script was created for Elf_Lord's campaign "Chasing the Dawn".
// Questions and comments are always welcome.
//-------------------------------------------------------------------------------------------------
// TECHNICAL INFORMATION
//-------------------------------------------------------------------------------------------------
// This AI script operates using 4 threads which operate as follows:
// 1. The obligatory BuildLoop thread - goes over the build_length arrays and produces units. Runs
// from start to finish without interruption. Relevent functions are in section {BADP}.
// 2. UnitAssignmentLoop thread - starts as a modified version of CampaignBasics, once FTSIO is
// declared, the thread calls the common CampaignBasics functions and becomes a normal AI thread.
// Relevent functions are in section {BADP}.
// 3. CommandLoop thread - Processes all commands, changing global variables as necessary. This
// thread also calls the function changing attack group composition according to COP owners. Once
// FTSIO is declared this thread terminates, and commands can no longer be processed. Also note that
// the AI script needs to process 12 starting commands before it functions, make sure that the map
// accomodates these conditions or they are changed in the configurations. Relevent functions are
// in section {CMON}.
// 4. AttackCaptainLoop - Responisble for all attack captain actions, and COP capturing. functions
// are divided to states, with each state function calling the next one if certain conditions are
// met, otherwise going back to a default state. For a more detailed explanation see section {ATKC}
// which starts with state descriptions. Note this AttackCaptainLoop is not called with StartThread
// and is actually a part of the main thread. Once FTSIO is declared, the loop in AttackCaptainLoop
// is broken and the main thread continues to the normal campaign AI section {DCAI}.
//
// The unit composition of the attack group is different in this map and works by a formula using
// the following global arrays: BASE_AMOUNT, UNIT_FACTOR and a DIFFICULTY_FACTOR. each unit is
// assigned a number (BUCKET_FOOTMAN, BUCKET_PRIEST, etc...) and a function loops over these numbers
// up to BUCKET_END. For each number there is an assigned unit type ID, with its own production
// factors. the formula works as follows: the unit has a starting amount, to which a factor is added
// according to difficulty, the unit type and the number of COPs owned by the enemy, like this:
//
// TotalAmount = BASE_AMOUNT[id] + ArithmaticFloor(UNIT_FACTOR[id]*NUM_COPs*DIFFICULTY_FACTOR)
//
// For example, in hard difficulty when the enemy owns 3 COPs, the amount of riflemen produced is:
//
// BASE_AMOUNT[BUCKET_RIFLEMAN] = 2
// UNIT_FACTOR[BUCKET_RIFLEMAN] = 0.5
// HARD_FACTOR = 1.0
// NUM_COPs = 3
//
// TotalAmount = 2 + (0.5*3*1.0) = 3.5 ~=~ 3 (3.5 turns to 3 due to arithmatic floor)
//
//=================================================================================================
//-------------------------------------------------------------------------------------------------
// {GVUF} || GLOBAL DECLERATION
//-------------------------------------------------------------------------------------------------
globals
//---------------------------------------------------------------------------------------------
// Insert here crusader object data
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
// COMMAND CATCHING VARIABLES
// Generate control points' XY coordinates (Set X cooedinate prefix = 7, Set Y cooedinate prefix = 8)
constant integer SET_COPx_1 = 71
constant integer SET_COPx_2 = 72
constant integer SET_COPx_3 = 73
constant integer SET_COPx_4 = 74
constant integer SET_COPy_1 = 81
constant integer SET_COPy_2 = 82
constant integer SET_COPy_3 = 83
constant integer SET_COPy_4 = 84
// States the current owner of the control point (Owner prefix = 1, Contested prefix = 2)
constant integer FORSAKEN_CONTROL = 11
constant integer CRUSADER_CONTROL = 12
constant integer NEUTRAL_CONTROL = 13
constant integer FORSAKEN_CONTESTED = 21 // DEPRECATED
constant integer CRUSADER_CONTESTED = 22
constant integer ACTIVATE_RED_SUN = 23
// Fuck this shit I'm out: changes the strategy of the ai entirely from a COP capture based ai
// to a normal attack waves ai
constant integer FTSIO_COMMAND = 100
// Amount of starting commands
constant integer START_COMMANDS = 12
//---------------------------------------------------------------------------------------------
// CONTROL POINT VARIABLES
// Stores each control point (x,y coordinates) and its owner (forsaken owner = 1, crusader owner = 2, neutral owner = 3)
integer array COP_X
integer array COP_Y
integer array COP_OWNER
// Indicates a change in COP owners and calls for recalculation.
boolean OwnerChange = false
// Stores how many control point each player owns
integer CRUSADER_COP
integer FORSAKEN_COP
// In the rare case all COPs are guarded by forsaken units, or another fault occurs, this
// variable is supposed to be set as Current_COP to indicate choice failure
constant integer COP_ERROR = 50
// Stores the current COP chosen for attack (initial value is high not to interfere with choice
integer Current_COP = 1000
//---------------------------------------------------------------------------------------------
// RED SUN VARIABLES
// These two boolean arrays represent the state of each COP. All variables start as false.
// Whenever WM steps on a COP, the relevent COP_CONTESTED will become true. Once the channeling
// is over, whether because WM died or succeeded, COP_CONTESTED will revert to false, and if
// WM did activate the COP, the COP_ACTIVATED will briefly turn to true then revert to false.
boolean array COP_CONTESTED
boolean array COP_ACTIVATED
//---------------------------------------------------------------------------------------------
// OFFENSIVE GROUP FORMATION FORMULA
// The offensive group will be comprised of the following formula for each unit:
// BaseAmount + (CRUSADER_COP*DifficultyFactor*specificUnitFactor) + OptionalAllyBasesDeadFactor = TotalProductionAmount
constant integer BUCKET_FOOTMAN = 1
constant integer BUCKET_RIFLEMAN = 2
constant integer BUCKET_KNIGHT = 3
constant integer BUCKET_PRIEST = 4
constant integer BUCKET_SORCERESS = 5
constant integer BUCKET_BREAKER = 6
constant integer BUCKET_GRYPHON = 7
constant integer BUCKET_END = 8
// Attack and defense group arrays
integer array DEFENSE_AMOUNT
integer array BUCKET_ID
integer array BASE_AMOUNT
real array UNIT_FACTOR
// Difficulty factor
constant real EASY_FACTOR = 0.4
constant real NORMAL_FACTOR = 0.7
constant real HARD_FACTOR = 1.0
//---------------------------------------------------------------------------------------------
// MISC VARIABLES
// Captain default home for phase 2 (after FTSIO) and when the attack group has no units
constant integer Default_Home_X = -1500
constant integer Default_Home_Y = -5500
// Offset distance for the group to be around a COP and not directly on it.
constant integer OFFSET = 300
// C_OFFSET is for the group to wiggle around the COP, so the HERO_UNIT might stand of the COP
// surrounding zone at some point.
constant real C_OFFSET = 200.0
// Defines thresholds above which the attack group can choose a certain COP to attack
// NG = unguarded COP |||| YG = guarded COP
constant integer MinSizeNG = 5
constant integer MinSizeYG = 10
// Set UnitId of Sally Whitemane and crusader farm building for ease of use
constant integer HERO_UNIT = 'Hpal'
constant integer RACE_FARM = 'hhou'
// Once Whitemane decides that "Fuck this shit I'm out", this variable is set to true and
// changes the ai to a normal campaign ai. After FTSIO is declered, the ai will no longer
// receive commands, so attempting to send additional commands might overload command tray
// (max 12 commands), use with caution.
boolean FTSIO = false
player user = Player(0)
endglobals
//-------------------------------------------------------------------------------------------------
// UTILITY FUNCTIONS
//-------------------------------------------------------------------------------------------------
function Dig2Str takes integer i returns string
if i == 1 then
return "1"
elseif i == 2 then
return "2"
elseif i == 3 then
return "3"
elseif i == 4 then
return "4"
elseif i == 5 then
return "5"
elseif i == 6 then
return "6"
elseif i == 7 then
return "7"
elseif i == 8 then
return "8"
elseif i == 9 then
return "9"
else
return "0"
endif
// Courtesy of AIAndy and Tommi from http://www.hiveworkshop.com/threads/two-custom-campaign-ais-examples.8939/
endfunction
function ArithmaticFloor takes real SourceReal returns integer
local integer SourceInt = R2I(SourceReal)
local real decimal = SourceReal - I2R(SourceInt)
if decimal <= 0.5 then
return SourceInt
elseif decimal > 0.5 then
return (SourceInt + 1)
endif
endfunction
function UpdateOwners takes nothing returns nothing
local integer index = 0
set CRUSADER_COP = 0
set FORSAKEN_COP = 0
loop
exitwhen index >= 4
if COP_OWNER[index] == FORSAKEN_CONTROL then
set FORSAKEN_COP = FORSAKEN_COP + 1
elseif COP_OWNER[index] == CRUSADER_CONTROL then
set CRUSADER_COP = CRUSADER_COP + 1
elseif COP_OWNER[index] == NEUTRAL_CONTROL then
//COP is neutral, should be like that most of the time.
else
call DisplayTextToPlayer(user,0,0,"COP owner unknown. Check COP command.")
endif
set index = index + 1
endloop
endfunction
//-------------------------------------------------------------------------------------------------
// {BADP} || ALL UNIT, BUILDING AND UPGRADE PREFERENCES
//-------------------------------------------------------------------------------------------------
function BuildingPriorities takes nothing returns nothing
call SetBuildUnit( 1, TOWN_HALL )
call SetBuildUnit( 5, PEASANT )
call SetBuildUnit( 1, BARRACKS )
call SetBuildUnit( 2, HOUSE )
call SetBuildUnit( 1, HUMAN_ALTAR )
call SetBuildUnit( 8, PEASANT )
call SetBuildUnit( 4, HOUSE )
call SetBuildUnit( 1, BLACKSMITH )
call SetBuildUnit( 1, LUMBER_MILL )
call SetBuildUnit( 1, KEEP )
call SetBuildUnit( 5, HOUSE )
call SetBuildUnit( 1, SANCTUM )
call SetBuildUnit( 1, ARCANE_VAULT )
call SetBuildUnit( 7, HOUSE )
call SetBuildUnit( 2, BARRACKS )
call SetBuildUnit( 1, WORKSHOP )
call SetBuildUnit( 8, HOUSE )
call SetBuildUnit( 1, AVIARY )
call SetBuildUnit( 10, HOUSE )
call SetBuildUnit( 1, CASTLE )
call SetBuildUnit( 2, SANCTUM )
call SetBuildUnit( 12, HOUSE )
call SetBuildUnit( 2, AVIARY )
call SetBuildUnit( 14, HOUSE )
// Always build the hero unit until FTSIO
if not FTSIO then
call SetBuildUnit( 1, HERO_UNIT )
endif
endfunction
function LoadUnitPreferences takes nothing returns nothing
// Set up unit ids
set BUCKET_ID[BUCKET_FOOTMAN] = FOOTMAN
set BUCKET_ID[BUCKET_RIFLEMAN] = RIFLEMAN
set BUCKET_ID[BUCKET_KNIGHT] = KNIGHT
set BUCKET_ID[BUCKET_PRIEST] = PRIEST
set BUCKET_ID[BUCKET_SORCERESS] = SORCERESS
set BUCKET_ID[BUCKET_BREAKER] = SPELL_BREAKER
set BUCKET_ID[BUCKET_GRYPHON] = GRYPHON
// Set up unit base amounts
set BASE_AMOUNT[BUCKET_FOOTMAN] = 4
set BASE_AMOUNT[BUCKET_RIFLEMAN] = 2
set BASE_AMOUNT[BUCKET_KNIGHT] = 2
set BASE_AMOUNT[BUCKET_PRIEST] = 2
set BASE_AMOUNT[BUCKET_SORCERESS] = 1
set BASE_AMOUNT[BUCKET_BREAKER] = 0
set BASE_AMOUNT[BUCKET_GRYPHON] = 0 //Only for the defense group
// Set up specific unit factors
set UNIT_FACTOR[BUCKET_FOOTMAN] = -1.0
set UNIT_FACTOR[BUCKET_RIFLEMAN] = 0.5
set UNIT_FACTOR[BUCKET_KNIGHT] = 1.0
set UNIT_FACTOR[BUCKET_PRIEST] = 1.0
set UNIT_FACTOR[BUCKET_SORCERESS] = 0.25
set UNIT_FACTOR[BUCKET_BREAKER] = 1.0
set UNIT_FACTOR[BUCKET_GRYPHON] = 0 //Only for the defense group
// Set up defence units amounts
set DEFENSE_AMOUNT[BUCKET_FOOTMAN] = 2
set DEFENSE_AMOUNT[BUCKET_RIFLEMAN] = 1
set DEFENSE_AMOUNT[BUCKET_KNIGHT] = 2
set DEFENSE_AMOUNT[BUCKET_PRIEST] = 2
set DEFENSE_AMOUNT[BUCKET_SORCERESS] = 0
set DEFENSE_AMOUNT[BUCKET_BREAKER] = 0
set DEFENSE_AMOUNT[BUCKET_GRYPHON] = 1
endfunction
function LoadCOPActivationState takes nothing returns nothing
local integer i = 0
loop
exitwhen i > 3
set COP_CONTESTED[i] = false
set COP_ACTIVATED[i] = false
set i = i + 1
endloop
endfunction
//-------------------------------------------------------------------------------------------------
// AI CONFIGURATION
//-------------------------------------------------------------------------------------------------
function AddAttackers takes nothing returns nothing
local integer index = 0
local integer needed
local integer id
call InitAssault()
loop
exitwhen index == harass_length
set id = harass_units[index]
set needed = harass_max[index]
call AddAssault(needed,id)
set index = index + 1
endloop
endfunction
function UnitAssignment takes nothing returns nothing
// Assign peons
call ClearHarvestAI()
call HarvestGold(0,campaign_gold_peons)
call HarvestWood(0,campaign_wood_peons)
// Assign attack and defense captain units
call BuildDefenders()
call AddAttackers()
endfunction
function UnitAssignmentLoop takes nothing returns nothing
call UnitAssignment()
call Sleep(5.0)
loop
call UnitAssignment()
call Sleep(5.0)
exitwhen FTSIO
endloop
//call DisplayTextToPlayer(user,0,0,"FTSIO: Reverting to CampaignBasicsA.")
set racial_farm = RACE_FARM
call CampaignBasics()
endfunction
//-------------------------------------------------------------------------------------------------
function ConfigureAI takes nothing returns nothing
call InitAI()
call InitBuildArray()
call InitAssaultGroup()
call CreateCaptains()
call SetHeroesFlee(true)
call SetGroupsFlee(false)
call SetSlowChopping(true)
call GroupTimedLife(true)
call SetPeonsRepair(true)
call SetTargetHeroes(false)
call SetUnitsFlee(false)
call DoCampaignFarms(false)
call SetWoodPeons(3)
call SetGoldPeons(5)
call SetReplacements(3,3,3)
call SetCampaignAI()
call Sleep(0.1)
call StartBuildLoop()
call StartThread(function UnitAssignmentLoop)
endfunction
//-------------------------------------------------------------------------------------------------
// GROUP SIZE CONTROL FUNCTIONS (DEFENDERS & ATTACKERS)
//-------------------------------------------------------------------------------------------------
function SetCampaignDefender takes integer qty, integer id returns nothing
if qty > 0 then
set defense_qty[defense_length] = qty
set defense_units[defense_length] = id
set defense_length = defense_length + 1
endif
endfunction
function AttackerFormulaEx takes integer id, integer factor returns integer
local integer TotalAmount
if difficulty == EASY then
set TotalAmount = BASE_AMOUNT[id] + ArithmaticFloor(UNIT_FACTOR[id]*factor*EASY_FACTOR)
return TotalAmount
elseif difficulty == NORMAL then
set TotalAmount = BASE_AMOUNT[id] + ArithmaticFloor(UNIT_FACTOR[id]*factor*NORMAL_FACTOR)
return TotalAmount
else //difficulty == HARD
set TotalAmount = BASE_AMOUNT[id] + ArithmaticFloor(UNIT_FACTOR[id]*factor*HARD_FACTOR)
return TotalAmount
endif
endfunction
function UpdateBuildAmounts takes integer factor returns nothing
local integer index = 1
local integer AttackerAmount
local integer TotalAmount
// Deletes all building preferences
call InitBuildArray()
call InitDefenseGroup()
call InitAssaultGroup()
//Sets the building priorities again
call BuildingPriorities()
loop
exitwhen index >= BUCKET_END
set AttackerAmount = AttackerFormulaEx(index,factor)
set TotalAmount = DEFENSE_AMOUNT[index] + AttackerAmount
call SetBuildUnit(TotalAmount,BUCKET_ID[index])
call SetCampaignDefender(DEFENSE_AMOUNT[index],BUCKET_ID[index])
call SetAssaultGroup(AttackerAmount,AttackerAmount,BUCKET_ID[index])
set index = index + 1
endloop
// Always add the hero to the attack group
call SetAssaultGroup(1,1,HERO_UNIT)
//call DisplayTextToPlayer(user,0,0,"All building priorities updated.")
endfunction
//-------------------------------------------------------------------------------------------------
// {CMON} || COMMAND HANDLING THREAD
//-------------------------------------------------------------------------------------------------
function ProcessOneCommand takes nothing returns nothing
local integer cmd = GetLastCommand()
local integer data = GetLastData()
call PopLastCommand()
//---------------------------------------------------------------------------------------------
// SET X START COMMANDS
//---------------------------------------------------------------------------------------------
if cmd == SET_COPx_1 then
set COP_X[0] = data
//---------------------------------------------------------------------------------------------
elseif cmd == SET_COPx_2 then
set COP_X[1] = data
//---------------------------------------------------------------------------------------------
elseif cmd == SET_COPx_3 then
set COP_X[2] = data
//---------------------------------------------------------------------------------------------
elseif cmd == SET_COPx_4 then
set COP_X[3] = data
//---------------------------------------------------------------------------------------------
// SET Y START COMMANDS
//---------------------------------------------------------------------------------------------
elseif cmd == SET_COPy_1 then
set COP_Y[0] = data
//---------------------------------------------------------------------------------------------
elseif cmd == SET_COPy_2 then
set COP_Y[1] = data
//---------------------------------------------------------------------------------------------
elseif cmd == SET_COPy_3 then
set COP_Y[2] = data
//---------------------------------------------------------------------------------------------
elseif cmd == SET_COPy_4 then
set COP_Y[3] = data
//---------------------------------------------------------------------------------------------
// SET COP OWNER
//---------------------------------------------------------------------------------------------
elseif cmd == FORSAKEN_CONTROL then
set COP_OWNER[data] = FORSAKEN_CONTROL
set OwnerChange = true
//call DisplayTextToPlayer(user,0,0,("AI: COP "+Dig2Str(data+1)+" has been taken by the forsaken"))
//---------------------------------------------------------------------------------------------
elseif cmd == CRUSADER_CONTROL then
set COP_OWNER[data] = CRUSADER_CONTROL
set OwnerChange = true
//call DisplayTextToPlayer(user,0,0,("AI: COP "+Dig2Str(data+1)+" has been taken by the crusaders"))
//---------------------------------------------------------------------------------------------
elseif cmd == NEUTRAL_CONTROL then
set COP_OWNER[data] = NEUTRAL_CONTROL
set OwnerChange = true
//call DisplayTextToPlayer(user,0,0,("AI: COP "+Dig2Str(data+1)+" is under neutral control"))
//---------------------------------------------------------------------------------------------
// CONTESTED COP
//---------------------------------------------------------------------------------------------
elseif cmd == CRUSADER_CONTESTED then
set COP_CONTESTED[data] = true
//call DisplayTextToPlayer(user,0,0,"WM stepped on COP.")
elseif cmd == ACTIVATE_RED_SUN then
set COP_ACTIVATED[data] = true
//call DisplayTextToPlayer(user,0,0,"COP activated.")
//---------------------------------------------------------------------------------------------
// FUCK THIS SHIT I'M OUT AND WRONG INPUT
//---------------------------------------------------------------------------------------------
elseif cmd == FTSIO_COMMAND then
set FTSIO = true
//---------------------------------------------------------------------------------------------
else
call DisplayTextToPlayer(user,0,0,"Unknown command, check triggers.")
endif
endfunction
function CommandLoop takes nothing returns nothing
local integer StartCommands = 0
call DisplayTextToPlayer(user,0,0,"Running startup commands")
//---------------------------------------------------------------------------------------------
// STARTING COMMANDS
//---------------------------------------------------------------------------------------------
// This loop runs at the start of the game catching all start commands which
// assign the COP coords and neutral owners, then breaks.
loop
loop
exitwhen CommandsWaiting() > 0
call Sleep(0.5)
endloop
call ProcessOneCommand()
set StartCommands = StartCommands + 1
exitwhen StartCommands >= START_COMMANDS
endloop
call DisplayTextToPlayer(user,0,0,"All startup commands received. Starting normal command loop.")
call UpdateOwners()
call UpdateBuildAmounts(FORSAKEN_COP)
set OwnerChange = false
//---------------------------------------------------------------------------------------------
// MIDGAME COMMANDS
//---------------------------------------------------------------------------------------------
// This loop runs indefinitely to catch all commands during the game.
// If the command catching function returns true, then COP owner variables
// are updated and the group size is set accordingly
loop
loop
exitwhen CommandsWaiting() > 0
call Sleep(0.5)
endloop
call ProcessOneCommand()
exitwhen FTSIO
if OwnerChange then
set OwnerChange = false
//call DisplayTextToPlayer(user,0,0,"Owner change detected. Updating entries.")
call UpdateOwners()
call UpdateBuildAmounts(FORSAKEN_COP)
endif
endloop
//---------------------------------------------------------------------------------------------
// END OF COMMANDS HERE - when FTSIO is true there is no need for more commands
//---------------------------------------------------------------------------------------------
//call DisplayTextToPlayer(user,0,0,"FTSIO: Command thread terminating.")
call SleepForever()
endfunction
//-------------------------------------------------------------------------------------------------
// {ATKC} || ATTACK CAPTAIN CONTROL THREAD
//-------------------------------------------------------------------------------------------------
// The attack captain will have several states to go through:
//1. DEFAULT STATE: the captain will wait for units until it passes a certain threshold.
//2. SEARCH STATE: the captain choose a COP at random and move towards it (not teleport).
//3. REACHED COP STATE: as soon as the captain is in goal, set the COP with a small offset as the
// captain's home and check if captain is in combat. When captain is not in combat go to
// the next state.
// this is basically a part of "TAKEOVER STATE", and will be reflected as such in the state
// functions (first function of takeover state).
//4. TAKEOVER STATE: The captain will be set to move around the COP for a couple
// of seconds (6 for now), and wait for CRUSADER_CONTESTED command. If Whitemane can't
// start taking the COP after 6 seconds, check if current COP is under crusader control. If it is
// raise debug message of faulty COP choice, if not raise debug message of failure to capture COP
//5. CHANNEL STATE: When the order indicating Whitemane started channeling is received, the home
// of the attack captain is set to the COP itself, and the thread enters a loop of:
// (channeling time + buffer seconds), which will exit when either time runs out, Whitemane dies
// or COP is captured. If time runs out raise debug message of error while channeling.
//6. STANDBY STATE: very short state, when channeling loop breaks, the script does a redundant
// check to see if Whitemane is still alive and if target COP has been successfuly taken,
// then returns to DEFAULT STATE.
//-------------------------------------------------------------------------------------------------
// CAPTAIN MANAGEMENT UTILITY FUNCTIONS
//-------------------------------------------------------------------------------------------------
// Contains 2 functions: 1 for setting the current target COP, and one to check if the attack group
// has Whitemane and enough units to start attacking.
//-------------------------------------------------------------------------------------------------
function HeroAlive takes nothing returns boolean
return (GetUnitCountDone(HERO_UNIT) > 0)
endfunction
function GroupCanFight takes boolean IsGuarded returns boolean
if IsGuarded then
return HeroAlive() and (CaptainGroupSize() > MinSizeYG)
else // Default - COP not guarded by forsaken units
return HeroAlive() and (CaptainGroupSize() > MinSizeNG)
endif
endfunction
function CheckChoiceViability takes integer i returns boolean
if COP_OWNER[i] == FORSAKEN_CONTROL then
return GroupCanFight(true)
else // COP_OWNER[i] is neutral or ally
return GroupCanFight(false)
endif
endfunction
function SetNextTarget takes nothing returns integer
local integer CurrentChoice = 0
local integer DebugIndex = 0
local boolean ChoiceIsViable = false
loop
exitwhen ChoiceIsViable
exitwhen DebugIndex > 20
set CurrentChoice = GetRandomInt(0,3)
if CurrentChoice != Current_COP then
set ChoiceIsViable = CheckChoiceViability(CurrentChoice)
endif
set DebugIndex = DebugIndex + 1
endloop
if DebugIndex > 20 then
return COP_ERROR
else
return CurrentChoice
endif
endfunction
//-------------------------------------------------------------------------------------------------
// SPECIAL STATE FUNCTIONS
//-------------------------------------------------------------------------------------------------
// These functions are meant to handle certain cases which might alter the captain's behaviour.
//-------------------------------------------------------------------------------------------------
function SpecialState_Cleanup takes nothing returns nothing
set COP_CONTESTED[1] = false
set COP_ACTIVATED[1] = false
set COP_CONTESTED[2] = false
set COP_ACTIVATED[2] = false
set COP_CONTESTED[3] = false
set COP_ACTIVATED[3] = false
set COP_CONTESTED[4] = false
set COP_ACTIVATED[4] = false
endfunction
function SpecialState_CaptainInCombat takes nothing returns nothing
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: SpecialState_CaptainInCombat")
call SleepInCombat()
endfunction
function SpecialState_WiggleRoom takes integer i, real x, real y returns nothing
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: SpecialState_WiggleRoom")
loop
exitwhen i < 4
set i = i - 4
endloop
if i == 0 then
set x = x + C_OFFSET
elseif i == 1 then
set y = y + C_OFFSET
elseif i == 2 then
set x = x - C_OFFSET
elseif i == 3 then
set y = y - C_OFFSET
else
//call DisplayTextToPlayer(user,0,0,"Error on COP C_OFFSET choice.")
//call Sleep(M10) // Jam captain actions shut for 10 minutes till you see defuq is wrong here
endif
call SetCaptainHome(ATTACK_CAPTAIN,x,y)
call ClearCaptainTargets()
call CaptainGoHome()
loop
if CaptainInCombat(true) then
call SpecialState_CaptainInCombat()
call ClearCaptainTargets()
call CaptainGoHome()
endif
exitwhen CaptainIsHome()
exitwhen not HeroAlive()
call Sleep(2.0)
endloop
call Sleep(3.0)
endfunction
//-------------------------------------------------------------------------------------------------
// STATE FUNCTIONS
//-------------------------------------------------------------------------------------------------
// All states are explained right under -- ATTACK CAPTAIN CONTROL THREAD.
// There is no "mainframe function" in this thread, and each state function calls the one after it
// if the group has enough units.
//-------------------------------------------------------------------------------------------------
function ChannelingState takes nothing returns nothing
local integer index = 0
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: ChannelingState")
loop
exitwhen COP_ACTIVATED[Current_COP]
exitwhen not HeroAlive()
call Sleep(2.0)
endloop
if COP_ACTIVATED[Current_COP] then
//call DisplayTextToPlayer(user,0,0,"channeling completed. cleanup then default.")
call SpecialState_Cleanup()
elseif GetUnitCountDone(HERO_UNIT) == 0 then
//call DisplayTextToPlayer(user,0,0,"WM dead mid channel. cleanup then default.")
call SpecialState_Cleanup()
else
call DisplayTextToPlayer(user,0,0,"Unknown error while channeling.")
call Sleep(5)
endif
endfunction
//-------------------------------------------------------------------------------------------------
function TakeoverState_ActivateCOP takes nothing returns nothing
local real x = I2R(COP_X[Current_COP])
local real y = I2R(COP_Y[Current_COP])
local integer index = 0
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: TakeoverState_ActivateCOP")
call SetCaptainHome(ATTACK_CAPTAIN,x,y)
call Sleep(1.0)
loop
exitwhen COP_CONTESTED[Current_COP]
exitwhen GetUnitCountDone(HERO_UNIT) == 0
call Sleep(2.0)
call SpecialState_WiggleRoom(index,x,y)
set index = index + 1
endloop
if COP_CONTESTED[Current_COP] then
call SetCaptainHome(ATTACK_CAPTAIN,x,y)
call ChannelingState()
elseif not HeroAlive() then
//call DisplayTextToPlayer(user,0,0,"WM dead, can't channel")
else
call DisplayTextToPlayer(user,0,0,"Unknown error waiting for channeling.")
endif
endfunction
function TakeoverState_ReachedCOP takes real x, real y returns nothing
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: TakeoverState_ReachedCOP")
call Sleep(1.0)
//At this point WM might already be channeling. check for that.
if COP_CONTESTED[Current_COP] then
call ChannelingState()
else
//If there are units guarding the COP, make sure to dispose of them before channeling.
if CaptainInCombat(true) then
call SpecialState_CaptainInCombat()
call SetCaptainHome(ATTACK_CAPTAIN,x,y)
endif
// By now the COP should be enemy free so WM can channel without a group
if HeroAlive() then
call TakeoverState_ActivateCOP()
endif
endif
endfunction
//-------------------------------------------------------------------------------------------------
function SearchState_GoToCOP takes integer cop returns nothing
local real target_x = I2R(COP_X[cop] - OFFSET)
local real target_y = I2R(COP_Y[cop] - OFFSET)
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: SearchState_GoToCOP")
call SetCaptainHome(ATTACK_CAPTAIN,target_x,target_y)
call ClearCaptainTargets()
call CaptainGoHome()
loop
exitwhen CaptainIsHome()
exitwhen not GroupCanFight(false)
if CaptainInCombat(true) then
call SpecialState_CaptainInCombat()
endif
call Sleep(1)
endloop
if GroupCanFight(false) then
//means that captain is has reached the goal, call next state.
call TakeoverState_ReachedCOP(target_x,target_y)
endif //else the group is too small and has to retreat
endfunction
function SearchState_ChooseCOP takes nothing returns nothing
local integer ProposedCOP = SetNextTarget()
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: SearchState_ChooseCOP")
if ProposedCOP <= 3 and ProposedCOP >= 0 then
set Current_COP = ProposedCOP
call SearchState_GoToCOP(Current_COP)
elseif ProposedCOP == COP_ERROR then
//call DisplayTextToPlayer(user,0,0,"No valid COP available, going back to default in 5 seconds")
call Sleep(5.0)
else
call DisplayTextToPlayer(user,0,0,"Chosen COP unknown, check script")
endif
endfunction
//-------------------------------------------------------------------------------------------------
// CAPTAIN MANAGEMENT MAIN LOOP
//-------------------------------------------------------------------------------------------------
// This is the default state.
function AttackCaptainLoop takes nothing returns nothing
if GroupCanFight(false) then
call SearchState_ChooseCOP()
endif
call Sleep(1.0)
loop
exitwhen FTSIO
if GroupCanFight(false) then
call SearchState_ChooseCOP()
endif
//call DisplayTextToPlayer(user,0,0,"ReturnbugDebug: Back to default")
call ClearCaptainTargets()
call SetCaptainHome(ATTACK_CAPTAIN, Default_Home_X, Default_Home_Y)
call CaptainGoHome()
call Sleep(5.0)
endloop
endfunction
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
// {MFEP} || MAIN ENTRY POINT
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
function main takes nothing returns nothing
call ConfigureAI()
call SetCaptainHome(DEFENSE_CAPTAIN, -1550.0, -5600.0)
call SetCaptainHome(ATTACK_CAPTAIN, Default_Home_X, Default_Home_Y)
// Script received debug
call DisplayTextToPlayer(user,0,0,"Script received")
// AI config
call DoCampaignFarms(false)
call SetPeonsRepair(true)
call SetPeonsRepair(true)
call SetWoodPeons(3)
call SetGoldPeons(5)
call SetReplacements(3,3,3)
// Setup initial building priorities and array variables.
call BuildingPriorities()
call LoadUnitPreferences()
call LoadCOPActivationState()
// Command handling loop
call StartThread(function CommandLoop)
// Attack captain movement coordination loop
call AttackCaptainLoop()
//-------------------------------------------------------------------------------------------------
// NORMAL AI GATEWAY
//-------------------------------------------------------------------------------------------------
// This part changes the ai player's strategy once WM is gone.
// Once WM is gone, the ai is supposed to exit this loop and move along to the next phase.
//-------------------------------------------------------------------------------------------------
call InitBuildArray()
call InitDefenseGroup()
call InitAssaultGroup()
call BuildingPriorities()
//-------------------------------------------------------------------------------------------------
// {DCAI} || NORMAL AI CONFIGURABLE PART
//-------------------------------------------------------------------------------------------------
call CampaignDefenderEx(2,3,4, KNIGHT )
call CampaignDefenderEx(1,3,3, PRIEST )
call CampaignDefenderEx(4,3,2, FOOTMAN )
call CampaignDefenderEx(2,3,3, RIFLEMAN )
call CampaignDefenderEx(0,1,3, GRYPHON )
// Wave 1
call CampaignAttackerEx(1,1,1, FOOTMAN )
call SuicideOnPlayer(40,user)
// Wave 2
call CampaignAttackerEx(2,2,2, FOOTMAN )
call SuicideOnPlayer(M1,user)
loop
// Wave 3
call CampaignAttackerEx(3,3,3, FOOTMAN )
call SuicideOnPlayer(80,user)
// Wave 4
call CampaignAttackerEx(4,4,4, FOOTMAN )
call SuicideOnPlayer(100,user)
endloop
//-------------------------------------------------------------------------------------------------
// END OF SCRIPT
//-------------------------------------------------------------------------------------------------
endfunction
Helpful comments will be repped.
Thanks in advance.
Attachments
Last edited: