• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Test data needed

Status
Not open for further replies.
Level 12
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:

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

  • CtD AI testmap.w3x
    233.9 KB · Views: 57
Last edited:
Level 12
Joined
Jun 15, 2016
Messages
472
I should also say this happened while uploading an updated map to this thread:

I uploaded the map, then deleted it from the post while editing it (I forgot to put something in the map). However, when trying to open the map in my editor, it won't open claiming I don't have the right privileges (windows 10 on admin). After a short time, the map disappeared from my folder. This had already happened once with a different map, but I thought the problem was on my computer. Anyways, I will try to recreate the bug and open a proper post in the bug thread should it happen again.
 
Status
Not open for further replies.
Top