Starcraft 2 functions/libraries here

Started by MisterTea, February 27, 2010, 03:12:19 PM

Previous topic - Next topic

MisterTea

I was seeking for it inside mpqs so here they are...

As AI doesn't work yet and editor isn't available yet we could try playing around with these ;)
Its way to go.
I was going to check If I could add some AI using these but I thought sharing what I found would be nice so here you go ...
I know it contains a lot of useless junk too :/


BuildAI.galaxy
//--------------------------------------------------------------------------------------------------
//  AI Building Functions
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  Build Flags
//--------------------------------------------------------------------------------------------------

const int c_makeNoFlags             = 0x00000000;

const int c_avoidClosePowerOrCreep  = 0x00000001;
const int c_avoidCloseFactory       = 0x00000002;
const int c_avoidCloseDefense       = 0x00000004;
const int c_avoidCloseDropoff       = 0x00000008;

const int c_avoidFarPowerOrCreep    = 0x00000010;
const int c_avoidFarFactory         = 0x00000020;
const int c_avoidFarDefense         = 0x00000040;
const int c_avoidFarDropoff         = 0x00000080;

const int c_nearClosePowerOrCreep   = 0x00000100;
const int c_nearCloseFactory        = 0x00000200;
const int c_nearCloseDefense        = 0x00000400;
const int c_nearCloseDropoff        = 0x00000800;

const int c_nearFarPowerOrCreep     = 0x00001000;
const int c_nearFarFactory          = 0x00002000;
const int c_nearFarDefense          = 0x00004000;
const int c_nearFarDropoff          = 0x00008000;

const int c_avoidResource           = 0x00010000;
const int c_avoidChokePoint         = 0x00020000;
const int c_nearResource            = 0x00040000;
const int c_nearChokePoint          = 0x00080000;

const int c_valueSpaceMinor         = 0x00100000;
const int c_valueSpaceMajor         = 0x00200000;

const int c_canLower                = 0x08000000;
const int c_onVespeneGas            = 0x10000000;
const int c_onlyHere                = 0x20000000;
const int c_ignoreDanger            = 0x40000000;

const int c_avoidPowerOrCreep       = c_avoidClosePowerOrCreep | c_avoidFarPowerOrCreep;
const int c_avoidFactory            = c_avoidCloseFactory | c_avoidFarFactory;
const int c_avoidDefense            = c_avoidCloseDefense | c_avoidFarDefense;
const int c_avoidDropoff            = c_avoidCloseDropoff | c_avoidFarDropoff;

const int c_nearPowerOrCreep        = c_nearClosePowerOrCreep | c_nearFarPowerOrCreep;
const int c_nearFactory             = c_nearCloseFactory | c_nearFarFactory;
const int c_nearDefense             = c_nearCloseDefense | c_nearFarDefense;
const int c_nearDropoff             = c_nearCloseDropoff | c_nearFarDropoff;

const int c_makeStandard            = c_makeNoFlags;
const int c_makeCenter              = c_nearResource;
const int c_makeCreep               = c_valueSpaceMajor | c_avoidPowerOrCreep;
const int c_makePower               = c_valueSpaceMajor | c_avoidPowerOrCreep;
const int c_makeExpanPower          = c_nearResource | c_nearDropoff | c_avoidClosePowerOrCreep;
const int c_makeDarkPower           = c_nearResource | c_nearDropoff;
const int c_makeCollector           = c_onVespeneGas | c_nearDropoff;
const int c_makeDefense             = c_avoidCloseDefense | c_nearResource | c_nearFarDropoff | c_nearFarFactory | c_nearFarPowerOrCreep | c_nearChokePoint;
const int c_makeResourceDefense     = c_avoidCloseDefense | c_nearResource;
const int c_makeLowerable           = c_makeStandard | c_canLower;

const int c_makeDefault = -1;

//--------------------------------------------------------------------------------------------------
//  AI Get Default Build Flags Functions
//--------------------------------------------------------------------------------------------------
int AIGetDefaultBuildFlags (int player, string objType) {

    // special protoss units
    if (objType == c_PB_Nexus) {
        return c_makeCenter;
    }
    if (objType == c_PB_Pylon) {
        return c_makePower;
    }
    if (objType == c_PB_Obelisk) {
        return c_makeDarkPower;
    }
    if (objType == c_PB_PhotonCannon) {
        return c_makeDefense;
    }
    if (objType == c_PB_Assimilator) {
        return c_makeCollector;
    }
   
    // special terran units
    if (objType == c_TB_CommandCenter) {
        return c_makeCenter;
    }
    if (objType == c_TB_Bunker) {
        return c_makeDefense;
    }
    if (objType == c_TB_MissileTurret) {
        return c_makeResourceDefense;
    }
    if (objType == c_TB_SensorTower) {
        return c_nearChokePoint;
    }
    if (objType == c_TB_PlanetaryFortress) {
        return c_makeDefense;
    }
    if (objType == c_TB_Refinery) {
        return c_makeCollector;
    }
    if (objType == c_TB_SupplyDepot) {
        return c_makeLowerable;
    }
   
    // special zerg units
    if (objType == c_ZB_Hatchery) {
        return c_makeCenter;
    }
    if (objType == c_ZB_Lair) {
        return c_makeCenter;
    }
    if (objType == c_ZB_Hive) {
        return c_makeCenter;
    }
    if (objType == c_ZB_CreepTumor) {
        return c_makeCreep;
    }
    if (objType == c_ZB_SpineCrawler) {
        return c_makeDefense;
    }
    if (objType == c_ZB_SporeCrawler) {
        return c_makeResourceDefense;
    }
    if (objType == c_ZB_Extractor) {
        return c_makeCollector;
    }
   
    // default
    return c_makeStandard;
}


Computer.galaxy
include "TriggerLibs/MeleeAI"
include "TriggerLibs/Terran"
include "TriggerLibs/Protoss"
include "TriggerLibs/Zerg"

MisterTea


MeleeAI.galaxy
//==================================================================================================
//  Melee AI Functions
//--------------------------------------------------------------------------------------------------
// rough rating target:
//   insane    2300
//   very hard 2000
//   hard      1700
//   medium    1400
//   easy      1100
//   very easy  800
//==================================================================================================

const bool DEBUG                = true;
const bool DEBUG_MAIN_STATE     = false;
const bool DEBUG_ATTACK_STATE   = false;
const bool DEBUG_WAVE           = false;

const int e_flagsScouting       = 1;
const int e_flagsDiversion      = 2;
const int e_flagsTimeout        = 3;
const int e_flagsRunScared      = 4;
const int e_flagsDetect         = 5;
const int e_flagsZergStock      = 6;
const int e_flagsExpanded       = 7;
const int e_flagsLateScout      = 8;
const int e_flagsClearObs       = 9;
const int e_flagsEarlyDefScout  = 10;

// limit of 16 states in CCaptain
const int e_mainState       = 1;
const int e_mainSubState    = 2;
const int e_attackState     = 3;
const int e_divert1State    = 4;
const int e_divert2State    = 5;
const int e_openRollState   = 6;
const int e_middleRollState = 7;
const int e_lateRollState   = 8;

// main states
const int e_mainState_Disabled      = -1;
const int e_mainState_Off           =  0;
const int e_mainState_Init          =  1;
const int e_mainState_OpenGnd0      =  2;
const int e_mainState_OpenGnd1      =  3;
const int e_mainState_OpenGnd2      =  4;
const int e_mainState_OpenGnd3      =  5;
const int e_mainState_OpenGnd4      =  6;
const int e_mainState_OpenGnd5      =  7;
const int e_mainState_OpenAir0      =  8;
const int e_mainState_OpenAir1      =  9;
const int e_mainState_OpenAir2      = 10;
const int e_mainState_OpenAir3      = 11;
const int e_mainState_OpenAir4      = 12;
const int e_mainState_OpenAir5      = 13;
const int e_mainState_Mid0          = 14;
const int e_mainState_Mid1          = 15;
const int e_mainState_Mid2          = 16;
const int e_mainState_Mid3          = 17;
const int e_mainState_Mid4          = 18;
const int e_mainState_Mid5          = 19;
const int e_mainState_Late0         = 20;
const int e_mainState_Late1         = 21;
const int e_mainState_Late2         = 22;
const int e_mainState_Late3         = 23;
const int e_mainState_Late4         = 24;
const int e_mainState_Late5         = 25;

// main sub states
const int e_mainSubState_Unset      = 1;
const int e_mainSubState_GndA       = 2;
const int e_mainSubState_GndB       = 3;
const int e_mainSubState_AirA       = 4;
const int e_mainSubState_AirB       = 5;

// attack states
const int e_attackState_Wait        = 1;
const int e_attackState_Idle        = 2;
const int e_attackState_Attack      = 3;
const int e_attackState_DropAttack  = 4;
const int e_attackState_Scared      = 5;
const int e_attackState_Retreat     = 6;
const int e_attackState_DropRetreat = 7;
const int e_attackState_InRetreat   = 8;

const fixed c_evalDefendRange   = 20;
const fixed c_evalRange         = 10;
const fixed c_evalHarassRange   = 8;

// drop attack variables
const fixed c_dropCheckRollFreq = 60.0;
const fixed c_dropCheckAttackFreq = 240.0;
const int c_dropAttackChance = 75;

//--------------------------------------------------------------------------------------------------
//  DebugAI
//--------------------------------------------------------------------------------------------------
void DebugAI (string s) {
    if (DEBUG) {
        TriggerDebugOutput(1, StringToText(s), true);
    }
}

//--------------------------------------------------------------------------------------------------
void DebugAIPlayer (int player, string s) {
    if (DEBUG) {
        TriggerDebugOutput(
            1,
            StringToText("[") +
            PlayerColorName(PlayerGetColorIndex(player, false)) +
            StringToText("] ") +
            StringToText(s),
            true
        );
    }
}

//--------------------------------------------------------------------------------------------------
void DebugAIPlayerWave (int player, string s) {
    if (DEBUG_WAVE) {
        DebugAIPlayer(player, s);
    }
}

//--------------------------------------------------------------------------------------------------
void DebugVarInt (string s, int value) {
    DebugAI(s + "= " + IntToString(value));
}

//--------------------------------------------------------------------------------------------------
void DebugVarString (string s, string value) {
    DebugAI(s + "= " + value);
}

//--------------------------------------------------------------------------------------------------
void DebugVarInt2 (string s1, int value1, string s2, int value2) {
    DebugAI(s1 + "= " + IntToString(value1) + ", " + s2 + "= " + IntToString(value2));
}

//--------------------------------------------------------------------------------------------------
void DebugVarInt3 (string s1, int value1, string s2, int value2, string s3, int value3) {
    DebugAI(s1 + "= " + IntToString(value1) + ", "
          + s2 + "= " + IntToString(value2) + ", "
          + s3 + "= " + IntToString(value3)
    );
}

//--------------------------------------------------------------------------------------------------
void DebugVarBool (string s, bool value) {
    if (value) {
        DebugAI(s + "= true");
    }
    else {
        DebugAI(s + "= false");
    }
}

//--------------------------------------------------------------------------------------------------
string AttackStateName (int state) {
    if (state == e_attackState_Wait)           {  return "Wait";           }
    if (state == e_attackState_Attack)         {  return "Attack";         }
    if (state == e_attackState_DropAttack)     {  return "DropAttack";     }
    if (state == e_attackState_Idle)           {  return "Idle";           }
    if (state == e_attackState_Scared)         {  return "Scared";         }
    if (state == e_attackState_Retreat)        {  return "Retreat";        }
    if (state == e_attackState_DropRetreat)    {  return "DropRetreat";    }
    if (state == e_attackState_InRetreat)      {  return "InRetreat";      }
    return "?" + IntToString(state) + "?";
}

//--------------------------------------------------------------------------------------------------
string WaveStateName (int state) {
    if (state == c_waveStateIdle)           { return "Idle";        }
    if (state == c_waveStateSuicide)        { return "Suicide";     }
    if (state == c_waveStateMerge)          { return "Merge";       }
    if (state == c_waveStateAttack)         { return "Attack";      }
    if (state == c_waveStateDefend)         { return "Defend";      }
    if (state == c_waveStateScout)          { return "Scout";       }
    if (state == c_waveStateRetreat)        { return "Retreat";     }
    if (state == c_waveStateClearObs)       { return "ClearObs";    }
    if (state == c_waveStateGuardHome)      { return "Home";        }
    return "?" + IntToString(state) + "?";
}

//--------------------------------------------------------------------------------------------------
void DebugWave (int player, string name, wave w) {
    string threat;
    if (AIDefenseThreat(c_dtAnyThreat, player, w)) {
        threat = "true";
    }
    else {
        threat = "false";
    } 
    DebugAIPlayer(
        player,
        "wave="         + name                                          +
        ", units="      + IntToString(AIWaveUnitCount(w))               +
        ", state="      + WaveStateName(AIWaveState(w))                 +
        ", ratio="      + IntToString(AIWaveEvalRatio(w, c_evalRange))  +
        ", combat="     + IntToString(AIWaveGetTimeInCombat(w))         +
        ", safe="       + IntToString(AIWaveGetTimeSinceCombat(w))      +
        ", threat="     + threat
    );
}

//--------------------------------------------------------------------------------------------------
void DebugMelee (int player) {

    DebugWave(player, "main", AIWaveGet(player, c_waveMain));
    DebugWave(player, "atck", AIWaveGet(player, c_waveAttack));
    DebugWave(player, "dfnd", AIWaveGet(player, c_waveDefend));
    DebugWave(player, "div1", AIWaveGet(player, c_waveDivert1));
    DebugWave(player, "div2", AIWaveGet(player, c_waveDivert2));
    DebugWave(player, "clob", AIWaveGet(player, c_waveClearObs));
    DebugWave(player, "home", AIWaveGet(player, c_waveHome));
   
    DebugVarInt2(
        "peons cur",    AIGetCurPeonCount(player, c_townMax),
        "max",          AIGetMaxPeonCount(player, c_townMax)
    );
    DebugVarBool("e_flagsScouting", AIGetFlag(player, e_flagsScouting));
    DebugVarBool("e_flagsTimeout", AIGetFlag(player, e_flagsTimeout));
}

//--------------------------------------------------------------------------------------------------
bool DebugMeleeTrigger (bool c, bool a) {
    if (a) {
        DebugMelee(StringToInt(StringWord(EventChatMessage(false), 2)));
    }
    return true;
}

//--------------------------------------------------------------------------------------------------
bool g_debugMeleeInit = false;
void DebugMeleeInit () {
    trigger t;

    if (!g_debugMeleeInit) {
        t = TriggerCreate("DebugMeleeTrigger");
        TriggerAddEventChatMessage(t, 1, "waves", false);

        g_debugMeleeInit = true;
    }
}

//--------------------------------------------------------------------------------------------------
string MainStateName (int state) {
    if (state == e_mainState_Init)              { return "Init";        }

    else if (state == e_mainState_OpenGnd0)     { return "OpenGnd0";    }
    else if (state == e_mainState_OpenGnd1)     { return "OpenGnd1";    }
    else if (state == e_mainState_OpenGnd2)     { return "OpenGnd2";    }
    else if (state == e_mainState_OpenGnd3)     { return "OpenGnd3";    }
    else if (state == e_mainState_OpenGnd4)     { return "OpenGnd4";    }
    else if (state == e_mainState_OpenGnd5)     { return "OpenGnd5";    }

    else if (state == e_mainState_OpenAir0)     { return "OpenAir0";    }
    else if (state == e_mainState_OpenAir1)     { return "OpenAir1";    }
    else if (state == e_mainState_OpenAir2)     { return "OpenAir2";    }
    else if (state == e_mainState_OpenAir3)     { return "OpenAir3";    }
    else if (state == e_mainState_OpenAir4)     { return "OpenAir4";    }
    else if (state == e_mainState_OpenAir5)     { return "OpenAir5";    }

    else if (state == e_mainState_Mid0)         { return "Mid0";        }
    else if (state == e_mainState_Mid1)         { return "Mid1";        }
    else if (state == e_mainState_Mid2)         { return "Mid2";        }
    else if (state == e_mainState_Mid3)         { return "Mid3";        }
    else if (state == e_mainState_Mid4)         { return "Mid4";        }
    else if (state == e_mainState_Mid5)         { return "Mid5";        }

    else if (state == e_mainState_Late0)        { return "Late0";       }
    else if (state == e_mainState_Late1)        { return "Late1";       }
    else if (state == e_mainState_Late2)        { return "Late2";       }
    else if (state == e_mainState_Late3)        { return "Late3";       }
    else if (state == e_mainState_Late4)        { return "Late4";       }
    else if (state == e_mainState_Late5)        { return "Late5";       }

    else if (state == e_mainState_Off)          { return "Off";         }

    return "?" + IntToString(state) + "?";
}

//--------------------------------------------------------------------------------------------------
//  AISetAttackState
//--------------------------------------------------------------------------------------------------
void AISetAttackState (int player, int attackState) {
    string msg;
    int oldState = AIState(player, e_attackState);

    AISetSpecificState(player, e_attackState, attackState);
   
    if (DEBUG_ATTACK_STATE) {
        msg = "Player " + IntToString(player) + "-" + PlayerRace(player) + " attack state " +
            AttackStateName(oldState) + " -> " + AttackStateName(attackState);
        DebugAI(msg);
    }
}

//--------------------------------------------------------------------------------------------------
//  AISetMainState
//--------------------------------------------------------------------------------------------------
void AISetMainState (int player, int mainState, int mainSubState) {
    string msg;
    int oldMainState = AIState(player, e_mainState);
    int oldSubState = AIState(player, e_mainSubState);

    AISetSpecificState(player, e_mainState, mainState);
    AISetSpecificState(player, e_mainSubState, mainSubState);

    if (DEBUG_MAIN_STATE) {
        msg = "Player " + IntToString(player) + "-" + PlayerRace(player) + " main state " +
            MainStateName(oldMainState) + "(" + IntToString(oldSubState) + ") --> " +
            MainStateName(mainState)  + "(" + IntToString(mainSubState) + ")";
        DebugAI(msg);
    }
}

//--------------------------------------------------------------------------------------------------
//  EndMeleeScript
//--------------------------------------------------------------------------------------------------
void EndMeleeScript (int player) {
    string msg = "Reached end of script for player " + IntToString(player) + "-" +
        PlayerRace(player) + " : More AI code coming soon.";
    UIDisplayMessage(PlayerGroupAll(), c_messageAreaSubtitle, StringToText(msg));
    DebugAI(msg);
    AISetMainState(player, e_mainState_Disabled, e_mainSubState_Unset);
}

//--------------------------------------------------------------------------------------------------
//  ErrorMeleeScript
//--------------------------------------------------------------------------------------------------
void ErrorMeleeScript (int player, string error) {
    string msg = "A script logic error occurred for player " + IntToString(player) +
        "-" + PlayerRace(player) + " : " + error;
    UIDisplayMessage(PlayerGroupAll(), c_messageAreaSubtitle, StringToText(msg));
    DebugAI(msg);
    AISetMainState(player, e_mainState_Disabled, e_mainSubState_Unset);
}

//--------------------------------------------------------------------------------------------------
//  AISetGasPeons
//--------------------------------------------------------------------------------------------------
void AISetGasPeons (int player, int count, string type, string from) {
    int sources = AITechCount(player, from, c_techCountCompleteOnly);
    if (sources < 1
        || AIHasRes(player, 0, 200)
        || AITechCount(player, type, c_techCountCompleteOnly) < 8)
    {
        AISetGasPeonCountOverride(player, c_townMain, c_defaultGasPeonCount);
    }

    else if (sources == 1) {
        AISetGasPeonCountOverride(player, c_townMain, 3);
    }
    else { // sources == 2+
        AISetGasPeonCountOverride(player, c_townMain, count);
    }
}

//--------------------------------------------------------------------------------------------------
//  AISetStockAll
//--------------------------------------------------------------------------------------------------
void AISetStockAll (int player, string source, int max, string what) {
    int make = AITechCount(player, what, c_techCountCompleteOnly)
             + 2 * AITechCount(player, source, c_techCountCompleteOnly);
    if (make > max) {
        make = max;
    }
    AISetStock(player, make, what);
}

//--------------------------------------------------------------------------------------------------
//  AIMergeUnit
//--------------------------------------------------------------------------------------------------
void AIMergeUnit (int player, unit u, wave w) {
    wave merge = AIWaveCreate(AIWaveInfoCreate(), player, AIGetGatherLocation(player, c_townMain));
    AIWaveAddUnit(merge, u);
    AIWaveSetType(merge, c_waveStateMerge, AIWaveTargetMerge(w));
}

//--------------------------------------------------------------------------------------------------
//  AINewUnitDefault
//--------------------------------------------------------------------------------------------------
void AINewUnitDefault (int player, unit u) {
    wave w = AIWaveGet(player, c_waveMain);
    if (AIDefenseThreat(c_dtAnyThreat, player, w)) {
        AIMergeUnit(player, u, w);
    }
    else {
        AIWaveAddUnit(w, u);
    }
}

//--------------------------------------------------------------------------------------------------
//  AICreateOrder
//--------------------------------------------------------------------------------------------------
order AICreateOrder (int player, string abilLink, int abilIndex) {
    abilcmd cmd = AbilityCommand(abilLink, abilIndex);
    order ord;
    if (cmd == c_nullCmd) {
        return null;
    }
    ord = Order(cmd);
    OrderSetPlayer(ord, player);
    return ord;
}

MisterTea

MeleeAI.galaxy < suite

//--------------------------------------------------------------------------------------------------
//  AITacticalOrder
//--------------------------------------------------------------------------------------------------
order AITacticalOrder (int player, unit aiUnit, string abilLink) {
    order ord = AICreateOrder(player, abilLink, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }
    return ord;
}

//--------------------------------------------------------------------------------------------------
//  AITacticalOrderIndex
//--------------------------------------------------------------------------------------------------
order AITacticalOrderIndex (int player, unit aiUnit, string abilLink, int abilIndex) {
    order ord = AICreateOrder(player, abilLink, abilIndex);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }
    return ord;
}

//--------------------------------------------------------------------------------------------------
//  AICombatPriority
//--------------------------------------------------------------------------------------------------
unit AICombatPriority (unit target, unitgroup attackers, unitgroup enemies) {
    return AIDefaultCombatPriority(target, attackers, enemies);
}

//--------------------------------------------------------------------------------------------------
//  AIDefendTown
//--------------------------------------------------------------------------------------------------
void AIDefendTown (int player, wave w) {
    int eval = AIWaveEvalRatio(w, c_evalDefendRange);
    int staticPercent = AILastWaveEvalStaticRatio();
    int neededEval = 60;

    if (staticPercent > 90) {
        neededEval = 80;
    }
    else if (staticPercent > 40) {
        neededEval = 70;
    }

    if (eval < neededEval) {
        if (AIWaveState(w) != c_waveStateRetreat) {
DebugAIPlayerWave(player, "defend1 set wave = retreat to gather defense");
            AIWaveSetType(w, c_waveStateRetreat, AIWaveTargetGatherD(player, c_townMax));
        }
    }
    else if (AIWaveState(w) != c_waveStateDefend) {
        if (eval > neededEval+10) {
            if (AIWaveGetTimeSinceOrdered(w) >= 20) {
                DebugAIPlayerWave(player, "defend2 set wave = defend vs. threats");
                AIWaveSetType(w, c_waveStateDefend, AIWaveTargetMeleeDefend(player));
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
//  AIWaveDefend
//--------------------------------------------------------------------------------------------------
void AIWaveDefend (int player, wave w) {
    if (AIDefenseThreat(c_dtAnyThreat, player, w)) {
        AIDefendTown(player, w);
    }
    else if (AIWaveState(w) != c_waveStateIdle) {
DebugAIPlayerWave(player, "defend3 set defend = idle at gather defense");
        AIWaveSetType(w, c_waveStateIdle, AIWaveTargetGatherD(player, c_townMax));
    }
}

//--------------------------------------------------------------------------------------------------
//  AIWaveMain
//--------------------------------------------------------------------------------------------------
void AIWaveMain (int player, wave w) {
    unit obstruction = null;
    int count = AIWaveUnitCount(w);
    int state = AIWaveState(w);
    int attackState = AIState(player, e_attackState);

    if (AIWaveIsInCombat(w)) {
        return;
    }
    if (AIDefenseThreat(c_dtAnyThreat, player, w)) {
        AIDefendTown(player, w);
        return;
    }

    if (count >= 3 && AIGetFlag(player, e_flagsTimeout)) {
        if (attackState != e_attackState_DropAttack && attackState != e_attackState_DropRetreat) {
            AIWaveMerge(player, c_waveMain, c_waveAttack);
            return;
        }
    }

    if (state != c_waveStateIdle) {
DebugAIPlayerWave(player, "main2 set main = idle at gather offense");
        AIWaveSetType(w, c_waveStateIdle, AIWaveTargetGatherO(player, c_townMax));
    }
}

//--------------------------------------------------------------------------------------------------
//  AIWaveDivert
//--------------------------------------------------------------------------------------------------
void AIWaveDivert (int player, wave w, int stateIndex) {
    int state;
    int count;
   
    if (AIDefenseThreat(c_dtAnyThreat, player, w)) {
        AIDefendTown(player, w);
        return;
    }
   
    state = AIWaveState(w);
    if (state == c_waveStateDefend) {
DebugAIPlayerWave(player, "divert1 set divert = idle at gather defense");//xxx
        AIWaveSetType(w, c_waveStateIdle, AIWaveTargetGatherD(player, c_townMax));
        return;
    }

    if (!AIGetFlag(player, e_flagsDiversion)) {
        return;
    }

    if (AIWaveEvalRatio(w, c_evalHarassRange) < 100) {
        if (state != c_waveStateRetreat) {
DebugAIPlayerWave(player, "divert2 set divert = retreat to harass point");//xxx
            AIWaveSetType(w, c_waveStateRetreat, AIWaveHarassRetreat(player, w, c_evalHarassRange));           
        }
    }
    else if (state != c_waveStateAttack) {
        if (AIWaveGetTimeSinceOrdered(w) >= 10) {
DebugAIPlayerWave(player, "divert3 set divert = attack harass target");//xxx
            AIWaveSetType(w, c_waveStateAttack, AIWaveTargetMeleeHarass(player));
        }
    }
}

//--------------------------------------------------------------------------------------------------
//  AIWaveHome
//--------------------------------------------------------------------------------------------------
void AIWaveHome (int player, wave w) {
    int state;
   
    state = AIWaveState(w);
    if (state != c_waveStateGuardHome) {
DebugAIPlayerWave(player, "home set divert = idle at gather defense");
        AIWaveSetType(w, c_waveStateGuardHome, AIWaveTargetGatherD(player, c_townMax));
        return;
    }
}

//--------------------------------------------------------------------------------------------------
// AIWaveClearObs
//--------------------------------------------------------------------------------------------------
void AIWaveClearObs (int player, wave w) {
    unit obstruction = AIGetObstruction(player);
    unit oldTarget = null;
    int obsLife;
    int state = AIWaveState(w);

    // If there are no longer any obstructions, merge back into the main wave
    if (obstruction == null) {
DebugAIPlayerWave(player, "clearobs1 merge clear obstruction -> main");
        AIWaveMerge(player, c_waveClearObs, c_waveMain);
        return;
    }

    // Defend against threats, unless we're almost done destroying the obstruction
    obsLife = UnitGetPropertyInt(obstruction, c_unitPropLifePercent, c_unitPropCurrent);
    if (AIDefenseThreat(c_dtAnyThreat, player, w) && obsLife > 20) {
        AIDefendTown(player, w);
        return;
    }

    // Let's hunt some rock!
    oldTarget = AIWaveTargetGetUnit(AIWaveGetTarget(w));
    if (state != c_waveStateClearObs || obstruction != oldTarget) {
DebugAIPlayerWave(player, "clearobs2 set clear obstruction = clear obstruction");
        AIWaveSetType(w, c_waveStateClearObs, AIWaveTargetUnit(obstruction));
    }
}

//--------------------------------------------------------------------------------------------------
// AINewDetector
//--------------------------------------------------------------------------------------------------
void AINewDetector (int player, unit u, bool offerToScout) {
    wave defendWave = AIWaveGet(player, c_waveDefend);
    wave attackWave = AIWaveGet(player, c_waveAttack);

    // first offer it to the attack wave if that wave has some units in it
    if (AIWaveUnitCount(attackWave) >= 4) {
        if (AIWaveDetectorCount(attackWave) == 0) {
            AIWaveAddUnitPriority(attackWave, u, c_prioWavePeon);
            return;
        }
    }

    // next offer it to the defense wave
    if (AIWaveDetectorCount(defendWave) == 0) {
        AIWaveAddUnitPriority(defendWave, u, c_prioWavePeon);
        return;
    }

    // last offer it to the scout synapse
    if (offerToScout) {
        if (AIOfferNewScout(player, u)) {
            return;
        }
    }

    // let the attack wave claim a second one
    if (AIWaveUnitCount(attackWave) >= 4) {
        if (AIWaveDetectorCount(attackWave) < 2) {
            AIWaveAddUnitPriority(attackWave, u, c_prioWavePeon);
            return;
        }
    }

    // else add the detector to the extra scout group
    AIAddToExtraScoutGroup(player, u);
}

//--------------------------------------------------------------------------------------------------
// AIWaveNeedClearObsUnits
//--------------------------------------------------------------------------------------------------
bool AIWaveNeedClearObsUnits (int player) {
    unit obstruction = null;
    wave waveClob = null;
    wave waveAtck = null;
    int countClob = 0;
    int countAtck = 0;
    int evalAtck = 0;

    // Check global option
    if (AIGetFlag(player, e_flagsClearObs) == false) {
        return false;
    }

    // Don't clear obstructions in the first 10 minutes (leaves the AI too open to being rushed)
    if (AIGetTime() < 600) {
        return false;
    }

    // See if there is any obstruction
    obstruction = AIGetObstruction(player);
    if (obstruction == null) {
        return false;
    }

    // See if the clear obstruction wave is already full
    waveClob = AIWaveGet(player, c_waveClearObs);
    countClob = AIWaveUnitCount(waveClob);
    if (countClob >= 4) {
        return false;
    }

    // If the attack wave is getting weak, keep units available for it instead
    waveAtck = AIWaveGet(player, c_waveAttack);
    countAtck = AIWaveUnitCount(waveAtck);
    evalAtck = AIWaveEvalRatio(waveAtck, c_evalRange);
    if (countAtck > 0 && evalAtck < 80) {
        return false;
    }

    // Yes, units are needed for clear obstruction duty
    return true;
}

//--------------------------------------------------------------------------------------------------
//  AIWaveAttackDefend
//--------------------------------------------------------------------------------------------------
static bool AIWaveAttackDefend (int player, wave w, int state) {

    if (!AIDefenseThreat(c_dtRealThreat, player, w)) {
        return false;
    }

    if ( state == e_attackState_Idle || state == e_attackState_Wait ||
         (AIWaveEval(AIWaveGet(player, c_waveMain)) + AIWaveEval(AIWaveGet(player, c_waveDefend)) < AIDefenseThreatEval(c_dtEvalRealThreats, player)) )
    {
        return true;
    }

    return false;
}

//--------------------------------------------------------------------------------------------------
//  AIIsAttacking
//--------------------------------------------------------------------------------------------------
bool AIIsAttacking (int player) {
    wave waveAttack = null;

    // for now I'm not counting e_attackState_DropAttack as attacking, it is a specialized attack
    // and not easy for other AI's to coordinate with it

    if (AIState(player, e_attackState) != e_attackState_Attack) {
        return false;
    }

    waveAttack = AIWaveGet(player, c_waveAttack);
    if (AIWaveUnitCount(waveAttack) == 0) {
        return false;
    }

    if (AIWaveState(waveAttack) != c_waveStateAttack) {
        return false;
    }

    return true;
}

//--------------------------------------------------------------------------------------------------
//  AINeedsDefending
//--------------------------------------------------------------------------------------------------
bool AINeedsDefending (int player) {
    return AIWaveAttackDefend(player, c_nullWave, e_attackState_Attack);
}

//--------------------------------------------------------------------------------------------------
//  TestUseDropAttack
//--------------------------------------------------------------------------------------------------
bool TestUseDropAttack (int player, wave w) {
    int numMobileTransports;

    // see if we can and want to do a drop attack
    numMobileTransports = AIGetNumMobileTransports(player);

    if (numMobileTransports >= 1) {
        // we have a transport, so request more so we can use them to do a drop later
        AISetWantsMultipleTransport(player, true);
    }
    else {
        // don't worry about drops if we have no transports.
        return false;
    }

    if (numMobileTransports < 3) {
        // wait until we have at least 3 transports to try a drop
        return false;
    }

    if (AIGetNextDropTimeCheck(player) >= AIGetTime()) {
        // only check drop attacks every so often
        return false;
    }

    AISetNextDropTimeCheck(player, AIGetTime() + c_dropCheckRollFreq);

    if (RandomInt(1,100) > c_dropAttackChance) {
        return false;
    }

    if (!AIFindDropAttackTarget(player, AIGetGatherLocation(player, c_townMain))) {
        return false;
    }

    AISetNextDropTimeCheck(player, AIGetTime() + c_dropCheckAttackFreq);

    AISetAttackState(player, e_attackState_DropAttack);
DebugAIPlayerWave(player, "attack1drop merge main -> attack; set attack = drop attack");
    AIWaveMerge(player, c_waveMain, c_waveAttack);
    AIWaveSetType(w, c_waveStateDropAttack, AIWaveTargetMeleeDrop(player, AILastDropLocation(), AILastDropGoal()));
    return true;
}

//--------------------------------------------------------------------------------------------------
//  AdvancedIdleAttackLogic
//--------------------------------------------------------------------------------------------------
void AdvancedIdleAttackLogic (int player, wave w, int count) {
    // hard/insane logic for when to attack again
    int eval;
    bool enoughForce = false;

    if (AILastAttackRatio(w) >= 120) {
        // if the last attack was successful (we killed a good bit more than we lost)
        // then attack again as soon as we've gathered 20 units or it's been a short amount of time
        if (count >= 20) {
            AISetAttackState(player, e_attackState_Attack);
        }
        else if (count >= 10 && AIGetFlag(player, e_flagsTimeout) && AIWaveGetTimeSinceCombat(w) >= 120) {
            AISetAttackState(player, e_attackState_Attack);
        }
    }
    else if (AILastAttackRatio(w) >= 80) {
        // if the last attack was even (we did fight and we killed about as much as we lost)
        // then we to attack until we've regrouped a bit
        if (count >= 12) {
            if (TestUseDropAttack(player,w)) {
                return;
            }
        }
        if (count >= 30) {
            AISetAttackState(player, e_attackState_Attack);
        }
        else if (count >= 15 && AIGetFlag(player, e_flagsTimeout) && AIWaveGetTimeSinceCombat(w) >= 300 - 5*count) {
            AISetAttackState(player, e_attackState_Attack);
        }
    }
    else { // AILastAttackRatio(w) < 80
        // if the last attack was a failure (we either retreated right away or lost more then we killed)
        if (count >= 12) {
            if (TestUseDropAttack(player,w)) {
                return;
            }
        }
        if (count < 20) {
            return;
        }

        // force us to gather more units than last time, or use at least 160 food cap
        if (PlayerGetPropertyInt(player, c_playerPropSuppliesUsed) > 160) {
            enoughForce = true;
        }
        else {
            eval = AIWaveEval(w) + AIWaveEval(AIWaveGet(player, c_waveMain));
            if (IntToFixed(eval) > IntToFixed(AILastAttackStartEval(w)) * 1.33) {
                enoughForce = true;
            }
        }

        if (enoughForce) {
            AISetAttackState(player, e_attackState_Attack);
        }
    }
}

//--------------------------------------------------------------------------------------------------
//  AIWaveAttack
//--------------------------------------------------------------------------------------------------
void AIWaveAttack (int player, wave w) {
    int state = AIState(player, e_attackState);
    int eval;
    int time;
    int count;

    //--- DEFEND ---

    if (AIWaveAttackDefend(player, w, state)) {
        AIDefendTown(player, w);
        return;
    }

    //--- ATTACK ---

    if (state == e_attackState_Attack) {
        time = AIWaveGetTimeInCombat(w);
        eval = AIWaveEvalRatio(w, c_evalRange);
        if (eval >= 70 && eval <= 85 && time <= 3) { // when not fighting and only a little weak
            if (AIGetFlag(player, e_flagsRunScared)) { // need to add check for ranges & max eval
                AISetAttackState(player, e_attackState_Scared);
            }
            else {
                AISetAttackState(player, e_attackState_Retreat);
            }
        }
        else if (eval < 70) { // need to add check for faster/retreat blocked
            AISetAttackState(player, e_attackState_Retreat);
        }
        else if (AIWaveState(w) != c_waveStateAttack) {
DebugAIPlayerWave(player, "attack1 merge main -> attack; set attack = attack vs. melee target");
            AIWaveMerge(player, c_waveMain, c_waveAttack);
            AIWaveSetType(w, c_waveStateAttack, AIWaveTargetMelee(player));
        }
    }

    //--- DROP-ATTACK ---

    if (state == e_attackState_DropAttack) {       
        eval = AIWaveEvalRatio(w, c_evalRange);
        if (eval < 80) { // need to add check for can we retreat successfully
            AISetAttackState(player, e_attackState_DropRetreat);
        }
        if (AIWaveState(w) != c_waveStateDropAttack) {
DebugAIPlayerWave(player, "attack1a In drop attack state, but not drop attack wave state?");
            AISetAttackState(player, e_attackState_DropRetreat);
        }
    }

    //--- SCARED ---

    else if (state == e_attackState_Scared) {
        eval = AIWaveEvalRatio(w, c_evalRange);
        if (eval > 95) { // turn and fight when strong
            AISetAttackState(player, e_attackState_Attack);
        }
        else if (eval < 70) { // retreat entirely if getting picked off
            AISetAttackState(player, e_attackState_Retreat);
        }
        else if (AIWaveState(w) != c_waveStateRetreat) {
DebugAIPlayerWave(player, "attack2 merge main -> attack; set attack = retreat to gather offense");
            AIWaveMerge(player, c_waveMain, c_waveAttack);
            AIWaveSetType(w, c_waveStateRetreat, AIWaveTargetGatherO(player, c_townMain));
        }
    }

    //--- RETREAT ---

    else if (state == e_attackState_Retreat) {       
DebugAIPlayerWave(player, "attack3 set attack = retreat to gather offense");
        AIWaveSetType(w, c_waveStateRetreat, AIWaveTargetGatherO(player, c_townMain));
        AISetAttackState(player, e_attackState_InRetreat);
    }

    //--- DROP RETREAT ---

    else if (state == e_attackState_DropRetreat) {       
DebugAIPlayerWave(player, "attack3drop set attack = drop retreat to gather offense");
        AIWaveSetType(w, c_waveStateDropRetreat, AIWaveTargetGatherO(player, c_townMain));
        AISetAttackState(player, e_attackState_InRetreat);
    }

    //--- IN RETREAT ---

    else if (state == e_attackState_InRetreat) {
        if (AIWaveState(w) != c_waveStateRetreat && AIWaveState(w) != c_waveStateDropRetreat) {
DebugAIPlayerWave(player, "attack4 merge main -> attack");
            AIWaveMerge(player, c_waveMain, c_waveAttack);
            AISetAttackState(player, e_attackState_Idle);
        }
    }
   

MisterTea

#3
MeleeAI.galaxy < suite

    //--- IDLE ---

    else if (state == e_attackState_Idle) {
        count = AIWaveUnitCount(w) + AIWaveUnitCount(AIWaveGet(player, c_waveMain));

        // wait 30 seonds at least after retreating before attacking again
        if (AIWaveGetTimeSinceRetreat(w) > 30) {
            // always support allies attacking
            if (AIAnyAllyAttacking(player)) {
                AISetAttackState(player, e_attackState_Attack);
            }
            if (PlayerDifficulty(player) >= c_skirVeryHard) {
                if (AIWaveState(w) == c_waveStateIdle) {
                    // wait until the wave is idle before considering attacking again
                    AdvancedIdleAttackLogic(player, w, count);
                }
            }
            else {
                // base logic attack any time we have more than 30 units or it's been a while
                if (count >= 30) {
                    AISetAttackState(player, e_attackState_Attack);
                }
                else if (AIGetFlag(player, e_flagsTimeout) && AIWaveGetTimeSinceCombat(w) >= 300-10*count) {
                    AISetAttackState(player, e_attackState_Attack);
                }
            }
        }
       
        if (AIState(player, e_attackState) == e_attackState_Idle) {
            if (AIWaveState(w) != c_waveStateIdle) {
                DebugAIPlayerWave(player, "attack5 set attack = idle at gather offense");
                AIWaveSetType(w, c_waveStateIdle, AIWaveTargetGatherO(player, c_townMax));
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
//  AIWaveThinkDefault
//--------------------------------------------------------------------------------------------------
void AIWaveThinkDefault (int player, wave w, int type) {
    if (AIWaveUnitCount(w) < 1) {
        return;
    }
    if (type == c_waveMain) {
        AIWaveMain(player, w);
    }
    else if (type == c_waveDefend) {
        AIWaveDefend(player, w);
    }
    else if (type == c_waveAttack) {
        AIWaveAttack(player, w);
    }
    else if (type == c_waveDivert1) {
        AIWaveDivert(player, w, e_divert1State);
    }
    else if (type == c_waveDivert2) {
        AIWaveDivert(player, w, e_divert2State);
    }
    else if (type == c_waveClearObs) {
        AIWaveClearObs(player, w);
    }
    else if (type == c_waveHome) {
        AIWaveHome(player, w);
    }
}

//--------------------------------------------------------------------------------------------------
//  AIMainStateSelect
//--------------------------------------------------------------------------------------------------
int AIMainStateSelect (int player, int roll, int range, int state, int subState) {
    if (roll >= 1 && roll <= range) {
        AISetMainState(player, state, subState);
    }
    return roll - range;
}

//--------------------------------------------------------------------------------------------------
//  AIMergeWait
//--------------------------------------------------------------------------------------------------
void AIMergeWait (int player, int count, string what, int size, int state, int subState) {
    wave atk = AIWaveGet(player, c_waveAttack);
   
    if (AIWaveState(atk) != c_waveStateRetreat) {
        if (AIWaveUnitCount(AIWaveGet(player, c_waveMain)) >= 3 ||
            AITechCount(player, what, c_techCountCompleteOnly) >= count)
        {
DebugAIPlayerWave(player, "merge1 merge main -> attack");
            AIWaveMerge(player, c_waveMain, c_waveAttack);
        }
    }
    if (AIWaveUnitCount(atk) >= size) {
        AISetAttackState(player, e_attackState_Attack);
        AISetMainState(player, state, subState);
        AISetFlag(player, e_flagsTimeout, true);
    }
    else if (AIHasRes(player, 1000, 500)) {
        AISetMainState(player, state, subState);
        AISetFlag(player, e_flagsTimeout, true);
    }
}

//--------------------------------------------------------------------------------------------------
//  AIStockAndWait
//--------------------------------------------------------------------------------------------------
bool AIStockAndWait (int player, int count, string type) {
    if (AITechCount(player, type, c_techCountInProgressOrBetter) < count) {
        AISetStock( player, count, type );   
        AIEnableStock(player);
        return true;
    }
    return false;
}

//--------------------------------------------------------------------------------------------------
//  AISuspectDetectionDanger
//--------------------------------------------------------------------------------------------------
bool AISuspectDetectionDanger (int player) {
    bool suspectDanger = false;

    suspectDanger = AIDefaultSuspectDetectionDanger(player);

    // script additions / override of the default

    return suspectDanger;
}

//--------------------------------------------------------------------------------------------------
//  AITownIsFull
//--------------------------------------------------------------------------------------------------
void AITownIsFull (int player, int town) {
    // town was unable to place a building because it ran out of room, it may still
    // be possible to place a smaller building or a different type of building
    AIUpdateMainTown(player);

    // if we only have no non-full town, we may want to expand now
}

//--------------------------------------------------------------------------------------------------
//  AITownWasLost
//--------------------------------------------------------------------------------------------------
void AITownWasLost (int player, int town) {
    // one of our towns was destroyed/lost, happens when we lose our last dropoff at the town (or all buildings).
    AIUpdateMainTown(player);
}

//--------------------------------------------------------------------------------------------------
//  AIMakeTest
//--------------------------------------------------------------------------------------------------
bool AIMakeTest (int player, int min, int max, string type, string tech,
                string base, int addons, string addon)
{
    int count = AICounterUnits(player, c_maxPlayers, type);
    if (count <= 0) {
        return false;
    }
    if (count < min) {
        count = min;
    }
    else if (count > max) {
        count = max;
    }
    if (AITechCount(player, type, c_techCountInProgressOrBetter) >= count) {
        return false;
    }
    if (count > 5) {
        AISetStockUnitNext( player, 3, base, c_stockIdle );
        AISetStockUnitNext( player, addons, addon, c_stockIdle );
    }
    else if (count > 2) {
        AISetStockUnitNext( player, 2, base, c_stockIdle );
        if (addons > 2) {
            AISetStockUnitNext( player, 2, addon, c_stockIdle );
        }
        else {
            AISetStockUnitNext( player, addons, addon, c_stockIdle );
        }
    }
    if (tech != c_noTech) {
        AISetStock( player, 1, tech );
    }
    AISetStockUnitNext( player, count, type, c_stockAlways );
    return true;
}

//--------------------------------------------------------------------------------------------------
//  AIMineralsTotal
//--------------------------------------------------------------------------------------------------
int AIMineralsTotal (int player) {
    return PlayerGetPropertyInt(player, c_playerPropMinerals) + AIGetMineralAmountLeft(player, c_townMax);
}

//--------------------------------------------------------------------------------------------------
//  AIWavePrimaryCount
//--------------------------------------------------------------------------------------------------
int AIWavePrimaryCount (int player) {
    return AIWaveUnitCount(AIWaveGet(player, c_waveMain)) + AIWaveUnitCount(AIWaveGet(player, c_waveAttack));
}

//--------------------------------------------------------------------------------------------------
//  AIDiffEnum
//--------------------------------------------------------------------------------------------------
int AIDiffEnum (int player, int base) {
    return base + PlayerDifficulty(player);
}

//--------------------------------------------------------------------------------------------------
//  AIDiffThreshold
//--------------------------------------------------------------------------------------------------
int AIDiffThreshold (int player, int defaultValue, int threshold, int alternateValue) {
    int diff = PlayerDifficulty(player);
    if (diff < threshold) {
        return defaultValue;
    }
    return alternateValue;
}

//--------------------------------------------------------------------------------------------------
//  AIAddDetectionDangerUnits
//--------------------------------------------------------------------------------------------------
void AIAddDetectionDangerUnits (int player) {
    AIAddDetectionDanger(player, c_TU_Ghost);
    AIAddDetectionDanger(player, c_TU_Banshee);
    AIAddDetectionDanger(player, c_ZU_Lurker);
    AIAddDetectionDanger(player, c_PU_DarkTemplar);
    AIAddDetectionDanger(player, c_PU_Mothership);
    AIAddDetectionDanger(player, c_TB_GhostAcademy);
    AIAddDetectionDanger(player, c_TB_FusionCore);
    AIAddDetectionDanger(player, c_ZB_LurkerDen);
    AIAddDetectionDanger(player, c_PB_DarkShrine);
}

//--------------------------------------------------------------------------------------------------
//  Default startup routine
//--------------------------------------------------------------------------------------------------
void AIMeleeStart (int player) {
    point targ;

    if (DEBUG) {
        DebugMeleeInit();
    }

    AIStart(player, false, DifficultyAPM(PlayerDifficulty(player)));
       
    if (PlayerDifficulty(player) >= c_skirVeryHard) {       
        if (PlayerDifficulty(player) >= c_skirCheater) {
            AIHarvestBonus(player, 2.0);
            AISetDifficulty(player, c_diffNormalVision, false);
            AISetDifficulty(player, c_diffLimitAPM, false);
        }
        AISetFlag(player, e_flagsRunScared, false); // TODO fix scared and re-add this
    }
    else {       
        AISetDifficulty(player, c_diffAdvanceWave, false);
        AISetDifficulty(player, c_diffFleeDamage, false);
        AISetDifficulty(player, c_diffWaveAvoidDanger, false);
        AISetFlag(player, e_flagsRunScared, false);
    }
    AISetDifficulty(player, c_diffAutoLoadBunkers, false);
    AISetDifficulty(player, c_diffEarlyGameRepair, false);   
    AISetDifficulty(player, c_diffEarlyDefenseScout, false);
    AISetFlag(player, e_flagsLateScout, false);
    AISetFlag(player, e_flagsClearObs, false);

    AIDeclareTown(player, c_townOne, PlayerStartLocation(player));
    AISetMainTown(player, c_townOne);
    AIHarvest(player, c_townOne);
    AISetNumScouts(player, 1);
    AIScout(player);
    AISetAllStates(player, 1);
   
    targ = AIWaveTargetGatherOPoint(player, c_townMain);
    AIWaveSet(player, c_waveMain,       AIWaveCreate(AIWaveInfoCreate(), player, targ));
    AIWaveSet(player, c_waveAttack,     AIWaveCreate(AIWaveInfoCreate(), player, targ));
    AIWaveSet(player, c_waveDivert1,    AIWaveCreate(AIWaveInfoCreate(), player, targ));
    AIWaveSet(player, c_waveDivert2,    AIWaveCreate(AIWaveInfoCreate(), player, targ));
    AIWaveSet(player, c_waveClearObs,   AIWaveCreate(AIWaveInfoCreate(), player, targ));
    AIWaveSet(player, c_waveHome,       AIWaveCreate(AIWaveInfoCreate(), player, targ));

    targ = AIWaveTargetGatherDPoint(player, c_townMain);
    AIWaveSet(player, c_waveDefend,     AIWaveCreate(AIWaveInfoCreate(), player, targ));

    AITransportIgnore(player, "VikingAssault");
    AITransportSetPanic(player, 0.6);
    AITransportSetReturn(player, targ);
 
    AISpecifiedMakers();
    AISetDefaultCombatFlags(player, true);
    AIAddDetectionDangerUnits(player);

    //AISetNukeConstants(player);
}

include "TriggerLibs/MeleeNotHardAI"

MisterTea



MeleeNotHardAI.galaxy
//==================================================================================================
//  Melee AI Functions only used by beginner/easy/medium.
//--------------------------------------------------------------------------------------------------
// Very Easy
//  APM: 100
//  scouts: starts late game
//  detect: late game or after being attacked by cloakers
//  wave 1: 100 resources, ~2 units, 480 seconds
//  wave 2: 300 resources, ~3 units, +360 seconds (840)
//  wave 3: 600 resources, ~4 units, +360 seconds (1200)
//  wave X: 900 resources, ~5 units, +300 seconds
//--------------------------------------------------------------------------------------------------
// Easy
//  APM: 200
//  scouts: starts late game
//  detect: late game or after being attacked by cloakers
//  wave 1: 300 resources, ~4 units, 450 seconds
//  wave 2: 800 resources, ~6 units, +340 seconds (790)
//  wave 3: 1500 resources, ~8 units, +340 seconds (1130)
//  wave X: xxx resources, ~10 units, +280 seconds
//--------------------------------------------------------------------------------------------------
// Medium
//  APM: 300
//  scouts: starts middle game
//  detect: middle game or after being attacked by cloakers
//  wave 1: 500 resources, ~6 units, 420 seconds
//  wave 2: 1000 resources, ~9 units, +320 seconds (740)
//  wave 3: 1800 resources, ~12 units, +320 seconds (1060)
//  wave X: xxx resources, ~15 units, +260 seconds
//--------------------------------------------------------------------------------------------------
// Hard
//  APM: 400
//  scouts: starts early game
//  detect: middle game or after being attacked by cloakers
//  wave 1: 700 resources, ~8 units, 390 seconds
//  wave 2: 1200 resources, ~12 units, +300 seconds (690)
//  wave 3: 2100 resources, ~16 units, +300 seconds (990)
//  wave X: xxx resources, ~20 units, +240 seconds
//==================================================================================================

//==================================================================================================
//  OPEN
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//  AIGenericStock
//--------------------------------------------------------------------------------------------------
static void AIGenericStock (int player, int state1, int state2) {
    AIWaveMerge(player, c_waveMain, c_waveAttack);
    AISetAttackState(player, e_attackState_Attack);
    AISetMainState(player, state1, state2);
    AIResetUserData(player);
}

//--------------------------------------------------------------------------------------------------
//  AIEnableStockOpen
//--------------------------------------------------------------------------------------------------
static bool AIEnableStockOpen (int player, string peonType, int time, bool veryEasy, int nextState) {
    AIEnableStock(player);
    if (veryEasy) {
        if (AITechCount(player, peonType, c_techCountCompleteOnly) >= 8) {
            AISetGasPeonCountOverride(player, c_townMain, 2);
        }
        else {
            AISetGasPeonCountOverride(player, c_townMain, 0);
        }
    }
    if (AIGetTime() < time) {
        return true;
    }
    AIGenericStock(player, nextState, e_mainSubState_GndA);
    return false;
}

bool AIEnableVeryEasyStockOpen (int player, string peonType) {
    return AIEnableStockOpen(player, peonType, 480, true, e_mainState_Mid0);
}
bool AIEnableEasyStockOpen (int player, string peonType) {
    return AIEnableStockOpen(player, peonType, 450, false, e_mainState_Mid1);
}
bool AIEnableMediumStockOpen (int player, string peonType) {
    return AIEnableStockOpen(player, peonType, 420, false, e_mainState_Mid2);
}
bool AIEnableHardStockOpen (int player, string peonType) {
    return AIEnableStockOpen(player, peonType, 390, false, e_mainState_Mid3);
}

//==================================================================================================
//  MID A
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//  AIVeryEasyHarvest
//--------------------------------------------------------------------------------------------------
void AIVeryEasyHarvest (int player, string peonType, int count) {
    if (AIHasRes(player, 0, 200) || AITechCount(player, peonType, c_techCountCompleteOnly) < 8) {
        AISetGasPeonCountOverride(player, c_townMain, c_defaultGasPeonCount);
    }
    else {
        AISetGasPeonCountOverride(player, c_townMain, count);
    }
}

//--------------------------------------------------------------------------------------------------
//    AIEnableStockMidA
//--------------------------------------------------------------------------------------------------
static bool AIEnableStockMidA (int player, string peonType, int count, bool veryEasy, int time, int nextState) {
    AIEnableStock(player);
    if (veryEasy) {
        AIVeryEasyHarvest(player, peonType, count);
    }
    if (AIGetTime() < time) {
        return true;
    }
    AIGenericStock(player, nextState, e_mainSubState_GndB);
    return false;
}

bool AIEnableVeryEasyStockMidA (int player, string peonType, int count) {
    return AIEnableStockMidA(player, peonType, count, true, 840, e_mainState_Mid0);
}
bool AIEnableEasyStockMidA (int player) {
    return AIEnableStockMidA(player, "", 0, false, 790, e_mainState_Mid1);
}
bool AIEnableMediumStockMidA (int player) {
    return AIEnableStockMidA(player, "", 0, false, 740, e_mainState_Mid2);
}
bool AIEnableHardStockMidA (int player) {
    return AIEnableStockMidA(player, "", 0, false, 690, e_mainState_Mid3);
}

//==================================================================================================
//  MID B
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//    AIEnableStockMidB
//--------------------------------------------------------------------------------------------------
bool AIEnableStockMidB (int player, string peonType, int count, bool veryEasy, int time, int nextState) {
    AIEnableStock(player);
    if (veryEasy) {
        AIVeryEasyHarvest(player, peonType, count);
    }
    if (AIGetTime() < time) {
        return true;
    }
    AIGenericStock(player, nextState, e_mainSubState_GndA);
    AISetFlag(player, e_flagsScouting, true);
    AISetFlag(player, e_flagsClearObs, true);
    return false;
}
bool AIEnableVeryEasyStockMidB (int player, string peonType, int count) {
    return AIEnableStockMidB(player, peonType, count, true, 1200, e_mainState_Late0);
}
bool AIEnableEasyStockMidB (int player, string peonType, int count) {
    return AIEnableStockMidB(player, peonType, count, false, 1130, e_mainState_Late1);
}
bool AIEnableMediumStockMidB (int player, string peonType, int count) {
    return AIEnableStockMidB(player, peonType, count, false, 1060, e_mainState_Late2);
}
bool AIEnableHardStockMidB (int player, string peonType, int count) {
    return AIEnableStockMidB(player, peonType, count, false, 990, e_mainState_Late3);
}

//==================================================================================================
//  LATE
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//    AIEnableStockLate
//--------------------------------------------------------------------------------------------------
static bool AIEnableStockLate (int player, string peonType, int count, int time) {
    AIEnableStock(player);
    AIVeryEasyHarvest(player, peonType, count);
    if (AIGetTime() % time < 5) {
        return true;
    }
    AIWaveMerge(player, c_waveMain, c_waveAttack);
    AISetAttackState(player, e_attackState_Attack);
    AIResetUserData(player);
    return false;
}

bool AIEnableVeryEasyStockLate (int player, string peonType, int count) {
    return AIEnableStockLate(player, peonType, count, 300);
}
bool AIEnableEasyStockLate (int player, string peonType, int count) {
    return AIEnableStockLate(player, peonType, count, 280);
}
bool AIEnableMediumStockLate (int player, string peonType, int count) {
    return AIEnableStockLate(player, peonType, count, 260);
}
bool AIEnableHardStockLate (int player, string peonType, int count) {
    return AIEnableStockLate(player, peonType, count, 240);
}

//==================================================================================================
//  OTHER
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//    AIPickFrom2
//--------------------------------------------------------------------------------------------------
string AIPickFrom2 (string a, string b) {
    int roll = RandomInt(1,2);
    if (roll == 1) {
        return a;
    }
    return b;
}

//--------------------------------------------------------------------------------------------------
//    AIPickFrom3
//--------------------------------------------------------------------------------------------------
string AIPickFrom3 (string a, string b, string c) {
    int roll = RandomInt(1,3);
    if (roll == 1) {
        return a;
    }
    if (roll == 2) {
        return b;
    }
    return c;
}

//--------------------------------------------------------------------------------------------------
//    AISetStockUserData
//--------------------------------------------------------------------------------------------------
void AISetStockUserData (int player) {
    int index = 1;   
    while (index <= 10) {
        AISetStock(player, AIGetUserInt(player, index), AIGetUserString(player, index));
        index = index + 1;
    }
}

//==================================================================================================
//  TECH
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//    TerranTechUp
//--------------------------------------------------------------------------------------------------
void TerranTechUp (int player, int tier) {

    AISetStock( player, 1, c_TB_Barracks );
    AISetStock( player, 1, c_TB_Refinery );
    AISetStock( player, 1, c_TB_BarracksTechLab );
   
    if (tier >= 2) {
        AISetStock( player, 1, c_TB_Factory );
        AISetStock( player, 1, c_TB_FactoryTechLab );
       
        if (tier >= 3) {
            AISetStock( player, 1, c_TB_Starport );
            AISetStock( player, 1, c_TB_StarportTechLab );
        }
    }

    AISetStock( player, 1, c_TB_EngineeringBay );

    if (tier >= 2) {
        AISetStock( player, 1, c_TB_SensorTower );
        AISetStock( player, 1, c_TB_GhostAcademy );
        AISetStock( player, 1, c_TB_MercCompound );
       
        if (tier >= 3) {
            AISetStock( player, 1, c_TB_Armory );

            if (tier >= 4) {
                AISetStock( player, 1, c_TB_FusionCore );
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
//    ZergTechUp
//--------------------------------------------------------------------------------------------------
void ZergTechUp (int player, int tier) {

    AISetStock( player, 1, c_ZB_SpawningPool );
    AISetStock( player, 1, c_ZB_Extractor );
    AISetStock( player, 1, c_ZB_EvolutionChamber );
    AISetStock( player, 1, c_ZB_HydraliskDen );

    AISetStock( player, 1, c_ZR_HydraliskSpeed );

    if (tier >= 2) {
        AISetStock( player, 1, c_ZR_OverseerSpeed );
        AISetStock( player, 1, c_ZB_BanelingNest );

        if (tier >= 3) {
            AISetStock( player, 1, c_ZB_RoachWarren );
            AISetStock( player, 1, c_ZB_Spire );
            AISetStock( player, 1, c_ZB_InfestationPit );

            if (tier >= 4) {
                AISetStock( player, 1, c_ZR_OverlordTransport );
                AISetStock( player, 1, c_ZB_UltraliskCavern );
                AISetStock( player, 1, c_ZB_LurkerDen );
                AISetStock( player, 1, c_ZB_GreaterSpire );
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
//    ProtossTechUp
//--------------------------------------------------------------------------------------------------
void ProtossTechUp (int player, int tier) {

    AISetStock( player, 1, c_PB_Gateway );
    AISetStock( player, 1, c_PB_Forge );
    AISetStock( player, 1, c_PB_Assimilator );

    if (tier >= 3) {
        AISetStock( player, 1, c_PB_RoboticsFacility );
        AISetStock( player, 1, c_PB_Stargate );
        AISetStock( player, 1, c_PB_TwilightCouncil );

        if (tier >= 4) {
            AISetStock( player, 1, c_PB_TemplarArchives );
            AISetStock( player, 1, c_PB_RoboticsBay );
            AISetStock( player, 1, c_PB_DarkShrine );
            AISetStock( player, 1, c_PB_FleetBeacon );
        }
    }
}

MisterTea



Protoss0.galaxy
//==================================================================================================
//  Protoss Melee Very Easy
//==================================================================================================

static void LateGround (int player);

//--------------------------------------------------------------------------------------------------
//  ProtossOpenGnd0
//--------------------------------------------------------------------------------------------------
void ProtossOpenGnd0 (int player) {
    AIClearStock(player);

    AISetStock( player, 1, c_PB_Nexus );
    AISetStock( player, 8, c_PU_Probe );
    AISetStock( player, 1, c_PB_Pylon );
   
    // around 100 resources in about 2 units
    AISetStock( player, 1, c_PU_Zealot );
    ProtossTechUp(player, 1);
   
    if (AIEnableVeryEasyStockOpen(player, c_PU_Probe)) {
        return;
    }

    // around 300 resources in about 3 unit
    AIAddStringInt(player, c_PU_Stalker, 1);
    AIAddStringInt(player, AIPickFrom2(c_PU_Zealot, c_PU_Disruptor), 1);
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidGndA
//--------------------------------------------------------------------------------------------------
static void ProtossMidGndA (int player) {
    AIClearStock(player);

    AIDefaultEconomy(player, c_PB_Nexus, c_PB_Assimilator, c_PB_Pylon, c_PU_Probe, 8, c_stockAlways);

    if (AISawCloakedUnit(player)) {
        AISetStock( player, 2, c_PB_PhotonCannon );
        AISetStock( player, 1, c_PU_Observer );
    }

    AISetStockUserData(player);
    ProtossTechUp(player, 2);

    if (AIEnableVeryEasyStockMidA(player, c_PU_Probe, 4)) {
        return;
    }

    // around 600 resources in about 4 unit
    AIAddStringInt(player, c_PU_Stalker, 1);
    AIAddStringInt(player, AIPickFrom2(c_PU_Zealot, c_PU_Disruptor), 1);
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidGndB
//--------------------------------------------------------------------------------------------------
static void ProtossMidGndB (int player) {
    AIClearStock(player);

    AIDefaultEconomy(player, c_PB_Nexus, c_PB_Assimilator, c_PB_Pylon, c_PU_Probe, 8, c_stockAlways);

    if (AISawCloakedUnit(player)) {
        AISetStock( player, 2, c_PB_PhotonCannon );
        AISetStock( player, 1, c_PU_Observer );
    }

    AISetStockUserData(player);
    ProtossTechUp(player, 3);

    if (AIEnableVeryEasyStockMidB(player, c_PU_Probe, 4)) {
        return;
    }

    // around 900 resources in about 5 unit
    LateGround(player);
}

//--------------------------------------------------------------------------------------------------
//  LateGround
//--------------------------------------------------------------------------------------------------
static void LateGround (int player) {
    AIAddStringInt(player, c_PU_Stalker, 1);
    AIAddStringInt(player, c_PU_Disruptor, 1);
    AIAddStringInt(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
    AIAddStringInt(player, AIPickFrom2(c_PU_Zealot, c_PU_Disruptor), 1);
}

//--------------------------------------------------------------------------------------------------
//  LateGnd
//--------------------------------------------------------------------------------------------------
static void LateGnd (int player) {
    AIClearStock(player);

    AIDefaultEconomy(player, c_PB_Nexus, c_PB_Assimilator, c_PB_Pylon, c_PU_Probe, 8, c_stockAlways);
    AIDefaultExpansion(player, c_PB_Nexus, 6000, 0, c_expandDefault);

    AISetStockUserData(player);
    AISetStock( player, 4, c_PU_Observer );
    AISetStock( player, 1, c_PU_WarpPrism );
    ProtossTechUp(player, 4);
    AISetStock( player, 4, c_PU_Observer );

    if (AIEnableVeryEasyStockLate(player, c_TU_SCV, 4)) {
        return;
    }
    LateGround(player);
}

//--------------------------------------------------------------------------------------------------
//  ProtossOpenAir0
//--------------------------------------------------------------------------------------------------
void ProtossOpenAir0 (int player) {
    AIClearStock(player);

    AISetStock( player, 1, c_PB_Nexus );
    AISetStock( player, 8, c_PU_Probe );
    AISetStock( player, 1, c_PB_Pylon );
    AISetStock( player, 1, c_PB_Assimilator );
    AISetStock( player, 1, c_PB_Gateway );
    AISetStock( player, 1, c_PB_CyberneticsCore );
    AISetStock( player, 1, c_PB_Stargate );
    AISetStock( player, 1, c_PU_VoidRay );
    AISetStock( player, 2, c_PU_Stalker );

    AIEnableStock(player);
//stock 3

    //---------------------------------------------------------

    if (AITechCount(player, c_PU_VoidRay, c_techCountCompleteOnly) < 1) {
        return;
    }
    AIWaveMerge(player, c_waveMain, c_waveAttack);
    AISetAttackState(player, e_attackState_Attack);
    AISetMainState(player, e_mainState_Mid1, e_mainSubState_AirA);
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidAirA
//--------------------------------------------------------------------------------------------------
static void ProtossMidAirA (int player) {
    AIClearStock(player);

    AIDefaultEconomy(player, c_PB_Nexus, c_PB_Assimilator, c_PB_Pylon, c_PU_Probe, 8, c_stockAlways);

    AISetStock( player, 2, c_PU_Stalker );
    AISetStock( player, 1, c_PU_VoidRay );

    AISetStock( player, 2, c_PB_Pylon );
    AISetStock( player, 1, c_PB_RoboticsFacility );

    AISetStock( player, 1, c_PU_Phoenix );
    AISetStock( player, 1, c_PU_Observer );
    AISetStock( player, 1, c_PU_WarpPrism );
    AISetStock( player, 2, c_PU_VoidRay );

    AISetStock( player, 1, c_PB_Forge );

    AIEnableStock(player);

    //---------------------------------------------------------

    if (AIGetTime() < 700) {
        return;
    }
    AIWaveMerge(player, c_waveMain, c_waveAttack);
    AISetAttackState(player, e_attackState_Attack);
    AISetMainState(player, e_mainState_Mid1, e_mainSubState_AirB);
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidAirB
//--------------------------------------------------------------------------------------------------
static void ProtossMidAirB (int player) {
    AIClearStock(player);

    AIDefaultEconomy(player, c_PB_Nexus, c_PB_Assimilator, c_PB_Pylon, c_PU_Probe, 8, c_stockAlways);
    AIDefaultExpansion(player, c_PB_Nexus, 6000, 1000, c_expandDefault);

    AISetStock( player, 2, c_PU_Stalker );
    AISetStock( player, 1, c_PU_VoidRay );
    AISetStock( player, 2, c_PB_Pylon );
    AISetStock( player, 1, c_PU_Phoenix );
    AISetStock( player, 1, c_PU_Observer );
    AISetStock( player, 1, c_PU_WarpPrism );
    AISetStock( player, 3, c_PU_VoidRay );
    AISetStock( player, 2, c_PU_Observer );
    AISetStock( player, 2, c_PU_Phoenix );

    AISetStock( player, 3, c_PB_PhotonCannon );

    AIEnableStock(player);

    //---------------------------------------------------------

    if (AIGetTime() < 1200) {
        return;
    }
    AIWaveMerge(player, c_waveMain, c_waveAttack);
    AISetAttackState(player, e_attackState_Attack);
    AISetMainState(player, e_mainState_Late1, e_mainSubState_AirA);

    AISetFlag(player, e_flagsScouting, true);
    AISetFlag(player, e_flagsClearObs, true);
}

//--------------------------------------------------------------------------------------------------
//  LateAir
//--------------------------------------------------------------------------------------------------
static void LateAir (int player) {
    AIClearStock(player);

    AIDefaultEconomy(player, c_PB_Nexus, c_PB_Assimilator, c_PB_Pylon, c_PU_Probe, 8, c_stockAlways);
    AIDefaultExpansion(player, c_PB_Nexus, 6000, 1000, c_expandDefault);

    AISetStock( player, 2, c_PU_Stalker );
    AISetStock( player, 1, c_PU_VoidRay );
    AISetStock( player, 2, c_PB_Pylon );
    AISetStock( player, 1, c_PU_Phoenix );
    AISetStock( player, 1, c_PU_Observer );
    AISetStock( player, 1, c_PU_WarpPrism );
    AISetStock( player, 3, c_PU_VoidRay );
    AISetStock( player, 2, c_PU_Observer );
    AISetStock( player, 2, c_PU_Phoenix );
    AISetStock( player, 1, c_PU_Carrier );
    AISetStock( player, 4, c_PU_Observer );

    AIEnableStock(player);

    //---------------------------------------------------------

    if (AIGetTime() % 300 > 5) {
        return;
    }
    AIWaveMerge(player, c_waveMain, c_waveAttack);
    AISetAttackState(player, e_attackState_Attack);

    AISetFlag(player, e_flagsScouting, true);
    AISetFlag(player, e_flagsClearObs, true);
}

//--------------------------------------------------------------------------------------------------
//  ProtossMid0
//--------------------------------------------------------------------------------------------------
void ProtossMid0 (int player) {
    int mainSubState = AIState(player, e_mainSubState);
    if (mainSubState == e_mainSubState_GndA)          { ProtossMidGndA(player);  }
    else if (mainSubState == e_mainSubState_GndB)     { ProtossMidGndB(player);  }
    else if (mainSubState == e_mainSubState_AirA)     { ProtossMidAirA(player);  }
    else if (mainSubState == e_mainSubState_AirB)     { ProtossMidAirB(player);  }
    else { ErrorMeleeScript(player, "Invalid Mid mainSubState"); }
}

//--------------------------------------------------------------------------------------------------
//  ProtossLate0
//--------------------------------------------------------------------------------------------------
void ProtossLate0 (int player) {
    int mainSubState = AIState(player, e_mainSubState);
    if (mainSubState == e_mainSubState_GndA)          { LateGnd(player);  }
    else if (mainSubState == e_mainSubState_AirA)     { LateAir(player);  }
    else { ErrorMeleeScript(player, "Invalid Late mainSubState"); }
}

MisterTea



Protoss.galaxy
//==================================================================================================
//  Protoss Melee AI
//==================================================================================================

include "TriggerLibs/Protoss0"

//--------------------------------------------------------------------------------------------------
//  Counter-Attack Units
//--------------------------------------------------------------------------------------------------
static void InitCounters (int player) {
//xxx lots of old data -- needs to be totally redone
    // versus Protoss
    AICounterUnit(player, c_PB_PhotonCannon,         0.40, c_PU_Immortal );
    AICounterUnit(player, c_PU_Zealot,              1.00, c_PU_Zealot   );
    AICounterUnit(player, c_PU_Stalker,             1.00, c_PU_Zealot   );
    AICounterUnit(player, c_PU_Immortal,            2.00, c_PU_Zealot   );
    AICounterUnit(player, c_PU_Disruptor,           0.75, c_PU_Zealot   );
    AICounterUnit(player, c_PU_HighTemplar,         1.00, c_PU_Stalker  );
    AICounterUnit(player, c_PU_DarkTemplar,         2.00, c_PU_Zealot   );
    AICounterUnit(player, c_PU_Archon,              1.00, c_PU_Immortal );
    AICounterUnit(player, c_PU_Colossus,            1.00, c_PU_Immortal );
    AICounterUnit(player, c_PU_VoidRay,             1.50, c_PU_Phoenix  );
    AICounterUnit(player, c_PU_Phoenix,             0.40, c_PU_Carrier  );
    AICounterUnit(player, c_PU_Carrier,             1.67, c_PU_VoidRay  );

    // versus Terran
    AICounterUnit(player, c_TB_Bunker,              0.40, c_PU_Immortal );
    AICounterUnit(player, c_TU_Marine,              0.50, c_PU_Zealot   );
    AICounterUnit(player, c_TU_Reaper,              1.00, c_PU_Zealot   );
    AICounterUnit(player, c_TU_Marauder,            1.00, c_PU_Zealot   );
    AICounterUnit(player, c_TU_Ghost,               1.00, c_PU_Stalker  );
    AICounterUnit(player, c_TU_Hellion,             0.25, c_PU_Immortal );
    AICounterUnit(player, c_TU_SiegeTank_Alias,     2.00, c_PU_Zealot   );
    AICounterUnit(player, c_TU_Thor,                3.00, c_PU_Zealot   );
    AICounterUnit(player, c_TU_Viking_Alias,        1.00, c_PU_Zealot   );
    AICounterUnit(player, c_TU_Viking_Alias,        0.50, c_PU_Phoenix  );
    AICounterUnit(player, c_TU_Banshee,             0.50, c_PU_Phoenix  );
    AICounterUnit(player, c_TU_BattlecruiserClass,  2.00, c_PU_VoidRay  );

    // versus Zerg
    AICounterUnit(player, c_ZB_SpineCrawler,        0.33, c_PU_Immortal );
    AICounterUnit(player, c_ZU_Zergling,            0.25, c_PU_Zealot   );
    AICounterUnit(player, c_ZU_Roach,               0.80, c_PU_Stalker  );
    AICounterUnit(player, c_ZU_Hydralisk,           0.50, c_PU_Stalker  );
    AICounterUnit(player, c_ZU_Lurker,              1.00, c_PU_Immortal );
    AICounterUnit(player, c_ZU_Infestor,            0.50, c_PU_Stalker  );
    AICounterUnit(player, c_ZU_Ultralisk,           1.00, c_PU_Stalker  );
    AICounterUnit(player, c_ZU_Mutalisk,            0.80, c_PU_Stalker  );
    AICounterUnit(player, c_ZU_Corruptor,           0.50, c_PU_Stalker  );
    AICounterUnit(player, c_ZU_BroodLord,       0.50, c_PU_Phoenix  );
}

//--------------------------------------------------------------------------------------------------
//  ProtossSubStateName
//--------------------------------------------------------------------------------------------------
string ProtossSubStateName (int state) {
    // TODO Call the individual difficulties to return a real substate name
    return "-" + IntToString(state) + "-";
}

//--------------------------------------------------------------------------------------------------
//  DebugCallbackProt
//--------------------------------------------------------------------------------------------------
void DebugCallbackProt (int player) {
    DebugAI("=====PROTOSS=====\n");
    DebugMelee(player);
    DebugAI("e_mainState = "        + MainStateName(AIState(player, e_mainState))           +
            ", e_mainSubState = "   + ProtossSubStateName(AIState(player, e_mainSubState))  +
            ", e_attackState = "    + AttackStateName(AIState(player, e_attackState))
    );
}

//--------------------------------------------------------------------------------------------------
//  AINewUnitProt
//--------------------------------------------------------------------------------------------------
void AINewUnitProt (int player, unit u) {
    int obs;
    string type = UnitGetType(u);

    // ignored units
    //
    if (UnitTypeTestAttribute(type, c_unitAttributeStructure)) {
        return;
    }
    if (UnitTypeTestFlag(type, c_unitFlagWorker)) {
        return;
    }
   
    if (type == c_PU_WarpPrism) {
        AIAddToExtraScoutGroup(player, u);
        return;
    }
   
    // detector
    //
    if (type == c_PU_Observer) {
        AINewDetector(player, u, true);
        return;
    }

    // clear obstacle units
    //
    if (AIWaveNeedClearObsUnits(player)) {
        if (type == c_PU_Zealot || type == c_PU_Stalker || type == c_PU_DarkTemplar) {
            AIMergeUnit(player, u, AIWaveGet(player, c_waveClearObs));
            return;
        }
    }

    // main wave units
    //
    AINewUnitDefault(player, u);
}

//--------------------------------------------------------------------------------------------------
//  AIGetScoutProt
//--------------------------------------------------------------------------------------------------
unit AIGetScoutProt (int player, int index, unit prev) {
    unit obs;
   
    if (!AIGetFlag(player, e_flagsScouting)) {
        return c_nullUnit;
    }
    if (UnitGetType(prev) == c_PU_Observer) {
        return prev;
    } 
    obs = AIGrabUnit(player, c_PU_Observer, c_prioScout, null);
    if (obs) {
        return obs;
    }
    if (prev) {
        return prev;
    }
    if (AIGetFlag(player, e_flagsLateScout)) {
        return c_nullUnit;
    }
    return AIGrabUnit(player, c_PU_Probe, c_prioScout, null);
}

//--------------------------------------------------------------------------------------------------
//  AIEarlyDefScoutProt
//--------------------------------------------------------------------------------------------------
unit AIEarlyDefScoutProt (int player, unit prev) {
    unit obs;
   
    if (!AIGetFlag(player, e_flagsEarlyDefScout)) {
        return c_nullUnit;
    }
    if (UnitGetType(prev) == c_PU_Observer) {
        return prev;
    }
    obs = AIGrabUnit(player, c_PU_Observer, c_prioScout, null);
    if (obs) {
        return obs;
    }
    if (UnitGetType(prev) == c_PU_Zealot) {
        return prev;
    }
    obs = AIGrabUnit(player, c_PU_Zealot, c_prioScout, null);
    if (obs) {
        return obs;
    }
    if (prev) {
        return prev;
    }
    return AIGrabUnit(player, c_PU_Probe, c_prioScout, null);
}

//--------------------------------------------------------------------------------------------------
//  AIWaveThinkProt
//--------------------------------------------------------------------------------------------------
void AIWaveThinkProt (int player, wave w, int type) {
    AIWaveThinkDefault(player, w, type);
}

//--------------------------------------------------------------------------------------------------
//  Protoss Init
//--------------------------------------------------------------------------------------------------
static void ProtossInit (int player) {
    int state;

    InitCounters(player);
    AISetNumScouts(player, 1);
    AISetFlag(player, e_flagsScouting, false);
    AISetFlag(player, e_flagsEarlyDefScout, false);
    AISetPowerBuilding(player, c_PB_Pylon);
    AITransportSetPanic(player, 0.27); // just below max shield threshold

    if (AIGetGroundStartLocs(PlayerStartLocation(player)) > 0) {
        state = AIDiffEnum(player, e_mainState_OpenGnd0);
    }
    else {
        state = AIDiffEnum(player, e_mainState_OpenAir0);
    }
    AISetMainState(player, state, e_mainSubState_Unset);
}

//--------------------------------------------------------------------------------------------------
//  AIMeleeProt
//--------------------------------------------------------------------------------------------------
void AIMeleeProt (int player) {   
    int mainState = AIState(player, e_mainState);

    if (mainState == e_mainState_Init)              { ProtossInit(player);     }

    else if (mainState == e_mainState_OpenGnd0)     { ProtossOpenGnd0(player); }
    //else if (mainState == e_mainState_OpenGnd1)     { ProtossOpenGnd1(player); }
    //else if (mainState == e_mainState_OpenGnd2)     { ProtossOpenGnd2(player); }
    //else if (mainState == e_mainState_OpenGnd3)     { ProtossOpenGnd3(player); }
    //else if (mainState == e_mainState_OpenGnd4)     { ProtossOpenGnd4(player); }
    //else if (mainState == e_mainState_OpenGnd5)     { ProtossOpenGnd5(player); }

    else if (mainState == e_mainState_OpenAir0)     { ProtossOpenAir0(player); }
    //else if (mainState == e_mainState_OpenAir1)     { ProtossOpenAir1(player); }
    //else if (mainState == e_mainState_OpenAir2)     { ProtossOpenAir2(player); }
    //else if (mainState == e_mainState_OpenAir3)     { ProtossOpenAir3(player); }
    //else if (mainState == e_mainState_OpenAir4)     { ProtossOpenAir4(player); }
    //else if (mainState == e_mainState_OpenAir5)     { ProtossOpenAir5(player); }

    else if (mainState == e_mainState_Mid0)         { ProtossMid0(player);     }
    //else if (mainState == e_mainState_Mid1)         { ProtossMid1(player);     }
    //else if (mainState == e_mainState_Mid2)         { ProtossMid2(player);     }
    //else if (mainState == e_mainState_Mid3)         { ProtossMid3(player);     }
    //else if (mainState == e_mainState_Mid4)         { ProtossMid4(player);     }
    //else if (mainState == e_mainState_Mid5)         { ProtossMid5(player);     }

    else if (mainState == e_mainState_Late0)        { ProtossLate0(player);    }
    //else if (mainState == e_mainState_Late1)        { ProtossLate1(player);    }
    //else if (mainState == e_mainState_Late2)        { ProtossLate2(player);    }
    //else if (mainState == e_mainState_Late3)        { ProtossLate3(player);    }
    //else if (mainState == e_mainState_Late4)        { ProtossLate4(player);    }
    //else if (mainState == e_mainState_Late5)        { ProtossLate5(player);    }

    else if (mainState == e_mainState_Off)          { EndMeleeScript(player);  }
    else if (mainState == e_mainState_Disabled)     {                          }
   
    else { ErrorMeleeScript(player, "Invalid mainState"); }
}

MisterTea


RequirementsAI.galaxy
//--------------------------------------------------------------------------------------------------
//  AI Requirements Functions
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//  Neutral Units
//--------------------------------------------------------------------------------------------------
const string c_NU_Minerals                = "MineralField";
const string c_NU_HighYieldMinerals       = "HighYieldMineralField";
const string c_NU_VespeneGeyser           = "VespeneGeyser";

//--------------------------------------------------------------------------------------------------
//  Protoss Units
//--------------------------------------------------------------------------------------------------
const string c_PU_Archon                  = "Archon";
const string c_PU_Carrier                 = "Carrier";
const string c_PU_Colossus                = "Colossus";
const string c_PU_DarkTemplar             = "DarkTemplar";
const string c_PU_HighTemplar             = "HighTemplar";
const string c_PU_Immortal                = "Immortal";
const string c_PU_Mothership              = "Mothership";
const string c_PU_Disruptor               = "Disruptor";
const string c_PU_Observer                = "Observer";
const string c_PU_Phoenix                 = "Phoenix";
const string c_PU_Probe                   = "Probe";
const string c_PU_Stalker                 = "Stalker";
const string c_PU_VoidRay                 = "VoidRay";
const string c_PU_WarpPrism               = "WarpPrism";
const string c_PU_WarpPrismPhasing        = "WarpPrismPhasing";
const string c_PU_Zealot                  = "Zealot";

//--------------------------------------------------------------------------------------------------
//  Protoss Buildings
//--------------------------------------------------------------------------------------------------
const string c_PB_Assimilator             = "Assimilator";
const string c_PB_CyberneticsCore         = "CyberneticsCore";
const string c_PB_DarkShrine              = "DarkShrine";
const string c_PB_Obelisk                 = "Obelisk";
const string c_PB_FleetBeacon             = "FleetBeacon";
const string c_PB_Forge                   = "Forge";
const string c_PB_Gateway                 = "Gateway";
const string c_PB_Nexus                   = "Nexus";
const string c_PB_PhotonCannon            = "PhotonCannon";
const string c_PB_Pylon                   = "Pylon";
const string c_PB_RoboticsBay             = "RoboticsBay";
const string c_PB_RoboticsFacility        = "RoboticsFacility";
const string c_PB_Stargate                = "Stargate";
const string c_PB_WarpGate                = "WarpGate";
const string c_PB_TemplarArchives         = "TemplarArchive";
const string c_PB_TwilightCouncil         = "TwilightCouncil";

//--------------------------------------------------------------------------------------------------
//  Protoss Research Upgrades
//--------------------------------------------------------------------------------------------------
const string c_PR_AirArmor1               = "ProtossAirArmorsLevel1";
const string c_PR_AirArmor2               = "ProtossAirArmorsLevel2";
const string c_PR_AirArmor3               = "ProtossAirArmorsLevel3";
const string c_PR_AirWeapons1             = "ProtossAirWeaponsLevel1";
const string c_PR_AirWeapons2             = "ProtossAirWeaponsLevel2";
const string c_PR_AirWeapons3             = "ProtossAirWeaponsLevel3";
const string c_PR_ColossusRange           = "ExtendedThermalLance";         // ExtendedThermalLance
const string c_PR_GroundArmor1            = "ProtossGroundArmorsLevel1";
const string c_PR_GroundArmor2            = "ProtossGroundArmorsLevel2";
const string c_PR_GroundArmor3            = "ProtossGroundArmorsLevel3";
const string c_PR_GroundWeapons1          = "ProtossGroundWeaponsLevel1";
const string c_PR_GroundWeapons2          = "ProtossGroundWeaponsLevel2";
const string c_PR_GroundWeapons3          = "ProtossGroundWeaponsLevel3";
const string c_PR_HighTemplarEnergy       = "HighTemplarKhaydarinAmulet";   // KhaydarinAmulet
const string c_PR_HighTemplarPsiStorm     = "PsiStormTech";                 // PsiStorm
const string c_PR_DisruptorHallucination  = "haltech";                      // Hallucination
const string c_PR_ObserverSpeed           = "ObserverGraviticBooster";      // GraviticBoosters
const string c_PR_Shields1                = "ProtossShieldsLevel1";
const string c_PR_Shields2                = "ProtossShieldsLevel2";
const string c_PR_Shields3                = "ProtossShieldsLevel3";
const string c_PR_StalkerBlink            = "BlinkTech";                    // Blink
const string c_PR_WarpGateResearch        = "WarpGateResearch";             // gateway -> warp gate
const string c_PR_WarpPrismSpeed          = "GraviticDrive";                // GraviticDrive
const string c_PR_ZealotCharge            = "Charge";                       // Charge

//--------------------------------------------------------------------------------------------------
//  Terran Units
//--------------------------------------------------------------------------------------------------
const string c_TU_AutoTurret              = "AutoTurret";
const string c_TU_Banshee                 = "Banshee";
const string c_TU_Battlecruiser           = "Battlecruiser";
const string c_TU_BattlecruiserClass      = "Alias_BattlecruiserClass";
const string c_TU_BattlecruiserDefensive  = "BattlecruiserDefensiveMatrix";
const string c_TU_BattlecruiserMissile    = "BattlecruiserHurricane";
const string c_TU_BattlecruiserYamato     = "BattlecruiserYamato";
const string c_TU_D8Charge                = "MagneticMine";
const string c_TU_Ghost                   = "Ghost";
const string c_TU_Hellion                 = "Hellion";
const string c_TU_Marauder                = "Marauder";
const string c_TU_Marine                  = "Marine";
const string c_TU_Medivac                 = "Medivac";
const string c_TU_Raven                   = "Raven";
const string c_TU_Reaper                  = "Reaper";
const string c_TU_SCV                     = "SCV";
const string c_TU_SiegeTank               = "SiegeTank";
const string c_TU_SiegeTankSieged         = "SiegeTankSieged";
const string c_TU_SiegeTank_Alias         = "Alias_SiegeTank";
const string c_TU_Thor                    = "Thor";
const string c_TU_Viking                  = "VikingFighter";
const string c_TU_VikingAir               = "VikingFighter";
const string c_TU_VikingGnd               = "VikingAssault";
const string c_TU_Viking_Alias            = "Alias_Viking";

//--------------------------------------------------------------------------------------------------
//  Terran Buildings
//--------------------------------------------------------------------------------------------------
const string c_TB_Armory                  = "Armory";
const string c_TB_Barracks                = "Barracks";
const string c_TB_BarracksReactor         = "BarracksNuclearReactor";
const string c_TB_BarracksTechLab         = "BarracksTechLab";
const string c_TB_Bunker                  = "Bunker";
const string c_TB_CommandCenter           = "CommandCenter";
const string c_TB_CommandCenter_Alias     = "Alias_CommandCenter";
const string c_TB_EngineeringBay          = "EngineeringBay";
const string c_TB_Factory                 = "Factory";
const string c_TB_FactoryReactor          = "FactoryNuclearReactor";
const string c_TB_FactoryTechLab          = "FactoryTechLab";
const string c_TB_FusionCore              = "FusionCore";
const string c_TB_GenericTechLab          = "TechLab";
const string c_TB_GhostAcademy            = "GhostAcademy";
const string c_TB_MercCompound            = "MercCompound";
const string c_TB_MissileTurret           = "MissileTurret";
const string c_TB_NuclearReactor          = "NuclearReactor";
const string c_TB_OrbitalCommand          = "OrbitalCommand";
const string c_TB_PlanetaryFortress       = "PlanetaryFortress";
const string c_TB_Refinery                = "Refinery";
const string c_TB_SensorTower             = "SensorTower";
const string c_TB_Starport                = "Starport";
const string c_TB_StarportReactor         = "StarportNuclearReactor";
const string c_TB_StarportTechLab         = "StarportTechLab";
const string c_TB_SupplyDepot             = "SupplyDepot";
const string c_TB_SupplyDepot_Alias       = "Alias_SupplyDepot";

//--------------------------------------------------------------------------------------------------
//  Terran Research Upgrades
//--------------------------------------------------------------------------------------------------
const string c_TR_BansheeCloak          = "BansheeCloak";                   // CloakingField
const string c_TR_BattlecruiserEnergy   = "ColossalReactor";                // ColossalReactor
const string c_TR_BuildingArmor         = "TerranBuildingArmor";
const string c_TR_BunkerSpace           = "NeosteelFrame";                  // NeosteelFrame
const string c_TR_GhostCloak            = "PersonalCloaking";               // PersonalCloaking
const string c_TR_GhostEnergy           = "GhostMoebiusReactor";            // MoebiusReactor
const string c_TR_HellionDamage         = "HighCapacityBarrels";            // InfernalPreIgniter
const string c_TR_InfantryArmor1        = "TerranInfantryArmorsLevel1";
const string c_TR_InfantryArmor2        = "TerranInfantryArmorsLevel2";
const string c_TR_InfantryArmor3        = "TerranInfantryArmorsLevel3";
const string c_TR_InfantryWeapons1      = "TerranInfantryWeaponsLevel1";
const string c_TR_InfantryWeapons2      = "TerranInfantryWeaponsLevel2";
const string c_TR_InfantryWeapons3      = "TerranInfantryWeaponsLevel3";
const string c_TR_MarineShield          = "ShieldWall";                     // ShieldWall
const string c_TR_MarineStimPack        = "StimPack";                       // StimPack
const string c_TR_MedivacEnergy         = "MedivacCaduceusReactor";         // CaduceusReactor
const string c_TR_MissileTurretRange    = "TerranDefenseRangeBonus";        // DefenseRangeBonus
const string c_TR_RavenEnergy           = "TitanReactor";                   // TitanReactor
const string c_TR_RavenMissiles         = "HunterSeeker";                   // HunterSeekerMissiles
const string c_TR_ReaperSpeed           = "ReaperSpeed";                    // Reaper Speed Upgrade
const string c_TR_ShipPlating1          = "TerranShipArmorsLevel1";
const string c_TR_ShipPlating2          = "TerranShipArmorsLevel2";
const string c_TR_ShipPlating3          = "TerranShipArmorsLevel3";
const string c_TR_ShipWeapons1          = "TerranShipWeaponsLevel1";
const string c_TR_ShipWeapons2          = "TerranShipWeaponsLevel2";
const string c_TR_ShipWeapons3          = "TerranShipWeaponsLevel3";
const string c_TR_SiegeTankSiege        = "SiegeTech";                      // SiegeTech
const string c_TR_VehiclePlating1       = "TerranVehicleArmorsLevel1";
const string c_TR_VehiclePlating2       = "TerranVehicleArmorsLevel2";
const string c_TR_VehiclePlating3       = "TerranVehicleArmorsLevel3";
const string c_TR_VehicleWeapons1       = "TerranVehicleWeaponsLevel1";
const string c_TR_VehicleWeapons2       = "TerranVehicleWeaponsLevel2";
const string c_TR_VehicleWeapons3       = "TerranVehicleWeaponsLevel3";

//--------------------------------------------------------------------------------------------------
//  Zerg Units
//--------------------------------------------------------------------------------------------------

const string c_ZU_Baneling                = "Baneling";
const string c_ZU_BanelingEgg             = "BanelingEgg";
const string c_ZU_Broodling               = "Broodling";
const string c_ZU_Changeling              = "Changeling";
const string c_ZU_ChangelingZealot        = "ChangelingZealot";
const string c_ZU_ChangelingZergling      = "ChangelingZergling";
const string c_ZU_ChangelingZerglingWings = "ChangelingZerglingWings";
const string c_ZU_ChangelingMarineShield  = "ChangelingMarineShield";
const string c_ZU_ChangelingMarine        = "ChangelingMarine";
const string c_ZU_Cocoon                  = "MutaliskCocoon";
const string c_ZU_Corruptor               = "Corruptor";
const string c_ZU_CreepTumor              = "CreepTumor";
const string c_ZU_Drone                   = "Drone";
const string c_ZU_Hydralisk               = "Hydralisk";
const string c_ZU_Infestor                = "Infestor";
const string c_ZU_InfestedTerran          = "InfestedTerran";
const string c_ZU_InfestedTerranEgg      = "InfestedTerransEggPlacement";
const string c_ZU_Larva                   = "Larva";
const string c_ZU_Lurker                  = "Lurker";
const string c_ZU_LurkerEgg               = "LurkerEgg";
const string c_ZU_Mantaling               = "SwarmGuardianSpawn";
const string c_ZU_Mutalisk                = "Mutalisk";
const string c_ZU_Overlord                = "Overlord";
const string c_ZU_Overlord_Alias          = "Alias_Overlord";
const string c_ZU_OverlordCocoon          = "OverlordCocoon";
const string c_ZU_Overseer                = "Overseer";
const string c_ZU_Queen                   = "Queen";
const string c_ZU_Roach                   = "Roach";
const string c_ZU_BroodLord               = "BroodLord";
const string c_ZU_Ultralisk               = "Ultralisk";
const string c_ZU_Zergling                = "Zergling";

//----------------------------------------------------------------------------------------------------
//  Zerg Buildings
//--------------------------------------------------------------------------------------------------

const string c_ZB_BanelingNest            = "BanelingNest";
const string c_ZB_CreepTumor              = "CreepTumorBurrowed";
const string c_ZB_EvolutionChamber        = "EvolutionChamber";
const string c_ZB_Extractor               = "Extractor";
const string c_ZB_GreaterSpire            = "GreaterSpire";
const string c_ZB_Hatchery                = "Hatchery";
const string c_ZB_Hatchery_Alias          = "Alias_Hatchery";
const string c_ZB_Hive                    = "Hive";
const string c_ZB_HydraliskDen            = "HydraliskDen";
const string c_ZB_HydraliskDen_Alias      = "Alias_HydraliskDen";
const string c_ZB_InfestationPit          = "InfestationPit";
const string c_ZB_Lair                    = "Lair";
const string c_ZB_Lair_Alias              = "Alias_Lair";
const string c_ZB_LurkerDen               = "LurkerDen";
const string c_ZB_NydusNetwork            = "NydusNetwork";
const string c_ZB_NydusWorm               = "NydusCanal";
const string c_ZB_RoachWarren             = "RoachWarren";
const string c_ZB_SpawningPool            = "SpawningPool";
const string c_ZB_SpineCrawler            = "SpineCrawler";
const string c_ZB_SpineCrawlerUp          = "SpineCrawlerUprooted";
const string c_ZB_Spire                   = "Spire";
const string c_ZB_Spire_Alias             = "Alias_Spire";
const string c_ZB_SporeCrawler            = "SporeCrawler";
const string c_ZB_SporeCrawlerUp          = "SporeCrawlerUprooted";
const string c_ZB_UltraliskCavern         = "UltraliskCavern";

//--------------------------------------------------------------------------------------------------
//  Zerg Research Upgrades
//--------------------------------------------------------------------------------------------------

const string c_ZR_Burrow                  = "Burrow";                       // Burrow
const string c_ZR_BanelingSpeed           = "CentrificalHooks";             // CentrificalHooks
const string c_ZR_CorruptorAttack         = "EnduringCorruption";           // EnduringCorruption
const string c_ZR_FlyerAttacks1           = "ZergFlyerWeaponsLevel1";
const string c_ZR_FlyerAttacks2           = "ZergFlyerWeaponsLevel2";
const string c_ZR_FlyerAttacks3           = "ZergFlyerWeaponsLevel3";
const string c_ZR_FlyerCarapace1          = "ZergFlyerArmorsLevel1";
const string c_ZR_FlyerCarapace2          = "ZergFlyerArmorsLevel2";
const string c_ZR_FlyerCarapace3          = "ZergFlyerArmorsLevel3";
const string c_ZR_GroundCarapace1         = "ZergGroundArmorsLevel1";
const string c_ZR_GroundCarapace2         = "ZergGroundArmorsLevel2";
const string c_ZR_GroundCarapace3         = "ZergGroundArmorsLevel3";
const string c_ZR_HydraliskSpeed          = "hydraliskspeed";               // MuscularAugments
const string c_ZR_InfestorEnergy          = "MetasynapticNode";             // MetasynapticNode
const string c_ZR_InfestorSpell           = "Disease";                      // FungalInfestation
const string c_ZR_MeleeAttacks1           = "ZergMeleeWeaponsLevel1";
const string c_ZR_MeleeAttacks2           = "ZergMeleeWeaponsLevel2";
const string c_ZR_MeleeAttacks3           = "ZergMeleeWeaponsLevel3";
const string c_ZR_MissileAttacks1         = "ZergMissileWeaponsLevel1";
const string c_ZR_MissileAttacks2         = "ZergMissileWeaponsLevel2";
const string c_ZR_MissileAttacks3         = "ZergMissileWeaponsLevel3";
const string c_ZR_OverseerSpeed           = "overlordspeed";                // PneumatizedCarapace
const string c_ZR_OverlordTransport       = "overlordtransport";            // VentralSacks
const string c_ZR_QueenDamagePlague       = "RazorTech";                    // RazorPlague
const string c_ZR_RoachRegen              = "OrganicCarapace";              // OrganicCarapace
const string c_ZR_RoachSpeed              = "GlialReconstitution";          // GlialReconstitution
const string c_ZR_ZerglingHaste           = "zerglingattackspeed";          // AdrenalGlands
const string c_ZR_ZerglingSpeed           = "zerglingmovementspeed";        // MetabolicBoost

MisterTea

RequirementsAI.galaxy < suite


//--------------------------------------------------------------------------------------------------
//  Markers
//--------------------------------------------------------------------------------------------------
const string c_MK_Danger                    = "AI/Tactical/Danger";
const string c_MK_EMP                       = "Abil/EMP/AI";
const string c_MK_PhaseShift                = "Abil/PhaseShift/AI";
const string c_MK_ForceField                = "Abil/ForceField/AI";
const string c_MK_FungalGrowth              = "Abil/FungalGrowth/AI";
const string c_MK_GameFungalGrowth          = "Abil/FungalGrowth";
const string c_MK_GameNeuralParasite        = "Abil/NeuralParasite";
const string c_MK_InfestorDisease           = "Abil/InfestorDisease/AI";
const string c_MK_Lockdown                  = "Abil/Lockdown/AI";
const string c_MK_D8Charge                  = "Abil/D8Charge/AI";
const string c_MK_D8ChargeFlee              = "Abil/D8ChargeFlee/AI";
const string c_MK_MissilePods               = "Abil/MissilePods/AI";
const string c_MK_LeviathanCharge           = "Abil/LeviathanCharge/AI";
const string c_MK_NeuralParasite            = "Abil/NeuralParsite/AI";
const string c_MK_PsiStorm                  = "Abil/PsiStorm/AI";
const string c_MK_GravitonBeam              = "Abil/GravitonBeam/AI";
const string c_MK_SapStructure              = "Abil/SapStructure/AI";
const string c_MK_Snipe                     = "Abil/Snipe/AI";
const string c_MK_SeekerMissile             = "Abil/HunterSeekerMissile/AI";
const string c_MK_250mmStrikeCannons        = "Abil/250mmStrikeCannons/AI";
const string c_MK_UnstableMutation          = "Abil/UnstableMutation/AI";
const string c_MK_Yamato                    = "Abil/Yamato/AI";

//--------------------------------------------------------------------------------------------------
//  Buffs
//--------------------------------------------------------------------------------------------------
const string c_BF_FungalGrowth              = "FungalGrowth";
const string c_BF_GravitonBeam              = "GravitonPrison";
const string c_BF_Lockdown                  = "LockdownB";
const string c_BF_MutantLarvaTimer          = "SpawnMutantLarvaTimer";
const string c_BF_PersonalCloaking          = "PersonalCloaking";
const string c_BF_ProbeBuff                 = "ProtonCharge";
const string c_BF_PsionicShockwave          = "PsionicShockwaveBehaviorController";
const string c_BF_Stim                      = "stim";
const string c_BF_SupplyDrop                = "SupplyDrop";
const string c_BF_SwarmInfestation          = "SwarmInfestation";
const string c_BF_TemporalBuff              = "TemporalRiftUnit";
const string c_BF_UnstableMutation          = "UnstableMutation";

//--------------------------------------------------------------------------------------------------
//  Effects
//--------------------------------------------------------------------------------------------------
const string c_EF_AcidSporesArea            = "AcidSporesSearch";
const string c_EF_BaneBuilding              = "BanelingU2";
const string c_EF_BaneUnit                  = "BanelingU";
const string c_EF_ConsumeDNADamage          = "ConsumeDNADamage";
const string c_EF_ConsumeDNAPersistent      = "ConsumeDNACP";
const string c_EF_EMPArea                   = "EMP2A";
const string c_EF_HybridStunArea            = "HybridFAoESearch";
const string c_EF_InfestorDiseaseArea       = "InfestorDiseaseSearch";
const string c_EF_D8ChargeDmg               = "D8ChargeExplodeDamage";
const string c_EF_MissilePodArea            = "HurricaneMissileDamage";
const string c_EF_Nuke                      = "nuke";
const string c_EF_NukeDamage                = "nukeDamage";
const string c_EF_OdinBarrageArea           = "BarrageSearchArea";
const string c_EF_OmegaStormArea            = "OmegaStormSearchArea";
const string c_EF_PlasmaBlastDamage         = "HybridCPlasmaBlastDamage";
const string c_EF_ProbeBuffArea             = "ProbeBuffSearchArea";
const string c_EF_ProtonChargeSearchArea    = "ProtonChargeSearchArea";
const string c_EF_PsionicShockWaveArea      = "PsionicShockwaveSearchArea";
const string c_EF_PsiStormArea              = "PsiStormSearch";
const string c_EF_SeekerDamage              = "HunterSeekerDamage";
const string c_EF_ShockWavePersistent       = "PsionicShockwaveCreatePersistent";
const string c_EF_SnipeDamage               = "snipeDamage";
const string c_EF_TemporalRiftSearchArea    = "TemporalRiftUnitSearchArea";
const string c_EF_250mmStrikeCannonsDamage  = "250mmStrikeCannonsDamage";
const string c_EF_250mmStrikeCannonsPersistent= "250mmStrikeCannonsCreatePersistent";
const string c_EF_Transfusion               = "Transfusion";
const string c_EF_YamatoDamage              = "YamatoU";
const string c_EF_VortexArea                = "VortexSearchArea";
const string c_EF_LeviathanChargeDamage     = "LeviathanChargeU";
const string c_EF_LeviathanCharge           = "LeviathanCharge";
const string c_EF_ZergVortexArea            = "ZergVortexSearchArea";

//--------------------------------------------------------------------------------------------------
//  Weapons
//--------------------------------------------------------------------------------------------------
const string c_WE_AutoTurret                = "AutoTurret";
const string c_WE_Reaper                    = "Reaper";
const string c_WE_SiegeTankSieged           = "SiegeTankSieged";
const string c_WE_SpineCrawler              = "SpineCrawler";
const string c_WE_VikingFighter             = "VikingFighter";
const string c_WE_InfestedTerran            = "InfestedTerran";

//--------------------------------------------------------------------------------------------------
//  Abilities
//--------------------------------------------------------------------------------------------------
const string c_AB_Move                      = "move";
const string c_AB_Follow                    = "move";

const string c_AB_AcidSpores                = "AcidSpores";
const string c_AB_AssaultMode               = "AssaultMode";
const string c_AB_Attack                    = "attack";
const string c_AB_AutoTurret                = "AutoTurret";
const string c_AB_Blink                     = "Blink";
const string c_AB_BuildFighter              = "CarrierHangar";
const string c_AB_BansheeCloak              = "BansheeCloak";
const string c_AB_BurrowBanelingDown        = "BurrowBanelingDown";
const string c_AB_BurrowBanelingUp          = "BurrowBanelingUp";
const string c_AB_BurrowDroneDown           = "BurrowDroneDown";
const string c_AB_BurrowDroneUp             = "BurrowDroneUp";
const string c_AB_BurrowHydraliskDown       = "BurrowHydraliskDown";
const string c_AB_BurrowHydraliskUp         = "BurrowHydraliskUp";
const string c_AB_BurrowInfestorDown        = "BurrowInfestorDown";
const string c_AB_BurrowInfestorUp          = "BurrowInfestorUp";
const string c_AB_BurrowLurkerDown          = "BurrowLurkerDown";
const string c_AB_BurrowLurkerUp            = "BurrowLurkerUp";
const string c_AB_BurrowRoachDown           = "BurrowRoachDown";
const string c_AB_BurrowRoachUp             = "BurrowRoachUp";
const string c_AB_BurrowQoBDown             = "Val03QueenOfBladesBurrow";
const string c_AB_BurrowQoBUp               = "Val03QueenOfBladesUnburrow";
const string c_AB_BurrowQueenDown           = "BurrowQueenDown";
const string c_AB_BurrowQueenUp             = "BurrowQueenUp";
const string c_AB_BurrowUltraliskDown       = "BurrowUltraliskDown";
const string c_AB_BurrowUltraliskUp         = "BurrowUltraliskUp";
const string c_AB_BurrowZerglingDown        = "BurrowZerglingDown";
const string c_AB_BurrowZerglingUp          = "BurrowZerglingUp";
const string c_AB_Changeling                = "SpawnChangeling";
const string c_AB_ConsumeDNA                = "ConsumeDNA";
const string c_AB_DefensiveMatrix           = "DefensiveMatrix";
const string c_AB_DepotLower                = "SupplyDepotLower";
const string c_AB_DepotRaise                = "SupplyDepotRaise";
const string c_AB_Drill                     = "RestoreResources";
const string c_AB_EMP                       = "EMP";
const string c_AB_ArgusLink                 = "ArgusLink";
const string c_AB_PhaseShift                = "PhaseShift";
const string c_AB_FighterMode               = "FighterMode";
const string c_AB_ForceField                = "ForceField";
const string c_AB_FungalGrowth              = "FungalGrowth";
const string c_AB_GenerateCreep             = "GenerateCreep";
const string c_AB_GhostCloak                = "PersonalCloaking";
const string c_AB_GravitonPrison            = "GravitonPrison";
const string c_AB_HybridAoEStun             = "HybridFAoEStun";
const string c_AB_HybridBlink               = "HybridBlink";
const string c_AB_InfestorDisease           = "InfestorDisease";
const string c_AB_InfestedTerrans           = "InfestedTerrans";
const string c_AB_Lockdown                  = "Lockdown";
const string c_AB_D8Charge                  = "D8Charge";
const string c_AB_MissilePods               = "MissilePods";
const string c_AB_NeuralParasite            = "NeuralParasite";
const string c_AB_Nuke                      = "nuke";
const string c_AB_OdinBarrage               = "OdinBarrage";
const string c_AB_Overload                  = "Overload";
const string c_AB_CalldownMULE              = "CalldownMULE";
const string c_AB_GravitonBeam              = "GravitonBeam";
const string c_AB_PlasmaBlast               = "HybridCPlasmaBlast";
const string c_AB_ProbeBuff                 = "ProtonCharge";
const string c_AB_PsionicShockwave          = "PsionicShockwave";
const string c_AB_PsiStorm                  = "PsiStorm";
const string c_AB_QoBIndignation            = "Indignation";
const string c_AB_QoBImplosion              = "Implosion";
const string c_AB_QoBOmegaStorm             = "OmegaStorm";
const string c_AB_QueenBuild                = "QueenBuild";
const string c_AB_Rally                     = "Rally";
const string c_AB_SapStructure              = "SapStructure";
const string c_AB_ScannerSweep              = "ScannerSweep";
const string c_AB_SeekerMissile             = "HunterSeekerMissile";
const string c_AB_ShieldBattery             = "ShieldBattery";
const string c_AB_SiegeMode                 = "SiegeMode";
const string c_AB_SiegeUnmode               = "Unsiege";
const string c_AB_Snipe                     = "Snipe";
const string c_AB_SpawnMutantLarva          = "SpawnMutantLarva";
const string c_AB_SpiderMine                = "SpiderMine";
const string c_AB_Stim                      = "StimPack";
const string c_AB_SupplyDrop                = "SupplyDrop";
const string c_AB_SpineCrawlerRoot          = "SpineCrawlerRoot";
const string c_AB_SporeCrawlerRoot          = "SporeCrawlerRoot";
const string c_AB_SpineCrawlerUproot        = "SpineCrawlerUproot";
const string c_AB_SporeCrawlerUproot        = "SporeCrawlerUproot";
const string c_AB_SwarmInfestation          = "SwarmInfestation";
const string c_AB_TemporalRift              = "TemporalRift";
const string c_AB_ThorRebirth               = "ThorReborn";
const string c_AB_250mmStrikeCannons        = "250mmStrikeCannons";
const string c_AB_Transfusion               = "Transfusion";
const string c_AB_UnstableMutation          = "UnstableMutation";
const string c_AB_Vortex                    = "Vortex";
const string c_AB_UpgradeToWarpGate         = "UpgradeToWarpGate";
const string c_AB_WormholeTransit           = "WormholeTransit";
const string c_AB_WPPhasingMode             = "PhasingMode";
const string c_AB_WPTransportMode           = "TransportMode";
const string c_AB_WraithCloak               = "WraithCloak";
const string c_AB_Yamato                    = "Yamato";
const string c_AB_LeviathanCharge           = "LeviathanCharge";
const string c_AB_ZergVortex                = "ZergVortex";

const string c_AB_BunkerChange              = "BunkerTransport";
const string c_AB_CommandCenterChange       = "CommandCenterTransport";
const int    e_AB_TransportLoadUnit         = 0;
const int    e_AB_TransportUnloadAll        = 1;
const int    e_AB_TransportUnloadUnit       = 3;
const int    e_AB_TransportLoadAll          = 4;

const string c_AB_Hallucinate               = "HighTemplarTrain";
const int    e_AB_Hallucinate_Probe         = 0;
const int    e_AB_Hallucinate_Zealot        = 1;
const int    e_AB_Hallucinate_Stalker       = 2;
const int    e_AB_Hallucinate_Immortal      = 3;
const int    e_AB_Hallucinate_HighTemplar   = 4;
const int    e_AB_Hallucinate_Archon        = 5;
const int    e_AB_Hallucinate_VoidRay       = 6;
const int    e_AB_Hallucinate_Phoenix       = 7;
const int    e_AB_Hallucinate_WarpPrism     = 8;
const int    e_AB_Hallucinate_Colossus      = 9;

//--------------------------------------------------------------------------------------------------
// Unit Attributes
//--------------------------------------------------------------------------------------------------

const int e_unitAttributeNone           = -1;
const int e_unitAttributeLight          =  0;
const int e_unitAttributeArmored        =  1;
const int e_unitAttributeBiological     =  2;
const int e_unitAttributeMechanical     =  3;
const int e_unitAttributeRobotic        =  4;
const int e_unitAttributePsionic        =  5;
const int e_unitAttributeMassive        =  6;
const int e_unitAttributeStructure      =  7;
const int e_unitAttributeHover          =  8;
const int e_unitAttributeHeroic         =  9;

//--------------------------------------------------------------------------------------------------
//  Object Types
//--------------------------------------------------------------------------------------------------
const int c_objTypeIsInvalid          = -1;
const int c_objTypeIsUnit             = 0;
const int c_objTypeIsBuilding         = 1;
const int c_objTypeIsResearch         = 2;


//--------------------------------------------------------------------------------------------------
//  AI Get Object Type
//--------------------------------------------------------------------------------------------------
int AIGetObjectType (int player, string objType) {

    // override any special cases here

    return AIDefaultGetObjectType(player, objType);
}

//--------------------------------------------------------------------------------------------------
//  AI Get Maker
//--------------------------------------------------------------------------------------------------
string AIGetMaker (int player, string objType) {

    // override any special cases here

    return AIDefaultGetMaker(player, objType);
}

//--------------------------------------------------------------------------------------------------
//  AI Get First Missing Req
//--------------------------------------------------------------------------------------------------
string AIGetFirstMissingReq (int player, string objType) {

    // override any special cases here

    return AIDefaultGetFirstMissingReq(player, objType);
}

//--------------------------------------------------------------------------------------------------
//  AI Any Unbuilt Reqs
//--------------------------------------------------------------------------------------------------
string AIGetFirstUnfinishedReq (int player, string objType) {

    // override any special cases here

    return AIDefaultGetFirstUnfinishedReq(player, objType);
}

//--------------------------------------------------------------------------------------------------
//  AI Get Full Make Time
//--------------------------------------------------------------------------------------------------
int AIGetFullMakeTime (int player, string objType) {

    // override any special cases here

    return AIDefaultGetFullMakeTime(player, objType);
}

//--------------------------------------------------------------------------------------------------
//  AI Specified Makers
//--------------------------------------------------------------------------------------------------
void AISpecifiedMakers () {
    AIReqAddSpecialMaker(c_TU_BattlecruiserDefensive, c_TU_Battlecruiser, "BattlecruiserSpecialize", 0);
    AIReqAddSpecialMaker(c_TU_BattlecruiserMissile, c_TU_Battlecruiser, "BattlecruiserSpecialize", 1);
    AIReqAddSpecialMaker(c_TU_BattlecruiserYamato, c_TU_Battlecruiser, "BattlecruiserSpecialize", 3);
}

//--------------------------------------------------------------------------------------------------
//  Declare the Nuke Constant Setting here
//--------------------------------------------------------------------------------------------------
void AISetNukeConstants (int player);

MisterTea



TacticalAI.galaxy
//==================================================================================================
//  Tactical AI Functions
//==================================================================================================

//==================================================================================================
//
//  Tactical AI System Known Issues:
//  * Marker Bug With Abilities that are missiles (Assigned to Andy / Dave)
//      - i.e. infestor, hunter seeker missile
//      - marker dissapears after cast, but before effect, so multiple casters may cast on the same unit
//  * Some Infestor / Overseer spells need AI (Assigned to Dave, Bob, or Andy & David Kim)
//      - An untested version of the old fungal growth and acid spores were started and then abandoned
//        when abilities changed fundamentally (see TactZergAI)
//      - An untested version of the old unstable mutation was started and then abandoned when the ability
//        changed fundamentally (see TactZergAI)
//      - Check with David Kim, who said that these are 90% sure going to change again.
//  * Infested Marine / Civilian Burrow (Task assigned to Andy Bond)
//      - AI Commented out for Burrow
//      - Needs to be placed back in after Andy fixes support to turn off abilities on Campaign
//      - Maps with infested units need to be tested
//
//  Wish List
//  * Need equivalent of AITime from synapses for tactical ai thinks
//      - scale it back when too much thinking, scale it up when too little
//
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//  CATALOG ACCESSOR CONSTANTS
//--------------------------------------------------------------------------------------------------

const string c_fieldAmount          = "Amount";
const string c_fieldAreaRadius0     = "AreaArray[0].Radius";
const string c_fieldAreaRadius1     = "AreaArray[1].Radius";
const string c_fieldAreaRadius2     = "AreaArray[2].Radius";
const string c_fieldAreaFraction0   = "AreaArray[0].Fraction";
const string c_fieldAreaFraction1   = "AreaArray[1].Fraction";
const string c_fieldAreaFraction2   = "AreaArray[2].Fraction";
const string c_fieldAttrStructure   = "AttributeBonus[Structure]";
const string c_fieldAttrArmored     = "AttributeBonus[Armored]";
const string c_fieldAttrPsionic     = "AttributeBonus[Psionic]";
const string c_fieldAttrLight       = "AttributeBonus[Light]";
const string c_fieldDrainFactor     = "DrainVitalCostFactor";
const string c_fieldEffectChange0   = "VitalArray[0].Change";
const string c_fieldEffectChange1   = "VitalArray[1].Change";
const string c_fieldEffectChange2   = "VitalArray[2].Change";
const string c_fieldEnergyCost      = "Cost[0].Vital[Energy]";
const string c_fieldEnergyMax       = "EnergyMax";
const string c_fieldFilters         = "SearchFilters";
const string c_fieldMinRange        = "MinimumRange";
const string c_fieldModification0   = "Modification.VitalRegenArray[Energy]";
const string c_fieldPeriodCount     = "PeriodCount";
const string c_fieldRadius          = "Radius";
const string c_fieldRadiusBonus0    = "AreaArray[0].RadiusBonus";
const string c_fieldRange0          = "Range[0]";
const string c_fieldRange           = "Range";
const string c_fieldSightDawn       = "Sight";
const string c_fieldTargetFilters   = "TargetFilters";
const string c_fieldTargetFiltersAB = "TargetFilters[0]";
const string c_fieldTargetFilters0  = "ValidatorArray[0].value.value";

//--------------------------------------------------------------------------------------------------
//  TACTICAL API
//
//  Usage:
//      filter = AIFilter(owner);
//      ...set various filter information...
//      newGroup = AIGetFilterGroup(filter, oldGroup);
//--------------------------------------------------------------------------------------------------
native aifilter AIFilter (int player);

native void AISetFilterAlliance (aifilter filter, int want);
native void AISetFilterMarker (aifilter filter, int min, int max, marker m);
native void AISetFilterSelf (aifilter filter, unit exclude);
native void AISetFilterBits (aifilter filter, unitfilter uf);
native void AISetFilterRange (aifilter filter, unit center, fixed radius);
native void AISetFilterLife (aifilter filter, fixed min, fixed max);
native void AISetFilterLifeLost (aifilter filter, fixed min, fixed max);
native void AISetFilterLifePercent (aifilter filter, fixed min, fixed max);
native void AISetFilterLifeSortReference (aifilter filter, fixed value, fixed distance);
native void AISetFilterLifeMod (aifilter filter, int type, fixed mod);
native void AISetFilterLifePerMarker (aifilter filter, fixed each, marker m);
native void AISetFilterShields (aifilter filter, fixed min, fixed max);
native void AISetFilterPlane (aifilter filter, int plane);
native void AISetFilterCanAttackEnemy (aifilter filter, int enemyGroundCount, int enemyAirCount);
native void AISetFilterCanAttackAlly (aifilter filter, bool groundAllies, bool airAllies);
native void AISetFilterBehaviorCount (aifilter filter, int minBehavior, int maxBehavior, string behaviorType);

const fixed c_minDamageFraction = .5;
const fixed c_distanceFromDamage = .2;

const int c_noMarkersMin = 0;
const int c_noMarkersMax = 0;

const int c_noBehaviorMin = 0;
const int c_noBehaviorMax = 0;

const int c_noMax = -1;
const int c_noMin = -1;

const bool c_groundAlliesNearby = true;
const bool c_airAlliesNearby = true;

const fixed c_noThreshold = 0;

native unitgroup AIGetFilterGroup (aifilter filter, unitgroup group);

const fixed c_maxDistanceToMinerals = 10;

native unitgroup AIFilterCasters (unitgroup group);
native unitgroup AIFilterPathable (unitgroup group, point inStart);
native unitgroup AIFilterGathering (unitgroup group, int inResource, fixed distance);

native fixed AIUnitGroupStrength (unitgroup inGroup);
native fixed AIAllyEnemyRatio (int player, point p, unitfilter filter, fixed range, fixed minThreshold);

native bool AIIsFollowingUnit (unit aiUnit, string unitType);
native int AIGetPlayerGroup (unitgroup inGroup);
native bool AINearbyPlaneTest (point p, int player, fixed range, int inPlane, int inAlliance);

//--------------------------------------------------------------------------------------------------
//  *** CATALOG INFO ***
//--------------------------------------------------------------------------------------------------
string AIAbilityStr (int player, string entry, string field) {
    return CatalogFieldValueGet(c_gameCatalogAbil, entry, field, player);
}
string AIEffectStr (int player, string entry, string field) {
    return CatalogFieldValueGet(c_gameCatalogEffect, entry, field, player);
}
string AIWeaponStr (int player, string entry, string field) {
    return CatalogFieldValueGet(c_gameCatalogWeapon, entry, field, player);
}
string AIUnitStr (int player, string entry, string field) {
    return CatalogFieldValueGet(c_gameCatalogUnit, entry, field, player);
}
string AIBehaviorStr (int player, string entry, string field) {
    return CatalogFieldValueGet(c_gameCatalogBehavior, entry, field, player);
}

fixed AIAbilityFixed (int player, string entry, string field) {
    return StringToFixed(AIAbilityStr(player, entry, field));
}
fixed AIEffectFixed (int player, string entry, string field) {
    return StringToFixed(AIEffectStr(player, entry, field));
}
fixed AIWeaponFixed (int player, string entry, string field) {
    return StringToFixed(AIWeaponStr(player, entry, field));
}
fixed AIUnitFixed (int player, string entry, string field) {
    return StringToFixed(AIUnitStr(player, entry, field));
}
fixed AIBehaviorFixed (int player, string entry, string field) {
    return StringToFixed(AIBehaviorStr(player, entry, field));
}

int AIAbilityInt (int player, string entry, string field) {
    return StringToInt(AIAbilityStr(player, entry, field));
}
int AIEffectInt (int player, string entry, string field) {
    return StringToInt(AIEffectStr(player, entry, field));
}
int AIWeaponInt (int player, string entry, string field) {
    return StringToInt(AIWeaponStr(player, entry, field));
}
int AIUnitInt (int player, string entry, string field) {
    return StringToInt(AIUnitStr(player, entry, field));
}
int AIBehaviorInt (int player, string entry, string field) {
    return StringToInt(AIBehaviorStr(player, entry, field));
}

unitgroup AIAbilityGroup (int player, string entry, unitgroup base) {
    return UnitGroupFilter(null, c_playerAny, base,
        UnitFilterStr(AIAbilityStr(player, entry, c_fieldTargetFiltersAB)), 0);
}
unitgroup AIEffectGroup (int player, string entry, unitgroup base) {
    return UnitGroupFilter(null, c_playerAny, base,
        UnitFilterStr(AIEffectStr(player, entry, c_fieldFilters)), 0);
}
unitgroup AIWeaponGroup (int player, string entry, unitgroup base) {
    return UnitGroupFilter(null, c_playerAny, base,
        UnitFilterStr(AIWeaponStr(player, entry, c_fieldTargetFilters)), 0);
}

//--------------------------------------------------------------------------------------------------
native void AISetTacticalAttackTargetPoint (unit u, point t);
native void AISetTacticalAttackTargetUnit (unit u, unit t);

//--------------------------------------------------------------------------------------------------
//  AIUnitGroupGetValidOrder
//--------------------------------------------------------------------------------------------------
const bool c_forwards = true;
const bool c_backwards = false;

//  Loops over a unitgroup forwards or backwards until finding a unit that makes a valid order.
//
native order AIUnitGroupGetValidOrder(unitgroup inGroup, order inOrder, unit caster, bool forwards);

//--------------------------------------------------------------------------------------------------
//  AICampSkirDiffTest
//--------------------------------------------------------------------------------------------------
bool AICampSkirDiffTest (int player, int campMinDiff, int skirMinDiff) {

    if (AIIsCampaign(player)) {
        return PlayerDifficulty(player) >= campMinDiff;
    }
    return PlayerDifficulty(player) >= skirMinDiff;
}

//--------------------------------------------------------------------------------------------------
//  AICastStandard
//--------------------------------------------------------------------------------------------------
bool AICastStandard (unit aiUnit, order ord, marker mark, bool retreat) {
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return false;
    }
    AICast(aiUnit, ord, mark, retreat);
    return true;
}

//--------------------------------------------------------------------------------------------------
//  AIMarker
//--------------------------------------------------------------------------------------------------
marker AIMarker (unit aiUnit, string name) {
    marker mark = MarkerCastingUnit(name, aiUnit);
    MarkerSetMatchFlag(mark, c_markerMatchLink, true);
    MarkerSetMatchFlag(mark, c_markerMatchCasterPlayer, true);
    return mark;
}

//--------------------------------------------------------------------------------------------------
//  AISpellPriority
//--------------------------------------------------------------------------------------------------
void AISpellPriority (unitgroup src, unitgroup dst) {

    // TODO: units that currently have an order should be prioritized lower than idle units since
    //  they're already doing something.


    // this simple AI just takes the first unit plus all following that share the same command
    //
    int count = UnitGroupCount(src, c_unitCountAll);
    unit first = UnitGroupUnit(src, 1);
    unit test;

    UnitGroupAdd(dst, first);
   
    while (count > 1) {
        test = UnitGroupUnit(src, count);
        count = count - 1;

        if (AISameCommand(first, test)) {
            UnitGroupAdd(dst, test);
        }
    }
}

//--------------------------------------------------------------------------------------------------
//  AIIsAttackOrder
//--------------------------------------------------------------------------------------------------
bool AIIsAttackOrder (order o) {
    return StringEqual(AbilityCommandGetAbility(OrderGetAbilityCommand(o)), c_AB_Attack, true);
}

// These should both be replaced by the xml/code versions CTargetFindRallyPoint
////--------------------------------------------------------------------------------------------------
////  AIThinkSetRallyDefault
////--------------------------------------------------------------------------------------------------
//void AIThinkSetRallyDefault (int player, unit aiUnit, int commandIndex) {
//    point rally;
//    order ord;
//
//    // Don't set rally points on campaign
//    if (AIIsCampaign(player)) {
//        return;
//    }
//
//    if (UnitRallyPointTargetCount(aiUnit, commandIndex + 1) != 0) {
//        return;
//    }
//
//    ord = AICreateOrder(player, c_AB_Rally, commandIndex);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return;
//    }
//
//    rally = AIWaveTargetGatherOPoint(player, c_townMax);
//    if (UnitRallyPointTargetPoint(aiUnit, 1, 1) == rally) {
//        return;
//    }
//
//    OrderSetTargetPoint(ord, rally);
//    AICast(aiUnit, ord, c_noMarker, c_castHold);
//}
//
////--------------------------------------------------------------------------------------------------
////  AIThinkSetRallyWorker
////--------------------------------------------------------------------------------------------------
//void AIThinkSetRallyWorker (int player, unit aiUnit, int commandIndex) {
//    order ord;
//    unitgroup findResults;
//
//    if (UnitRallyPointTargetCount(aiUnit, commandIndex + 1) != 0) {
//        return;
//    }
//
//    ord = AICreateOrder(player, c_AB_Rally, commandIndex);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return;
//    }
//
//    findResults = AIFindUnits(0, c_NU_Minerals, UnitGetPosition(aiUnit), 8, c_noMaxCount);
//    if (UnitGroupCount(findResults, c_unitCountAll) == 0) {
//        findResults = AIFindUnits(0, c_NU_HighYieldMinerals, UnitGetPosition(aiUnit), 8, c_noMaxCount);
//        if (UnitGroupCount(findResults, c_unitCountAll) == 0) {
//            OrderSetTargetPoint(ord, UnitGetPosition(aiUnit));
//            AICast(aiUnit, ord, c_noMarker, c_castHold);
//            return;
//        }
//    }
//
//    OrderSetTargetUnit(ord, UnitGroupUnit(findResults, 1));
//    AICast(aiUnit, ord, c_noMarker, c_castHold);
//}

//--------------------------------------------------------------------------------------------------
//  PointAlongLine
//--------------------------------------------------------------------------------------------------
point PointAlongLine (point from, point toward, fixed distance) {
    return PointWithOffsetPolar(from, distance, AngleBetweenPoints(toward, from));
}

//--------------------------------------------------------------------------------------------------
//  Set Nuke Constants
//--------------------------------------------------------------------------------------------------
void AISetNukeConstants (int player) {
    AISetNukeGhost(player, c_TU_Ghost);
    AISetNukeNukeEffect(player, c_EF_Nuke);
    AISetNukeCloak(player, c_TR_GhostCloak);
    AISetNukeNukeAbilLink(player, c_AB_Nuke);
    AISetNukeCloakAbilLink(player, c_AB_GhostCloak);
   
    AISetNukeCloakRegenRate(player, AIBehaviorFixed(player, c_BF_PersonalCloaking, c_fieldModification0));
    AISetNukeCloakCost(player, AIAbilityFixed(player, c_AB_GhostCloak, c_fieldEnergyCost));
    AISetNukeNukeCastTime(player, 20);

    AISetNukeDamage(player, AIEffectFixed(player, c_EF_NukeDamage, c_fieldAmount), AIEffectFixed(player, c_EF_NukeDamage, c_fieldAttrStructure));
    AISetNukeRadiusClose(player, AIEffectFixed(player, c_EF_NukeDamage, c_fieldAreaRadius0), AIEffectFixed(player, c_EF_NukeDamage, c_fieldAreaFraction0));
    AISetNukeRadiusMedium(player, AIEffectFixed(player, c_EF_NukeDamage, c_fieldAreaRadius1), AIEffectFixed(player, c_EF_NukeDamage, c_fieldAreaFraction1));
    AISetNukeRadiusFar(player, AIEffectFixed(player, c_EF_NukeDamage, c_fieldAreaRadius2), AIEffectFixed(player, c_EF_NukeDamage, c_fieldAreaFraction2));
}

//--------------------------------------------------------------------------------------------------
//  AISetDefaultCombatFlags
//--------------------------------------------------------------------------------------------------
void AISetDefaultCombatFlags (int player, bool isMelee) {
    int group1;
    int group2;

    if (isMelee) {
        group1 = c_skirVeryEasy;
        group2 = c_skirHard;
    }
    else {
        group1 = c_campBeginner;
        group2 = c_campExpert;
    }

    AICombatTargetProduction       (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetDropOffs         (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetFood             (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetActiveProduction (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetSelfThreats      (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetCurrent          (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetAir              (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetMovers           (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetInAttackRange    (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetThreats          (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetAttackers        (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatTargetSpecial          (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatAvoidNonThreats        (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatAvoidTimedUnits        (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatAvoidWeakUnits         (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));
    AICombatAvoidDisabledUnits     (player, AIDiffThreshold(player, c_combatDisable, group1, c_combatEnable));

    AICombatTargetWorkers          (player, AIDiffThreshold(player, c_combatDisable, group2, c_combatEnable));
    AICombatTargetAllyThreats      (player, AIDiffThreshold(player, c_combatDisable, group2, c_combatEnable));
    AICombatTargetInjuries         (player, AIDiffThreshold(player, c_combatDisable, group2, c_combatEnable));
    AICombatTargetHealers          (player, AIDiffThreshold(player, c_combatDisable, group2, c_combatEnable));
    AICombatTargetSiege            (player, AIDiffThreshold(player, c_combatDisable, group2, c_combatEnable));
}

//--------------------------------------------------------------------------------------------------
include "TriggerLibs/TactProtAI"
include "TriggerLibs/TactTerrAI"
include "TriggerLibs/TactZergAI"

MisterTea



TactProtAI.galaxy
//--------------------------------------------------------------------------------------------------
//  *** Disruptor ***
//--------------------------------------------------------------------------------------------------
const fixed c_forceFieldRadius = 3;
const fixed c_disruptorRange = 11;

fixed AIRangeDisruptor (int player, unit aiUnit) {
    return c_disruptorRange;
}

order AIForceField (int player, unit aiUnit, unitgroup scanGroup, fixed scanRange, marker m) {
    order ord;
    order enemyOrd;
    unitgroup enemyGroup;
    unitgroup friendlyGroup;
    unitfilter filter;
    unit enemyUnit;
    unit friendlyUnit;
    unit possibleTargetUnit;
    unit bestTargetUnit = null;
    point myPos = UnitGetPosition(aiUnit);
    fixed myFacing = UnitGetFacing(aiUnit);
    point forceFieldTarget;
    int forceFieldMaxRadius = 5;
    int priorityFound = 10;
    bool emergencyCast = false;

    // Loop control
    int enemyCount = 0;
    int friendlyCount = 0;

    // Enemy test values
    int enemyVit = 0;
    point enemyPos;
    fixed enemyFacing = 0;

    // Friendly test values
    int friendlyVit = 0;
    fixed friendlyFacing = 0;

    // Do not proceed if the order is invalid for some reason
    ord = AICreateOrder(player, c_AB_ForceField, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return ord;
    }

    // Is there a threat in the area
    enemyGroup = UnitGroupFilterThreat(scanGroup, aiUnit, null, 0);
    enemyCount = UnitGroupCount(enemyGroup, c_unitCountAlive);
    if (enemyCount <= 0) {
        return ord;
    }

    // Assess life and energy
    // A good energy buffer is 50 energy left over.  100 energy is needed to cast in any battle
    // If our life is less than 50%, use the energy buffer.
    if (UnitGetPropertyInt(aiUnit, c_unitPropEnergy, c_unitPropCurrent) <= 100) {
        if (UnitGetPropertyInt(aiUnit, c_unitPropLifePercent, c_unitPropCurrent) <= 50) {
            emergencyCast = true;
        }
    }

    // Setup a friendly filter
    filter = UnitFilter(0,0,0,0);
    UnitFilterSetState(filter, c_targetFilterSelf,                      c_unitFilterRequired);
    UnitFilterSetState(filter, c_targetFilterAlly,                      c_unitFilterRequired);

    UnitFilterSetState(filter, c_targetFilterUncommandable,             c_unitFilterExcluded);
    UnitFilterSetState(filter, c_targetFilterWorker,                    c_unitFilterExcluded);
    UnitFilterSetState(filter, c_targetFilterUnderConstruction,         c_unitFilterExcluded);
    UnitFilterSetState(filter, c_targetFilterStructure,                 c_unitFilterExcluded);

    // *** Scan the area for the following, set priority unit when found ***
    // Priority 1: A friendly unit close to death, who has a melee enemy close by
    // Priority 2: An enemy unit close to death, who has a friendly unit close by
    // Priority 3: A friendly ranged unit who has a melee enemy close by
    // Priority 4: Enemy near me
    // Priority 5: Any Enemy close to death, facing away from me
    //
    // Scan enemies
    while (enemyCount > 0) {
        enemyUnit = UnitGroupUnit(enemyGroup, enemyCount);
        enemyCount = enemyCount - 1;

        // Get enemy data
        enemyVit = UnitGetPropertyInt(enemyUnit, c_unitPropLifePercent, c_unitPropCurrent);
        enemyFacing = UnitGetFacing(enemyUnit);
        enemyPos = UnitGetPosition(enemyUnit);
        enemyOrd = UnitOrder(enemyUnit, 0);
   
        friendlyGroup = UnitGroup(null, c_playerAny, RegionCircle(enemyPos, forceFieldMaxRadius), filter, 0);
        friendlyCount = UnitGroupCount(friendlyGroup, c_unitCountAll);

        // Scan friends near enemyUnit
        while (friendlyCount > 0) {
            friendlyUnit = UnitGroupUnit(friendlyGroup, friendlyCount);
            friendlyCount = friendlyCount - 1;
            // Get friendly data
            friendlyVit = UnitGetPropertyInt(friendlyUnit, c_unitPropLifePercent, c_unitPropCurrent);

            if (UnitMarkerCount(friendlyUnit, m) > 0) {
                continue;
            }
            // Test Priority Target 4 first, if we are in a state of saving ourself
            if (emergencyCast) {
                if (PointsInRange(myPos, enemyPos, forceFieldMaxRadius)) {
                    priorityFound = 1;
                    bestTargetUnit = friendlyUnit;
                    break;
                }
            }
            // Test Priority Target 1
            else if (friendlyVit < 25) {
                priorityFound = 1;
                bestTargetUnit = friendlyUnit;
                break;
            }
            // Test Priority Target 2
            else if (priorityFound > 2 && enemyVit < 25) { // && Test for non-ranged only
                priorityFound = 2;
                possibleTargetUnit = enemyUnit;
            }
            // Test Priority Target 3
            else if (false) { //priorityFound > 3
                // Not really able to test for this yet
                // Need to determine if the unit in question has a ranged weapon
            }
            // Test Priority Target 4
            else if (priorityFound > 4 && PointsInRange(myPos, enemyPos, forceFieldMaxRadius)) {
                priorityFound = 4;
                possibleTargetUnit = enemyUnit;
            }
            // Test Priority Target 5
            else if (priorityFound > 5 && enemyVit < 25 && enemyFacing - myFacing < 90) {
                priorityFound = 5;
                possibleTargetUnit = enemyUnit;
            }
            // More tests here
        }

        if (bestTargetUnit) {
            break;
        }
    }

    if (bestTargetUnit != null) {
        if (possibleTargetUnit) {
            bestTargetUnit = possibleTargetUnit;
        }
    }

    // Do some other things here, like if we are targetting a retreating enemy (priority 5)
    // adjust the target offset a bit ahead of the unit based on facing.
    // We would want this for priority 3 as well when that is valid.

    forceFieldTarget = UnitGetPosition(bestTargetUnit);

    if (!forceFieldTarget) {
        return null;
    }
    OrderSetTargetPoint(ord, forceFieldTarget);

    return ord;
}

//--------------------------------------------------------------------------------------------------
static bool Hallucinate (int player, unit aiUnit, unitgroup scanGroup) {
    point here;
    order ord;

    // wait until fighting 5+ enemy units
    //
    if (!AIIsAttackOrder(UnitOrder(aiUnit, 0))) {
        return false;
    }
    if (UnitGroupCount(scanGroup, c_unitCountAlive) < 5) {
        return false;
    }

    ord = AICreateOrder(player, c_AB_Hallucinate, e_AB_Hallucinate_Zealot);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return false;
    }

    here = UnitGetPosition(aiUnit);
    if (AINearbyUnits(player, c_PU_Zealot, here, 5, 2)) {
        AICast(aiUnit, ord, c_noMarker, c_castHold);
        return true;
    }
    if (AINearbyUnits(player, c_PU_Stalker, here, 5, 2)) {
        ord = AICreateOrder(player, c_AB_Hallucinate, e_AB_Hallucinate_Stalker);
        AICast(aiUnit, ord, c_noMarker, c_castHold);
        return true;
    }
    if (AINearbyUnits(player, c_PU_Immortal, here, 5, 2)) {
        ord = AICreateOrder(player, c_AB_Hallucinate, e_AB_Hallucinate_Immortal);
        AICast(aiUnit, ord, c_noMarker, c_castHold);
        return true;
    }
    return false;
}

//--------------------------------------------------------------------------------------------------
static bool ForceField (int player, unit aiUnit, unitgroup scanGroup) {

    return false;

//    forceFieldMarker = MarkerCastingUnit(c_MK_Snipe, aiUnit);
//    MarkerSetMatchFlag(forceFieldMarker, c_markerMatchLink, true);
//    MarkerSetMismatchFlag(forceFieldMarker, c_markerMatchId, true);
//
//    ord = AIForceField(player, aiUnit, scanGroup, c_disruptorRange, forceFieldMarker);
//
//    if (ord) {
//        AICast(aiUnit, ord, forceFieldMarker, c_castHold);
//        return;
//    }
}

//--------------------------------------------------------------------------------------------------
static bool MolDisplacer (int player, unit aiUnit, unitgroup scanGroup) {
    return false;
}

//--------------------------------------------------------------------------------------------------
void AIThinkDisruptor (int player, unit aiUnit, unitgroup scanGroup) {
    if (Hallucinate(player, aiUnit, scanGroup)) {
        return;
    }
    if (ForceField(player, aiUnit, scanGroup)) {
        return;
    }
    MolDisplacer(player, aiUnit, scanGroup);
}

//--------------------------------------------------------------------------------------------------
//  *** DARK PYLON ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeObelisk (int player, unit aiUnit) {
    return AIAbilityFixed(player, c_AB_ShieldBattery, c_fieldRange0) + 1;

    //return MaxF(AIAbilityFixed(player, c_AB_ArgusLink, c_fieldRange0),
    //            AIAbilityFixed(player, c_AB_ShieldBattery, c_fieldRange0)) + 1
    //            ;
}

////--------------------------------------------------------------------------------------------------
//order ProtonCharge (int player, unit aiUnit, unitgroup scanGroup) {
//    order ord;
//    point loc;
//    unitgroup probeGroup;
//    aifilter filter;
//
//    ord = AITacticalOrder(player, aiUnit, c_AB_ProbeBuff);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return null;
//    }
//
//    probeGroup = AIFindUnits(
//        player,
//        c_PU_Probe,
//        UnitGetPosition(aiUnit),
//        AIAbilityFixed(player, c_AB_ProbeBuff, c_fieldRange0) + AIEffectFixed(player, c_EF_ProtonChargeSearchArea, c_fieldAreaRadius0),
//        c_noMaxCount
//    );
//
//    //  Filter for probes that are gathering, but not buffed.
//    //
//    filter = AIFilter(player);
//    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_ProbeBuff, c_fieldTargetFiltersAB)));
//    AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, c_BF_ProbeBuff);
//    probeGroup = AIGetFilterGroup(filter, probeGroup);
//    probeGroup = AIFilterGathering(probeGroup, c_resourceTypeMinerals, c_maxDistanceToMinerals);
//
//    loc = AIBestTargetPoint(
//        probeGroup,
//        5, // min hits
//        50, // damage base. set to whatever since don't care about score.
//        0, // score. set to zero since only care about hits.
//        AIEffectFixed(player, c_EF_ProtonChargeSearchArea, c_fieldAreaRadius0),
//        UnitGetPosition(aiUnit),
//        AIAbilityFixed(player, c_AB_ProbeBuff, c_fieldRange0),
//        c_unitAttributeNone
//    );
//
//    if (loc == null) {
//        return null;
//    }
//   
//    OrderSetTargetPoint(ord, loc);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return null;
//    }
//    return ord;
//}

////--------------------------------------------------------------------------------------------------
//const fixed c_argusLinkMax = 150;
//
//order ArgusLink (int player, unit aiUnit, unitgroup scanGroup) {
//    order ord;
//    unitgroup group;
//    aifilter filter;
//   
//    fixed reserve;
//    fixed highTemplarMaxEnergy;
//    fixed energy;
//    fixed argusLinkMin;
//   
//    ord = AITacticalOrder(player, aiUnit, c_AB_ArgusLink);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return null;
//    }
//
//    //  Find the minimum amount of mana the high templar must have in order to
//    //  save 25 mana for buffing probes.
//    //
//    reserve = AIAbilityFixed(player, c_AB_ProbeBuff, c_fieldEnergyCost);
//    highTemplarMaxEnergy = AIUnitFixed(player, c_PU_HighTemplar, c_fieldEnergyMax);
//    energy = UnitGetPropertyFixed(aiUnit, c_unitPropEnergy, c_unitPropCurrent);
//    argusLinkMin = highTemplarMaxEnergy + (reserve - energy) / AIAbilityFixed(player, c_AB_ArgusLink, c_fieldDrainFactor);
//
//    if (argusLinkMin > c_argusLinkMax) {
//        return null;
//    }
//
//    //  For now, just recharge high templars.
//    //
//    group = AIFindUnits(player, c_PU_HighTemplar, UnitGetPosition(aiUnit), AIRangeObelisk(player, aiUnit), c_noMaxCount);
//    filter = AIFilter(player);
//    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_ArgusLink, c_fieldTargetFiltersAB)));
//    AISetFilterAlliance(filter, c_playerGroupAlly);
//    AISetFilterEnergy(filter, argusLinkMin, c_argusLinkMax);   
//    AISetFilterLife(filter, c_noMin, c_noMax);  //  Give the energy to the highest life high templar first.
//   
//    group = AIGetFilterGroup(filter, group);
//    return AIUnitGroupGetValidOrder(group, ord, aiUnit, c_forwards);
//}

////--------------------------------------------------------------------------------------------------
//const fixed c_shieldBatteryMaxShields = 15;
//const int c_batteryMinEnergyNoHostiles = 100;
//const int c_batteryMinEnergyHostiles = 6;
//
//order ShieldBattery (int player, unit aiUnit, unitgroup scanGroup) {
//    order ord;
//    aifilter allyFilter;
//    unitgroup group;
//    region r;
//    int energy = UnitGetPropertyInt(aiUnit, c_unitPropEnergy, c_unitPropCurrent);
//
//    //  It looks silly to cast every time the shield battery gains one energy, so require some low minimum threshold.
//    //
//    if (energy < c_batteryMinEnergyHostiles) {
//        return null;
//    }
//
//    ord = AITacticalOrder(player, aiUnit, c_AB_ShieldBattery);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return null;
//    }
// 
//    //  If there are no enemy units, and energy is too low, don't heal.
//    //
//    r = RegionCircle(UnitGetPosition(aiUnit), AIRangeObelisk(player, aiUnit));
//    if (UnitGroupCount(UnitGroupAlliance(player, c_unitAllianceEnemy, r, null, c_noMaxCount), c_unitCountAll) == 0 && energy > c_batteryMinEnergyNoHostiles) {
//        return null;
//    }
//
//    //  Otherwise, look for allies with low shields to heal.
//    //
//    group = AIFindUnits(player, null, UnitGetPosition(aiUnit), AIRangeObelisk(player, aiUnit), c_noMaxCount);
//    allyFilter = AIFilter(player);
//    AISetFilterBits(allyFilter, UnitFilterStr(AIAbilityStr(player, c_AB_ShieldBattery, c_fieldTargetFiltersAB)));
//    AISetFilterAlliance(allyFilter, c_playerGroupAlly);
//    AISetFilterShields(allyFilter, c_noMin, c_shieldBatteryMaxShields);
//   
//    group = AIGetFilterGroup(allyFilter, group);
//    return AIUnitGroupGetValidOrder(group, ord, aiUnit, c_forwards);
//}

MisterTea

TactProtAI.galaxy < suite

//--------------------------------------------------------------------------------------------------
void AIThinkObelisk (int player, unit aiUnit, unitgroup scanGroup) {
    ////  1. Buff harvesting probes
    ////  2. If unit shields low & enemies nearby, heal it.
    ////  3. If high templar nearby, charge mana.
    //order ord;

    ////ord = ProtonCharge(player, aiUnit, scanGroup);
    ////if (ord != null) {
    ////    AICast(aiUnit, ord, c_noMarker, c_castHold);
    ////    return;
    ////}

    ////ord = ArgusLink(player, aiUnit, scanGroup);
    ////if (ord != null) {
    ////    AICast(aiUnit, ord, c_noMarker, c_castHold);
    ////}

    ////ord = ShieldBattery(player, aiUnit, scanGroup);
    ////if (ord != null) {
    ////    AICast(aiUnit, ord, c_noMarker, c_castHold);
    ////}
}

//--------------------------------------------------------------------------------------------------
//  *** MOTHERSHIP ***
//--------------------------------------------------------------------------------------------------

fixed AIRangeMothership (int player, unit aiUnit) {
    return MaxF(AIAbilityFixed(player, c_AB_Vortex, c_fieldRange0) + 1,
                AIAbilityFixed(player, c_AB_TemporalRift, c_fieldRange0) + 1);
}

fixed MinToCastMotherShip (int player) {
    //return AIAbilityFixed(player, c_AB_Vortex, c_fieldEnergyCost) + //  To Save enough mana for wormhole transit, swap out the commented lines.
    //       AIAbilityFixed(player, c_AB_WormholeTransit, c_fieldEnergyCost);
    return AIAbilityFixed(player, c_AB_Vortex, c_fieldEnergyCost);
}

const fixed c_enemyMultiplierVortex = 1.25;

//--------------------------------------------------------------------------------------------------
order Vortex (int player, unit aiUnit, unitgroup scanGroup) {
    point loc;
    unitgroup vortexGroup;

    order ord = AITacticalOrder(player, aiUnit, c_AB_Vortex);
    if (ord == null) {
        return null;
    }

    vortexGroup = AIEffectGroup(player, c_EF_VortexArea, scanGroup);

    loc = AIBestTargetPoint(
        vortexGroup,
        5, // min hits
        40, // damage base
        4.0, // score
        AIEffectFixed(player, c_EF_VortexArea, c_fieldAreaRadius0),
        UnitGetPosition(aiUnit),
        AIRangeMothership(player, aiUnit),
        c_unitAttributeNone
    );

    if (loc == null) {
        return null;
    }
   
    OrderSetTargetPoint(ord, loc);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }
    return ord;
}

//--------------------------------------------------------------------------------------------------
order TemporalRift (int player, unit aiUnit, unitgroup scanGroup) {
    point loc;
   
    order ord = AITacticalOrder(player, aiUnit, c_AB_TemporalRift);
    if (ord == null) {
        return null;
    }

    loc = AIBestTargetPoint(
        AIEffectGroup(player, c_EF_TemporalRiftSearchArea, scanGroup),
        5, // min hits
        40, // damage base
        3.85, // min score
        AIEffectFixed(player, c_EF_TemporalRiftSearchArea, c_fieldAreaRadius0),
        UnitGetPosition(aiUnit),
        AIRangeMothership(player, aiUnit),
        c_unitAttributeNone
    );

    if (loc == null) {
        return null;
    }
   
    OrderSetTargetPoint(ord, loc);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }
    return ord;
}

//--------------------------------------------------------------------------------------------------
void AIThinkMothership (int player, unit aiUnit, unitgroup scanGroup) {
    order ord;
    unitfilter f;
    fixed allyEnemyRatio;
    aifilter filter;
   
    //  Make sure to save mana so we use both abilities.
    //
    if (UnitGetPropertyInt(aiUnit, c_unitPropEnergy, c_unitPropCurrent) < MinToCastMotherShip(player)) {
        return;
    }
   
    //  If a unit already has temporal rift, filter it.
    //
    filter = AIFilter(player);
    AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, c_BF_TemporalBuff);
    scanGroup = AIGetFilterGroup(filter, scanGroup);

    //  Get the ratio of allies to enemies to see if we should cast vortex or temporal rift
    //
    f = UnitFilterStr("-;Missile,Dead,Stasis,Worker");
    allyEnemyRatio = AIAllyEnemyRatio(player, UnitGetPosition(aiUnit), f, AIRangeMothership(player, aiUnit), c_noThreshold);

    if (allyEnemyRatio < c_enemyMultiplierVortex) {
        ord = Vortex(player, aiUnit, scanGroup);
        if (ord != null) {
            AICast(aiUnit, ord, c_noMarker, c_castRetreat);
            return;
        }
    }

    ord = TemporalRift(player, aiUnit, scanGroup);
    if (ord != null) {
        AICast(aiUnit, ord, c_noMarker, c_castRetreat);
        return;
    }
}

//--------------------------------------------------------------------------------------------------
//  *** HIGH TEMPLAR ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeHighTemplar (int player, unit aiUnit) {
    return AIAbilityFixed(player, c_AB_PhaseShift, c_fieldRange0) + 1;
}

const int c_phaseShiftMinHealth = 425; //  carriers and above.
const int c_phaseShiftLowVitMinHealth = 300;

order PhaseShift (int player, unit aiUnit, unitgroup scanGroup, marker mark, bool lowVitality) {
    order ord;
    aifilter filter;
    bool airAllies;
    int minVitality;

    ord = AITacticalOrder(player, aiUnit, c_AB_PhaseShift);
    if (ord == null) {
        return null;
    }

    if (lowVitality) {
        minVitality = c_phaseShiftLowVitMinHealth;
    }
    else {
        minVitality = c_phaseShiftMinHealth;
    }

    //  Test to see if we have airborne allies, to determine whether to PhaseShift
    //  an enemy that only attacks air.
    airAllies = AINearbyPlaneTest(UnitGetPosition(aiUnit), player, AIRangeHighTemplar(player, aiUnit), c_planeAir, c_unitAllianceAlly);
   
    filter = AIFilter(player);
    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_PhaseShift, c_fieldTargetFiltersAB)));
    AISetFilterLife(filter, minVitality, c_noMax);
    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
    AISetFilterCanAttackAlly(filter, c_groundAlliesNearby, airAllies);
   
    scanGroup = AIGetFilterGroup(filter, scanGroup);
    return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_forwards);
}

//--------------------------------------------------------------------------------------------------
const fixed c_EnemyMultiplierHighTemplar = 1.25;
const fixed c_MinThreshold = 100.0;
const int c_HighTemplarLowVitPerc = 50;

void UnitGroupTest (unitgroup scanGroup) {
    DebugVarInt("group strength", FixedToInt(AIUnitGroupStrength(scanGroup)));
}

void AIThinkHighTemplar (int player, unit aiUnit, unitgroup scanGroup) {
    marker mark;
    order ord;
    bool lowVitality;
    unitfilter f;
    region r;
    unitgroup enemyGroup;

    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }

    //  If we already have a psi storm order, ignore any new orders since psi storm is more important.
    //  If we already have an PhaseShift order, ignore any new PhaseShift orders.
    if (UnitOrderHasAbil(aiUnit, c_AB_PhaseShift) || UnitOrderHasAbil(aiUnit, c_AB_PsiStorm)) {
        return;
    }

    //  Low Vitality = less than 50%, then use this to escape.
    lowVitality = (UnitGetPropertyInt(aiUnit, c_unitPropVitalityPercent, c_unitPropCurrent) < c_HighTemplarLowVitPerc);

    if (!lowVitality) {
        //  Don't cast if the enemy only has 1 unit when we are at full health.
        //  If we have less than 1.25 * the enemies forces.
        f = UnitFilterStr("-;Missile,Dead,Stasis,Worker");
        r = RegionCircle(UnitGetPosition(aiUnit), AIRangeHighTemplar(player, aiUnit));
        if ((UnitGroupCount(UnitGroupAlliance(player, c_unitAllianceEnemy, r, null, c_noMaxCount), c_unitCountAll) < 2) ||
            AIAllyEnemyRatio(player, UnitGetPosition(aiUnit), f, AIRangeHighTemplar(player, aiUnit), c_MinThreshold) > c_EnemyMultiplierHighTemplar) {
            return;
        }
    }

    mark = AIMarker(aiUnit, c_MK_PhaseShift);
    ord = PhaseShift(player, aiUnit, scanGroup, mark, lowVitality);
    if (ord != null) {
        AICast(aiUnit, ord, mark, c_castRetreat);
        return;
    }
}


//--------------------------------------------------------------------------------------------------
void AIThinkGateway (int player, unit aiUnit, unitgroup scanGroup) {
    order ord;
   
    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }

    if (AITechCount(player, c_PR_WarpGateResearch, c_techCountCompleteOnly) == 0) {
        return;
    }

    AISetWantsToUpgrade(aiUnit);

    ord = AICreateOrder(player, c_AB_UpgradeToWarpGate, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return;
    }
 
    AICast(aiUnit, ord, c_noMarker, c_castHold);
}


//--------------------------------------------------------------------------------------------------
//  *** WarpPrism ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeWarpPrism (int player, unit aiUnit) {
    return 1;
}

//--------------------------------------------------------------------------------------------------
//  *** WarpPrism (transport mode) ***
//--------------------------------------------------------------------------------------------------
const fixed c_warpPrismPhaseRange = 1.0;

void AIThinkWarpPrism (int player, unit aiUnit, unitgroup scanGroup) {
    order ord = AICreateOrder(player, c_AB_WPPhasingMode, 0);
    string type = AIGetBullyType(aiUnit);
   
    if (UnitOrderCount(aiUnit) > 0) {
        return;
    }

    if (!UnitOrderIsValid(aiUnit, ord)) {
        return;
    }

    if (type != c_PU_WarpPrismPhasing) {
        return;
    }

    if (AIGetHomePosition(aiUnit) == c_nullPoint) {
        return;
    }

    if (!PointsInRange(UnitGetPosition(aiUnit), AIGetHomePosition(aiUnit), c_warpPrismPhaseRange)) {
        return;
    }

    AICast(aiUnit, ord, c_noMarker, c_castHold);
}

//--------------------------------------------------------------------------------------------------
//  *** WarpPrism (power mode) ***
//--------------------------------------------------------------------------------------------------
void AIThinkWarpPrismPhasing (int player, unit aiUnit, unitgroup scanGroup) {
    order ord = AICreateOrder(player, c_AB_WPTransportMode, 0);
    string type = AIGetBullyType(aiUnit);
   
    if (UnitOrderCount(aiUnit) > 0) {
        return;
    }

    if (!UnitOrderIsValid(aiUnit, ord)) {
        return;
    }

    if (type == c_PU_WarpPrismPhasing) {
        if (AIGetHomePosition(aiUnit) != c_nullPoint) {
            if (PointsInRange(UnitGetPosition(aiUnit), AIGetHomePosition(aiUnit), c_warpPrismPhaseRange)) {
                // we're supposed to provide power at this point
                return;
            }
        }
    }

    AICast(aiUnit, ord, c_noMarker, c_castHold);
}

MisterTea


TactTerrAI.galaxy
//--------------------------------------------------------------------------------------------------
//  *** THOR ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeThor (int player, unit aiUnit) {
    return AIAbilityFixed(player, c_AB_250mmStrikeCannons, c_fieldRange0) + 1;
}

order AIOrder250mmStrikeCannons(int player, unit aiUnit, unitgroup scanGroup, marker mark) {
    order ord;
    fixed damage;
    aifilter filter;
    bool airAllies;

    ord = AICreateOrder(player, c_AB_250mmStrikeCannons, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    damage = AIEffectFixed(player, c_EF_250mmStrikeCannonsPersistent, c_fieldPeriodCount) *
             AIEffectFixed(player, c_EF_250mmStrikeCannonsDamage, c_fieldAmount)
             ;

    filter = AIFilter(player);
    AISetFilterAlliance(filter, c_playerGroupEnemy);
    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_250mmStrikeCannons, c_fieldTargetFiltersAB)));
    AISetFilterRange(filter, aiUnit, AIRangeThor(player, aiUnit));
    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
    AISetFilterLife(filter, damage*c_minDamageFraction, c_noMax);
    AISetFilterLifeSortReference(filter, damage, damage*c_distanceFromDamage);
    //  Filter out units that can't attack allies since this ability is a disable.
    //
    airAllies = AINearbyPlaneTest(UnitGetPosition(aiUnit),
                                  player,
                                  AIRangeThor(player, aiUnit),
                                  c_planeAir,
                                  c_unitAllianceAlly)
                                  ;
    AISetFilterCanAttackAlly(filter, c_groundAlliesNearby, airAllies);

    //  Select starting from the end, which is the target that has health closest to the cannon's damage
    //
    scanGroup = AIGetFilterGroup(filter, scanGroup);
    return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_backwards);
}

void AIThinkThor (int player, unit aiUnit, unitgroup scanGroup) {
    order ord;
    marker mark;
   
    //  If we already have a cannon order, ignore new orders so that we do not count
    //  our own marker again, when validating.
    //
    if (UnitOrderHasAbil(aiUnit, c_AB_250mmStrikeCannons)) {
        return;
    }

    mark = AIMarker(aiUnit, c_MK_250mmStrikeCannons);
    ord = AIOrder250mmStrikeCannons(player, aiUnit, scanGroup, mark);
    if (ord != null) {
        AICast(aiUnit, ord, c_noMarker, c_castHold);
    }
}

//--------------------------------------------------------------------------------------------------
//  *** GHOST ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeGhost (int player, unit aiUnit) {
    return MaxF(AIAbilityFixed(player, c_AB_EMP, c_fieldRange0) + 1,
                AIAbilityFixed(player, c_AB_Snipe, c_fieldRange0) + 1);
}

//---------------------------------------------------------------------------------------------
order AIOrderSnipe (int player, unit aiUnit, unitgroup scanGroup, marker mark) {
    order ord;
    fixed damage;
    aifilter filter;

    ord = AICreateOrder(player, c_AB_Snipe, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    damage = AIEffectInt(player, c_EF_SnipeDamage, c_fieldAmount);

    filter = AIFilter(player);
    AISetFilterAlliance(filter, c_playerGroupEnemy);
    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_Snipe, c_fieldTargetFiltersAB)));
    AISetFilterRange(filter, aiUnit, AIAbilityFixed(player, c_AB_Snipe, c_fieldRange0) + 1);
    AISetFilterLifePerMarker(filter, damage, mark);
    AISetFilterLifeSortReference(filter, damage, damage*c_distanceFromDamage);

    //  Select starting from the end, to obtain the target that has health closest to snipe
    //  damage.
    //
    scanGroup = AIGetFilterGroup(filter, scanGroup);
    return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_backwards);
}

//
const int c_gameLoopsPerSecond = 16;
const int c_framesPerThink = 12;
const int c_secondsPerSnipe = 4;
const int c_ghostMaxRandom = c_gameLoopsPerSecond * c_secondsPerSnipe / c_framesPerThink;

//---------------------------------------------------------------------------------------------
void AIThinkGhost (int player, unit aiUnit, unitgroup scanGroup) {
    // **Cloaking / EMP relocated to XML**
    marker mark;
    order ord;
    int randomVal;

    //  Add a delay on campaign.
    //
    if (AIIsCampaign(player)) {
        if (RandomInt(0, c_ghostMaxRandom) != 1) {
            return;
        }
    }

    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }

    mark = AIMarker(aiUnit, c_MK_Snipe);
    ord = AIOrderSnipe(player, aiUnit, scanGroup, mark); // this modifies scanGroup.
    if (ord != null) {
        AICast(aiUnit, ord, mark, c_castHold);
        return;
    }
}

//--------------------------------------------------------------------------------------------------
//  *** REAPER ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeReaper (int player, unit aiUnit) {
    return AIWeaponFixed(player, c_WE_Reaper, c_fieldRange);
}

const int c_BldgOnlyPercent = 60;

order AIOrderReapMine (int player, unit aiUnit, unitgroup scanGroup, marker mark) {
    order ord;
    int scanCount;
    unit target;
    bool bldgOnly;
    unitgroup friends;
    aifilter filter;
    unitfilter f;
    int bonus;

    //  See if this spell can be cast at all.
    //
    ord = AICreateOrder(player, c_AB_D8Charge, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    bonus = AIEffectInt(player, c_EF_D8ChargeDmg, c_fieldAttrArmored) * 2;
    bldgOnly = (UnitGetPropertyInt(aiUnit, c_unitPropVitalityPercent, c_unitPropCurrent) > c_BldgOnlyPercent);

    if (AICampSkirDiffTest(player, c_campAdvanced, c_skirVeryHard)) {
        bldgOnly = false;
    }

    //  Get enemies with enough life to be around when the spell goes off.
    //
    filter = AIFilter(player);
    AISetFilterAlliance(filter, c_playerGroupEnemy);
    AISetFilterLife(filter, AIEffectInt(player, c_EF_D8ChargeDmg, c_fieldAmount) * 2, c_noMax - bonus);
    AISetFilterLifeMod(filter, c_unitAttributeArmored, bonus);
    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
    if (bldgOnly) {
        f = UnitFilterStr("Structure;Missile,Dead,Stasis,Worker"); // require buildings.
    }
    else {
        f = UnitFilterStr("-;Missile,Dead,Stasis,Worker"); // default filter.
    }
   
    //  Pick a building/unit with no allies nearby, starting with lowest health buildings that passed above tests.
    //
    scanGroup = AIGetFilterGroup(filter, scanGroup);
    scanCount = UnitGroupCount(scanGroup, c_unitCountAll);

    while (scanCount > 0) {
        target = UnitGroupUnit(scanGroup, scanCount);
        scanCount = scanCount - 1;
   
        //  For now, Reapers are only used as a diversion wave, so we can't target photon cannons.
        //  Photon cannons are very dangerous to reapers
        //
        if (UnitGetType(target) == c_PB_PhotonCannon) {
            continue;
        }

        //  Check for friendly fire.
        //
        friends = UnitGroupFilterAlliance(
            AIFindUnits(player, null, UnitGetPosition(target), AIEffectFixed(player, c_EF_D8ChargeDmg, c_fieldAreaRadius2), c_noMaxCount),
            player,
            c_allianceIdPassive,
            0
        );
        if (UnitGroupCount(friends, c_unitCountAlive) > 0) {
            continue;
        }

        //  Check target validity.
        //
        OrderSetTargetUnit(ord, target);
        if (UnitOrderIsValid(aiUnit, ord)) {
            return ord;
        }
    }
    return null;
}

void AIThinkReaper (int player, unit aiUnit, unitgroup scanGroup) {
    // **Reaper AI reproduced in XML**
    //  Melee AI does not call this AIThink routine.

    //  Reaper AI is left in TactTerrAI to serve as an example of two different ways to write the same
    //  AI.  To see the xml equivalent of the Reaper AI, look in TacticalData.xml and TargetFindData.xml.
    //  In general, AI implemented in galaxy scripts will be slower than AI implemented in data.
    //  If it is convenient to express AI in data, one should do so.  In this case, converting reaper AI
    //  to xml resulted in a 2x performance gain.
    //
    marker mark = AIMarker(aiUnit, c_MK_D8Charge);
    order ord;

    scanGroup = UnitGroupFilterPlane(scanGroup, c_planeGround, 0);
    ord = AIOrderReapMine(player, aiUnit, scanGroup, mark); //  Modifies scanGroup.
    if (ord == null) {
        return;
    }
    AICast(aiUnit, ord, mark, c_castRetreat);
}

//--------------------------------------------------------------------------------------------------
//  *** BATTLE CRUISER ***
//--------------------------------------------------------------------------------------------------
order AIOrderYamato (int player, unit aiUnit, unitgroup scanGroup, marker mark) {
    order ord;
    aifilter filter;
    fixed damage;

    //  Only cast as part of an existing offensive.
    //
    if (!AIIsAttackOrder(UnitOrder(aiUnit, 0))) {
        return null;
    }
   
    //  See if this spell can be cast at all.
    //
    ord = AICreateOrder(player, c_AB_Yamato, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    damage = AIEffectFixed(player, c_EF_YamatoDamage, c_fieldAmount);

    //  Search enemies for those with hit points closest to the damage yamato gun inflicts.
    //
    filter = AIFilter(player);
    AISetFilterAlliance(filter, c_playerGroupEnemy);
    AISetFilterLifePerMarker(filter, damage, mark);
    AISetFilterLifeSortReference(filter, damage, damage*c_distanceFromDamage);
    scanGroup = AIGetFilterGroup(filter, scanGroup);
   
    return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_backwards);
}

//---------------------------------------------------------------------------------------------
void AIThinkBattleCruiser (int player, unit aiUnit, unitgroup scanGroup) {
    // **Defensive Matrix located in TacticalData.xml / ValidatorData.xml**
    // **Missile Pods located in TacticalData.xml / TargetFindData.xml**
   
    marker mark;
    order ord;

    //  If we already have a yamato order, ignore new orders so that we do not count
    //  our own markers again.
    if (UnitOrderHasAbil(aiUnit, c_AB_Yamato)) {
        return;
    }
   
    //  This modifies scanGroup, which is Ok.. so long as it is not used elsewhere.
    mark = AIMarker(aiUnit, c_MK_Yamato);
    ord = AIOrderYamato(player, aiUnit, scanGroup, mark);
    if (ord != null) {
        AICast(aiUnit, ord, mark, c_castHold);
        return;
    }
}

//--------------------------------------------------------------------------------------------------
//  *** VIKING (ground mode) ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeViking (int player, unit aiUnit) {
    return AIWeaponFixed(player, c_WE_VikingFighter, c_fieldRange) + 2;
}

point VikingModeChange (int player, unit aiUnit, unitgroup scanGroup, bool inAssault) {
    int scanCount;
    int inAir;
    int onGround;
    int cliffLevel;
    int testCliffLevel;
    string type = AIGetBullyType(aiUnit);
    point here = UnitGetPosition(aiUnit);
    point there;
    unitgroup scanGroupThreat;
    int onGroundThreatCount;

    //  First, find all units within viking range.
    //
    scanGroup = UnitGroupFilterRegion(scanGroup, RegionCircle(here, AIRangeViking(player, null)), 0);
    inAir = UnitGroupCount(scanGroup, c_unitCountAlive);

    //  Next, find the number of ground and air units within viking range.
    //
    scanGroup = UnitGroupFilterPlane(scanGroup, c_planeGround, 0);
    onGround = UnitGroupCount(scanGroup, c_unitCountAlive);
    inAir = inAir - onGround; // air = all - ground

    //  If a bully type is set, check to see if we're in the right mode if idle.
    if (type != null && onGround == 0 && inAir == 0 && UnitOrderCount(aiUnit) == 0) {
        if (type == UnitGetType(aiUnit)) {
            return null;
        }
        else {
            return UnitGetPosition(aiUnit);
        }
    }

    //  Only pay attention to threats on the ground.
    //
    scanGroupThreat = UnitGroupFilterThreat(scanGroup, aiUnit, null, 0);
    onGroundThreatCount = UnitGroupCount(scanGroupThreat, c_unitCountAlive);

    if (inAssault) { // viking is on ground.
        //  Go to air mode when there's nothing on the ground anymore.
        //
        if (onGround == 0) {
            return UnitGetPosition(aiUnit);
        }
        //  Also, go to air mode when a pack of new air units shows up and we have killed most of the
        //  ground units.
        if (inAir >= onGroundThreatCount + 3 || (inAir > 0 && onGroundThreatCount == 0)) {
            return UnitGetPosition(aiUnit);
        }
    }
    else { // viking is in air.
        //  If air targets remain, finish them off before switching.
        //
        if (inAir != 0) {
            return null;
        }

        if (onGround > 0) {
            cliffLevel = CliffLevel(here);
           
            //  Only switch if there is a ground target on the same or lower cliff level so that
            //  we are guaranteed to be able to navigate to it.
            //
            while (onGround > 0) {
                there = UnitGetPosition(UnitGroupUnit(scanGroup, onGround));
                testCliffLevel = CliffLevel(there);
                if (cliffLevel >= testCliffLevel) {
                    return there;
                }
                onGround = onGround - 1;
            }
        }
    }
    return null;
}

//---------------------------------------------------------------------------------------------
void AIThinkVikingAssault (int player, unit aiUnit, unitgroup scanGroup) {
    order ord = AICreateOrder(player, c_AB_FighterMode, 0);
    unitgroup airGroup;
    int inAir;
   
    //  Check to see if this is a valid order at all.
    //
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return;
    }

    //  Check the number of air units to decide whether to ignore based on order count.
    //
    airGroup = UnitGroupFilterRegion(scanGroup,
                                     RegionCircle(UnitGetPosition(aiUnit), AIRangeViking(player, null)),
                                     0)
                                     ;
    airGroup = UnitGroupFilterPlane(airGroup, c_planeAir, 0);
    inAir = UnitGroupCount(airGroup, c_unitCountAlive);

    //  Do not switch to an air unit while processing an order on the ground. That way we won't
    //  interrupt the queued attack move order we get when we were told to land.
    //
    if (inAir == 0 && UnitOrderCount(aiUnit) > 0) {
        return;
    }

    if (VikingModeChange(player, aiUnit, scanGroup, true) == null) {
        return;
    }

    AICast(aiUnit, ord, c_noMarker, c_castHold);
}

//--------------------------------------------------------------------------------------------------
//  *** VIKING (air mode) ***
//--------------------------------------------------------------------------------------------------
void AIThinkVikingFighter (int player, unit aiUnit, unitgroup scanGroup) {
    point there;
    order ord = AICreateOrder(player, c_AB_AssaultMode, 0);

    if (!UnitOrderIsValid(aiUnit, ord)) {
        return;
    }

    there = VikingModeChange(player, aiUnit, scanGroup, false);
    if (there == null) {
        return;
    }

    AICast(aiUnit, ord, c_noMarker, c_castHold);

    //  Queue an attack move towards the threat to make sure we path past a LOS blocker.
    //
    AISetTacticalAttackTargetPoint(aiUnit, there);
}

MisterTea

TactTerrAI.galaxy < suite


//--------------------------------------------------------------------------------------------------
//  CargoDefend
//--------------------------------------------------------------------------------------------------
const fixed c_campaignBunkerLoadRange = 4.0;
const bool c_bunkerUnload = true;
const bool c_bunkerLoad = false;

//---------------------------------------------------------------------------------------------
unit CampaignWantsToBeInBunker (int player, unit aiUnit, unitgroup bunkerGroup, bool unload) {
    int bunkerCount;
    unit unitToCheck;
    bool wantsToBeInBunker;

    //  When loading, check to see if there is space in the bunker at all.
    //
    if (!unload) {
        if (UnitCargoValue(aiUnit, c_unitCargoSpaceFree) == 0) {
            return null;
        }
    }

    bunkerCount = UnitGroupCount(bunkerGroup, c_unitCountAll);
    while (bunkerCount > 0) {
        unitToCheck = UnitGroupUnit(bunkerGroup, bunkerCount);
        bunkerCount = bunkerCount - 1;

        //  Make sure the unit is alive.
        //
        if (!UnitIsAlive(unitToCheck)) {
            continue;
        }
        //  When loading, make sure the unit is not allready in a transport.
        //
        if (!unload) {
            if (UnitTestState(unitToCheck, c_unitStateInsideTransport)) {
                continue;
            }
        }

        //  The unit wants to be somewhere far away, do not load it.
        //
        wantsToBeInBunker = true;

        //  The unit is forced to move, do not load.
        if (AIControlForceToMove(unitToCheck)) {
            wantsToBeInBunker = false;
        }
        //  The unit wants to execute order, do not load.
        else if (UnitOrderCount(unitToCheck) > 0) {
            wantsToBeInBunker = false;
        }
        //  The unit wants to move, do not load if safe.
        else if (AIControlWantsToMove(unitToCheck)) {
            if (!AIUnitIsInCombat(unitToCheck) && !AIUnitIsInCombat(aiUnit)) {
                wantsToBeInBunker = false;
            }
        }
        else {
            // Need to check the home point
            if (AIGetHomePosition(unitToCheck) == c_nullPoint) {
                wantsToBeInBunker = false;
            }
            else if (!PointsInRange(UnitGetPosition(aiUnit), AIGetHomePosition(unitToCheck), c_campaignBunkerLoadRange)) {
                wantsToBeInBunker = false;
            }
        }

        //  Do not care about units that want to be in bunker when we want to unload.
        //  Similarly, do not care about units that do not want to be in bunker when we want to load.
        //
        if (wantsToBeInBunker == unload) {
            continue;
        }

        return unitToCheck;
    }
    return null;
}

//---------------------------------------------------------------------------------------------
bool CargoDefend (int player, unit aiUnit, unitgroup scanGroup, int searchRange, int loadRange, string wanted, string command) {
    unitgroup nearBunkerGroup;
    int bunkerCount;
    unit unitToCheck;   
    order ord = null;
    bool autoLoad = false;
    bool wantsToBeInBunker;

    scanGroup = UnitGroupFilterThreat(scanGroup, aiUnit, null, 0);
    scanGroup = UnitGroupFilterRegion(scanGroup, RegionCircle(UnitGetPosition(aiUnit), searchRange), 0);
   
    if (UnitGroupCount(scanGroup, c_unitCountAlive) == 0) { // no nearby enemies.

        //  Both checks are needed because auto loading bunkers is needed on campaign before the
        //  AI is active.....
        if (AIIsCampaign(player)) {
            autoLoad = true;
        }
        else if (AIGetDifficulty(player, c_diffAutoLoadBunkers)) {
            autoLoad = true;
        }

        if (autoLoad && (command == c_AB_BunkerChange)) {
            // handle bunkers on campaign differently.
            unitToCheck = CampaignWantsToBeInBunker(player, aiUnit, UnitCargoGroup(aiUnit), c_bunkerUnload);
            if (unitToCheck != null) {
                ord = AICreateOrder(player, command, e_AB_TransportUnloadUnit); // unload the bunker.
                OrderSetTargetPassenger(ord, unitToCheck);
            }

            if (ord == null) {

                unitToCheck = CampaignWantsToBeInBunker(player,
                                                        aiUnit,
                                                        AIFindUnits(player, wanted, UnitGetPosition(aiUnit), c_campaignBunkerLoadRange, c_noMaxCount),
                                                        c_bunkerLoad);
                if (unitToCheck != null) {
                    ord = AICreateOrder(player, command, e_AB_TransportLoadUnit); // load the bunker.
                    OrderSetTargetUnit(ord, unitToCheck);
                }
            }
        }
        else { // not a campaign bunker
            if (UnitCargoValue(aiUnit, c_unitCargoSpaceUsed) == 0) { // nothing to unload
                return false;
            }
            ord = AICreateOrder(player, command, e_AB_TransportUnloadAll); // unload bunker
        }
    }
    else { // nearby enemies found.
        if (UnitCargoValue(aiUnit, c_unitCargoSpaceFree) == 0) { // check for space
            return false;
        }

        if (command == c_AB_CommandCenterChange) {
            if (!AIAnyWorkersFleeingNearby(player,UnitGetPosition(aiUnit),8.0)) {
                return false;
            }
        }

        nearBunkerGroup = AIFindUnits(player, wanted, UnitGetPosition(aiUnit), loadRange, c_noMaxCount);
        bunkerCount = UnitGroupCount(nearBunkerGroup, c_unitCountAll);
        while (bunkerCount > 0) {
            unitToCheck = UnitGroupUnit(nearBunkerGroup, bunkerCount);
            bunkerCount = bunkerCount - 1;

            if (!UnitIsAlive(unitToCheck)) {
                continue;
            }
            if (UnitTestState(unitToCheck, c_unitStateInsideTransport)) {
                continue;
            }
           
            if (command == c_AB_CommandCenterChange) {
                ord = AICreateOrder(player, command, e_AB_TransportLoadAll);
            }
            else {
                ord = AICreateOrder(player, command, e_AB_TransportLoadUnit);
                OrderSetTargetUnit(ord, unitToCheck);
            }
            break;
        }
    }
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return false;
    }
    AICast(aiUnit, ord, c_noMarker, c_castHold);
    return true;
}

//--------------------------------------------------------------------------------------------------
//  *** COMMAND CENTER ***
//--------------------------------------------------------------------------------------------------
static order CallDownSupply (int player, unit aiUnit) {
    unitgroup scanGroup;
    int scanCount;
    unit depot;
    order ord;
    aifilter filter;
   
    ord = AICreateOrder(player, c_AB_SupplyDrop, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    scanGroup = AIFindUnits(player, "SupplyDepotUnderground", UnitGetPosition(aiUnit),
                            AIUnitFixed(player, c_TB_CommandCenter, c_fieldRadius)
                            + AIAbilityFixed(player, c_AB_SupplyDrop, c_fieldRange0),
                            c_noMaxCount
    );

    //  Filter out supply depots that are already buffed.
    //
    filter = AIFilter(player);
    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_SupplyDrop, c_fieldTargetFiltersAB)));
    AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, c_BF_SupplyDrop);
   
    scanGroup = AIGetFilterGroup (filter, scanGroup);
    return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_backwards);
}

static bool CallDownMule (int player, unit aiUnit) {
    unitgroup scanGroup;
    int scanCount;
    unit peon;
    order ord;

    ord = AICreateOrder(player, c_AB_CalldownMULE, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return false;
    }

    scanGroup = AIFindUnits(player, c_TU_SCV, UnitGetPosition(aiUnit),
                            AIAbilityFixed(player, c_AB_CalldownMULE, c_fieldRange0),
                            c_noMaxCount
    );

    //  Find only scv's that are gathering minerals and are close to their mineral target.
    scanGroup = AIFilterGathering(scanGroup, c_resourceTypeMinerals, c_maxDistanceToMinerals);
    scanCount = UnitGroupCount(scanGroup, c_unitCountAll);
    while (scanCount > 0) {
        //  Pick one to cast on / next to.
        peon = UnitGroupUnit(scanGroup, scanCount);
        scanCount = scanCount - 1;

        ord = AICreateOrder(player, c_AB_CalldownMULE, 0);
        OrderSetTargetPoint(ord, UnitGetPosition(peon));
        if (!UnitOrderIsValid(aiUnit, ord)) {
            continue;
        }
        AICast(aiUnit, ord, c_noMarker, c_castHold);
        return true;
    }
    return false;
}

void AIThinkCommandCenter(int player, unit aiUnit, unitgroup scanGroup) {
    order ord;

    if (CargoDefend(player, aiUnit, scanGroup, 10, 10, c_TU_SCV, c_AB_CommandCenterChange))
    {
        return;
    }

    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }

    if (AIIsCampaign(player)) {
        return;
    }

    if (AISuspectDetectionDanger(player)) {
        if (UnitGetPropertyInt(aiUnit, c_unitPropEnergy, c_unitPropCurrent) < 150) {
            return; // save for two comsats
        }
    }
    if (CallDownMule(player, aiUnit)) {
        return;
    }

    //  For now do supply only if we can't do mule,
    //  tempted to comment this out entirely
    ord = CallDownSupply(player, aiUnit);
    if (ord != null) {
        AICast(aiUnit, ord, c_noMarker, c_castHold);
        return;
    }
}

//--------------------------------------------------------------------------------------------------
//  *** BUNKER ***
//--------------------------------------------------------------------------------------------------
void AIThinkBunker(int player, unit aiUnit, unitgroup scanGroup) {
    CargoDefend(player, aiUnit, scanGroup, 8, 10, c_TU_Marine, c_AB_BunkerChange);
}

//--------------------------------------------------------------------------------------------------
//  ScannerSweep
//--------------------------------------------------------------------------------------------------
bool ScannerSweep (int player, unit aiUnit) {
    order ord;
    point loc;

    ord = AICreateOrder(player, c_AB_ScannerSweep, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return false;
    }

    //  If something is attacking while cloaked and we can defend if we reveal it, cast sweep.
    //
    loc = AIGetCloakedAttacker(player);

    //  Else if we have high energy then use sweep to scout something.
    //
    if (loc == null) {
        // TODO fix AIGetNextScoutLoc to return only important locations
        //if (UnitGetPropertyFixed(aiUnit, c_unitPropEnergyPercent, true) >= 200) {
        //    loc = AIGetNextScoutLoc(player);
        //}
    }
    if (loc == null) {
        return false;
    }
    OrderSetTargetPoint(ord, loc);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return false;
    }
    AICast(aiUnit, ord, c_noMarker, c_castHold);
    AIClearCloakedAttacker(player, loc);
    return true;
}

//--------------------------------------------------------------------------------------------------
//  *** SURVEILLANCE STATION ***
//--------------------------------------------------------------------------------------------------
void AIThinkOrbitalCommand (int player, unit aiUnit, unitgroup scanGroup) {
    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }

    if (!ScannerSweep(player, aiUnit)) {
        AIThinkCommandCenter(player, aiUnit, scanGroup);
    }
}

//--------------------------------------------------------------------------------------------------
//  *** MagneticMine ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeD8Charge (int player, unit aiUnit) {
    return AIEffectFixed(player, c_EF_D8ChargeDmg, c_fieldAreaRadius2) + 1.0;
}

void AIThinkD8Charge (int player, unit aiUnit, unitgroup scanGroup) {
    int scanCount;
    marker mark = AIMarker(aiUnit, c_MK_D8ChargeFlee);
    aifilter filter = AIFilter(player);

    scanGroup = AIFindUnits(player, null, UnitGetPosition(aiUnit), AIRangeD8Charge(player, aiUnit), c_noMaxCount);

    AISetFilterAlliance(filter, c_playerGroupAlly);
    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
    AISetFilterSelf(filter, aiUnit);
    scanGroup = AIGetFilterGroup(filter, scanGroup);

    scanCount = UnitGroupCount(scanGroup, c_unitCountAll);
    while (scanCount > 0) {
        AICastFlee(UnitGroupUnit(scanGroup, scanCount), aiUnit, 6, mark);
        scanCount = scanCount - 1;
    }
}

//--------------------------------------------------------------------------------------------------
//  *** Raven ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeRaven (int player, unit aiUnit) {
    return 10;
}

const int c_seekerMissileMinMarker = 0;
const int c_seekerMissileMaxMarker = 0;

order HunterSeekerMissile (int player, unit aiUnit, unitgroup scanGroup, marker mark) {
    fixed damage;
    unitgroup targetGroup;
    aifilter filter;

    order ord = AITacticalOrder(player, aiUnit, c_AB_SeekerMissile);
    if (!ord) {
        return null;
    }

    //  Create the filters
    //
    filter = AIFilter(player);
    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_SeekerMissile, c_fieldTargetFiltersAB)));
    AISetFilterAlliance(filter, c_playerGroupEnemy);
    damage = AIEffectFixed(player, c_EF_SeekerDamage, c_fieldAmount);
    AISetFilterLife(filter, damage*c_minDamageFraction, c_noMax);
    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
    AISetFilterLifeSortReference(filter, damage, damage*c_distanceFromDamage);
   
    //  Select starting from the end, which is the target who's health is closest to the missile's
    //  damage.
    //
    targetGroup = AIGetFilterGroup(filter, scanGroup);
    return AIUnitGroupGetValidOrder(targetGroup, ord, aiUnit, c_backwards);
}

//---------------------------------------------------------------------------------------------
static bool CastAutoTurret (int player, unit aiUnit, unitgroup scanGroup, bool lowVitality) {   
    // must not modify scanGroup in this function
    point loc;
    order ord = AITacticalOrder(player, aiUnit, c_AB_AutoTurret);
    fixed minScore;

    if (!ord) {
        return false;
    }

    //  Relax the constraints a little if the raven is low on health.
    //
    if (lowVitality) {
        minScore = 1.0;
    }
    else {
        minScore = 2.0;
    }

    //  Finds the best point for the ai to cast an area of effect spell.  The point must put the
    //  turret in range of min hits targets, with an accumulative score GE 2.  Each target is assigned
    //  a score of 0.0 to 1.0 points, depending on whether the target's vitality is from 0.0 to 40.0.
    //  Up to an additional 1.0 point can be rewarded if the target has the optional bonus attribute.
    //
    loc = AIBestTargetPoint(
        AIWeaponGroup(player, c_WE_AutoTurret, scanGroup),
        2, // min hits
        40, // damage base
        minScore, // min score
        AIWeaponFixed(player, c_WE_AutoTurret, c_fieldRange),
        UnitGetPosition(aiUnit),
        AIRangeRaven(player, aiUnit),
        c_unitAttributeNone
    );
    if (!loc) {
        return false;
    }
    OrderSetTargetPoint(ord, loc);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return false;
    }
    AICast(aiUnit, ord, c_noMarker, c_castRetreat);
    return true;
}

fixed minToCastAutoTurret(int player) {
    return AIAbilityFixed(player, c_AB_AutoTurret, c_fieldEnergyCost) +
           AIAbilityFixed(player, c_AB_SeekerMissile, c_fieldEnergyCost);
}

const int c_RavenLowVitalityPercent = 65;

//---------------------------------------------------------------------------------------------
void AIThinkRaven (int player, unit aiUnit, unitgroup scanGroup) {
    order ord;
    marker mark;
    bool lowVitality;

    //  Must not modify scanGroup because it will be used for CastAutoTurret.
    mark = AIMarker(aiUnit, c_MK_SeekerMissile);
    ord = HunterSeekerMissile(player, aiUnit, scanGroup, mark);
    if (ord != null) {
        AICast(aiUnit, ord, mark, c_castRetreat);
        return;
    }
   
    //  If Raven not in danger, save enough energy for seeker missile.
    lowVitality = UnitGetPropertyInt(aiUnit, c_unitPropVitalityPercent, c_unitPropCurrent) < c_RavenLowVitalityPercent;
    if (!lowVitality && UnitGetPropertyInt(aiUnit, c_unitPropEnergy, c_unitPropCurrent) < minToCastAutoTurret(player)) {
        return;
    }

    if (CastAutoTurret(player, aiUnit, scanGroup, lowVitality)) {
        return;
    }
}

MisterTea



TactZergAI.galaxy
//--------------------------------------------------------------------------------------------------
//  *** QUEEN ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeQueen (int player, unit aiUnit) {
    return MaxF(AIAbilityFixed(player, c_AB_Transfusion, c_fieldRange0),
                AIAbilityFixed(player, c_AB_SpawnMutantLarva, c_fieldRange0)) + 1;
}

//--------------------------------------------------------------------------------------------------
order Transfusion (int player, unit aiUnit) {
    order ord;
    unitgroup group;
    aifilter filter;

    ord = AICreateOrder(player, c_AB_Transfusion, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    group = AIFindUnits(player, null, UnitGetPosition(aiUnit),
                        AIAbilityFixed(player, c_AB_Transfusion, c_fieldRange0) + 1,
                        c_noMaxCount)
    ;

    filter = AIFilter(player);
    AISetFilterAlliance(filter, c_playerGroupAlly);
    AISetFilterLifeLost(filter, AIEffectFixed(player, c_EF_Transfusion, c_fieldEffectChange0), c_noMax);
    group = AIGetFilterGroup(filter, group);

    return AIUnitGroupGetValidOrder(group, ord, aiUnit, c_forwards);
}

//--------------------------------------------------------------------------------------------------
order SpawnLarva (int player, unit aiUnit) {
    order ord;
    unitgroup hatcheries;
    int larvaCount;

    //  Only cast if idle.
    //
    if (UnitOrder(aiUnit, 0) != null) {
        return null;
    }

    ord = AICreateOrder(player, c_AB_SpawnMutantLarva, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    //  Don't cast if we already own at least 10 larva
    //
    larvaCount =
        TechTreeBehaviorCount(player, c_BF_MutantLarvaTimer, c_techCountQueuedOrBetter) * 4
      + TechTreeUnitCount(player, c_ZU_Larva, c_techCountQueuedOrBetter)
    ;
    if (larvaCount >= 10) {
        return null;
    }

    hatcheries = AIFindUnits(player,
                             c_ZB_Hatchery_Alias,
                             UnitGetPosition(aiUnit),
                             15,
                             c_noMaxCount)
                             ;
    return AIUnitGroupGetValidOrder(hatcheries, ord, aiUnit, c_backwards);
}

//--------------------------------------------------------------------------------------------------
order CreepTumor (int player, unit aiUnit) {

    order ord;
    point p;

    //  Only cast if idle.
    //
    if (UnitOrder(aiUnit, 0) != null) {
        return null;
    }

    ord = AICreateOrder(player, c_AB_QueenBuild, 1);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    //  Don't cast if there is no creep point set for this player.
    p = AIGetBestCreepSpot(player, aiUnit, 8);
    if (p == null) {
        return null;
    }

    OrderSetTargetPlacement(ord, p, aiUnit, c_ZU_CreepTumor);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    return ord;
}

//--------------------------------------------------------------------------------------------------
void AIThinkQueen (int player, unit aiUnit, unitgroup scanGroup) {
    order ord;
    unit heal;

    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }
    ord = Transfusion(player, aiUnit);
    if (ord != null) {
        AICast(aiUnit, ord, c_noMarker, c_castHold);
        return;
    }
    ord = SpawnLarva(player, aiUnit);
    if (ord != null) {
        AICast(aiUnit, ord, c_noMarker, c_castHold);
        return;
    }
    ord = CreepTumor(player, aiUnit);
    if (ord != null) {
        AICast(aiUnit, ord, c_noMarker, c_castRetreat);
        return;
    }
}

//--------------------------------------------------------------------------------------------------
//  *** INFESTOR ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeInfestor (int player, unit aiUnit) {
    return MaxF(AIAbilityFixed(player, c_AB_InfestedTerrans, c_fieldRange0),
                AIAbilityFixed(player, c_AB_NeuralParasite, c_fieldRange0)) + 1;

    // AIAbilityFixed(player, c_AB_UnstableMutation, c_fieldRange0)
}

//  Minimum vitality of a unit in order to mind control it,
//  infestors will mind control units > carrier.
const fixed c_neuralParasiteMinVitality = 425;

//---------------------------------------------------------------------------------------------
order NeuralParasite (int player, unit aiUnit, unitgroup scanGroup, marker mark, marker gameMark, bool lowVitality) {
    order ord;
    aifilter filter;
    unit target;

    //  vars related to nearby enemies.
    //
    aifilter groundAirFilter;
    unitgroup threatGroup;
    int enemyGroundCount;
    int enemyAirCount;
   
    //  Create order and check validity
    //
    ord = AICreateOrder(player, c_AB_NeuralParasite, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    //  Scan for enemy air and ground units
    //
    enemyGroundCount = 0;
    enemyAirCount = 0;

    groundAirFilter = AIFilter(player);
    AISetFilterAlliance(groundAirFilter, c_playerGroupEnemy);
    AISetFilterPlane(groundAirFilter, c_planeGround);
    enemyGroundCount = UnitGroupCount(AIGetFilterGroup(groundAirFilter, scanGroup), c_unitCountAlive);

    AISetFilterPlane(groundAirFilter, c_planeAir);
    enemyAirCount = UnitGroupCount(AIGetFilterGroup(groundAirFilter, scanGroup), c_unitCountAlive);

    //  Apply filters to enemies
    //
    filter = AIFilter(player);
    AISetFilterAlliance(filter, c_playerGroupEnemy);
    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_NeuralParasite, c_fieldTargetFiltersAB)));
    AISetFilterRange(filter, aiUnit, AIAbilityFixed(player, c_AB_NeuralParasite, c_fieldRange0) + 1);
    AISetFilterLife(filter, c_neuralParasiteMinVitality, c_noMax);
    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
    AISetFilterCanAttackEnemy(filter, enemyGroundCount, enemyAirCount);
    scanGroup = AIGetFilterGroup(filter, scanGroup);

    //  Since it's a missile, we also have to check the game-side marker
    //
    filter = AIFilter(player);
    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, gameMark);
    scanGroup = AIGetFilterGroup(filter, scanGroup);

    //  Return valid target
    //
    return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_forwards);
}

//--------------------------------------------------------------------------------------------------
//fixed MinToCastUnstableMutation (int player) {
//    return AIAbilityFixed(player, c_AB_NeuralParasite, c_fieldEnergyCost) +
//           AIAbilityFixed(player, c_AB_UnstableMutation, c_fieldEnergyCost);
//}
//
//order UnstableMutation (int player, unit aiUnit, unitgroup scanGroup, marker mark, bool lowVitality) {
//    order ord;
//    int energy;
//    aifilter filter;
//    unitgroup enemyCasters;
//   
//    ord = AICreateOrder(player, c_AB_UnstableMutation, 0);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return null;
//    }
//
//    //  Save energy for neural parasite
//    energy = UnitGetPropertyInt(aiUnit, c_unitPropEnergy, c_unitPropCurrent);
//    if (energy < MinToCastUnstableMutation(player)) {
//        return null;
//    }
//
//    //  For now, only use on enemy casters, since we want them dead and they can't attack our units.
//    filter = AIFilter(player);
//    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_UnstableMutation, c_fieldTargetFiltersAB)));
//    AISetFilterAlliance(filter, c_playerGroupEnemy);
//    AISetFilterLife(filter, c_noMin, c_noMax);
//    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
//    AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, c_BF_UnstableMutation);
//    enemyCasters = AIGetFilterGroup(filter, scanGroup);
//
//    enemyCasters = AIFilterCasters(enemyCasters);
//
//    return AIUnitGroupGetValidOrder(enemyCasters, ord, aiUnit, c_forwards);
//}

//---------------------------------------------------------------------------------------------
fixed MinToCastInfestedTerrans (int player) {
    return AIAbilityFixed(player, c_AB_NeuralParasite, c_fieldEnergyCost) +
           AIAbilityFixed(player, c_AB_InfestedTerrans, c_fieldEnergyCost);
}

order InfestedTerrans (int player, unit aiUnit, unitgroup scanGroup, bool lowVitality) {
    order ord;
    int energy;
    aifilter filter;
    point castPoint;
    unit target;
    unitgroup targetGroup;  //  Don't want to modify the scanGroup... still need it for neural parasite.
   
    ord = AICreateOrder(player, c_AB_InfestedTerrans, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    //  Reserve 125 mana for neural parasite
    energy = UnitGetPropertyInt(aiUnit, c_unitPropEnergy, c_unitPropCurrent);
    if (energy < MinToCastInfestedTerrans(player) && !lowVitality) {
        return null;
    }

    //  Cast on top of the weakest unit.
    //
    filter = AIFilter(player);
    AISetFilterAlliance(filter, c_playerGroupEnemy);
    AISetFilterBits(filter, UnitFilterStr(AIWeaponStr(player, c_WE_InfestedTerran, c_fieldTargetFilters)));
    AISetFilterRange(filter, aiUnit, AIAbilityFixed(player, c_AB_InfestedTerrans, c_fieldRange0) + 1);
    AISetFilterLifePercent(filter, c_noMin, c_noMax);
    targetGroup = AIGetFilterGroup(filter, scanGroup);

    //  Check for valid target
    //
    target = UnitGroupUnit(targetGroup, UnitGroupCount(targetGroup, c_unitCountAll));
    if (target == null) {
        return null;
    }
   
    castPoint = AIPlacementNearbyFindTest(player, UnitGetPosition(target), 5.0, c_ZU_InfestedTerranEgg);
    if (castPoint == c_nullPoint) {
        return null;
    }

    OrderSetTargetPoint(ord, castPoint);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    return ord;
}

//---------------------------------------------------------------------------------------------
const fixed enemyMultiplierInfestor = 1.25;
const fixed minThreshold = 100.0;
const int lowVitalityPercent = 75;

void AIThinkInfestor (int player, unit aiUnit, unitgroup scanGroup) {
    //  **Infestor disease in TargetFindData.xml** Currently not in the game.
    marker mark;
    marker gameMark;
    order ord;
    unitfilter f;
    bool lowVitality;

    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }

    //  Low Vitality = less than 75%, since it takes a WHILE for the infested marines to hatch
    lowVitality = (UnitGetPropertyInt(aiUnit, c_unitPropVitalityPercent, c_unitPropCurrent) < lowVitalityPercent);
   
    //  If we outnumber the enemy, don't even bother casting any spell, unless we are less than 75%
    if (!lowVitality) {
        //  If we have greater than 1.25 * the enemies forces, we outnumber, so don't cast.
        f = UnitFilterStr("-;Missile,Dead,Stasis,Worker");
        if (AIAllyEnemyRatio(player, UnitGetPosition(aiUnit), f, AIRangeInfestor(player, aiUnit), minThreshold) > enemyMultiplierInfestor) {
            return;
        }
    }

    //  This must preserve scanGroup for Neural Parasite check.
    ord = InfestedTerrans(player, aiUnit, scanGroup, lowVitality);
    if (ord != null) {
        AICast(aiUnit, ord, c_noMarker, c_castRetreat);
    }

    //mark = AIMarker(aiUnit, c_MK_UnstableMutation);
    //ord = UnstableMutation(player, aiUnit, scanGroup, mark, lowVitality);
    //if (ord != null) {
    //    AICast(aiUnit, ord, mark, c_castRetreat);
    //    return;
    //}
   
    mark = AIMarker(aiUnit, c_MK_NeuralParasite);
    gameMark = AIMarker(aiUnit, c_MK_GameNeuralParasite);
    ord = NeuralParasite(player, aiUnit, scanGroup, mark, gameMark, lowVitality);
    if (ord != null) {
        AICast(aiUnit, ord, mark, c_castRetreat);
        return;
    }
}

//--------------------------------------------------------------------------------------------------
//  *** BANELING ***
//--------------------------------------------------------------------------------------------------
const fixed c_banelingRange = 8;
fixed AIRangeBaneling (int player, unit aiUnit) {
    return c_banelingRange;
}

//---------------------------------------------------------------------------------------------
order SapStructure (int player, unit aiUnit, unitgroup scanGroup, marker mark) {
    order ord;
    unitgroup enemyBuildingGroup;
    unitgroup enemyUnitGroup;
    unit closestEnemy;
    point banelingPosition;
    fixed distanceToEnemy;
    unit closestEnemyBuilding;
   
    bool airAllies = false;
    int damage;

    aifilter buildingFilter;
    aifilter unitFilter;

    //  Create and check the validity of order.
    //
    ord = AICreateOrder(player, c_AB_SapStructure, 0);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    //  Create non-building filter to find the closest enemy.
    //
    unitFilter = AIFilter(player);
    AISetFilterAlliance(unitFilter, c_playerGroupEnemy);
    AISetFilterBits(unitFilter, UnitFilterStr(AIEffectStr(player, c_EF_BaneUnit, c_fieldFilters)));
    AISetFilterRange(unitFilter, aiUnit, AIRangeBaneling(player, aiUnit));
    enemyUnitGroup = AIGetFilterGroup(unitFilter, scanGroup);
   
    banelingPosition = UnitGetPosition(aiUnit);
    closestEnemy = UnitGroupNearestUnit(enemyUnitGroup, banelingPosition);
    if (closestEnemy != null) {
        distanceToEnemy = DistanceBetweenPoints(banelingPosition, UnitGetPosition(closestEnemy));
    }
    else {
        distanceToEnemy = AIRangeBaneling(player, aiUnit);  //  Default distance.
    }

    //  Create building filter.
    //
    buildingFilter = AIFilter(player);
    AISetFilterAlliance(buildingFilter, c_playerGroupEnemy);
    AISetFilterBits(buildingFilter, UnitFilterStr(AIAbilityStr(player, c_AB_SapStructure, c_fieldTargetFiltersAB)));
    AISetFilterRange(buildingFilter, aiUnit, distanceToEnemy);
    damage = AIEffectInt(player, c_EF_BaneBuilding, c_fieldAmount);
    AISetFilterLifePerMarker(buildingFilter, damage, mark);
    airAllies = AINearbyPlaneTest(UnitGetPosition(aiUnit), player, AIRangeBaneling(player, aiUnit), c_planeAir, c_unitAllianceAlly);
    AISetFilterCanAttackAlly(buildingFilter, c_groundAlliesNearby, airAllies); //  Ground allies always true, since banelings are ground units.
    enemyBuildingGroup = AIGetFilterGroup(buildingFilter, scanGroup);

    closestEnemyBuilding = UnitGroupNearestUnit(enemyBuildingGroup, banelingPosition);
    if (closestEnemyBuilding == null) {
        return null;
    }

    OrderSetTargetUnit(ord, closestEnemyBuilding);
    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }

    return ord;
}

//---------------------------------------------------------------------------------------------
void AIThinkBaneling (int player, unit aiUnit, unitgroup scanGroup) {
    marker mark;
    order ord;
   
    if (AIEvalTacticalData(aiUnit, null)) {
        return;
    }

    mark = AIMarker(aiUnit, c_MK_SapStructure);
    ord = SapStructure(player, aiUnit, scanGroup, mark);
    if (ord != null) {
        AICast(aiUnit, ord, mark, c_castHold);
    }
}

//--------------------------------------------------------------------------------------------------
//  *** OVERSEER ***
//--------------------------------------------------------------------------------------------------
fixed AIRangeOverseer (int player, unit aiUnit) { 
    return AIUnitFixed(player, c_ZU_Overseer, c_fieldSightDawn) + 1;

    //return MaxF(MaxF(AIAbilityFixed(player, c_AB_FungalGrowth, c_fieldRange0),
    //    AIAbilityFixed(player, c_AB_AcidSpores, c_fieldRange0)),
    //    AIAbilityFixed(player, c_AB_Changeling, c_fieldRange0)) + 1;
}

//fixed MinToCastFungalGrowth (int player) {
//    return AIAbilityFixed(player, c_AB_FungalGrowth, c_fieldEnergyCost) +
//           AIAbilityFixed(player, c_AB_AcidSpores, c_fieldEnergyCost);
//}

//order FungalGrowth (int player, unit aiUnit, marker mark, marker gameMark, unitgroup scanGroup) {
//    order ord;
//    fixed energy;
//    aifilter filter;
//    bool groundAllies;
//    unitgroup enemyGroup;
   
//    ord = AICreateOrder(player, c_AB_FungalGrowth, 0);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return null;
//    }

//    //  Save energy for acid spores
//    energy = UnitGetPropertyFixed(aiUnit, c_unitPropEnergy, c_unitPropCurrent);
//    if (energy < MinToCastFungalGrowth(player)) {
//        return null;
//    }

//   
//    groundAllies = AINearbyPlaneTest(UnitGetPosition(aiUnit), player, AIRangeOverseer(player, aiUnit), c_planeGround, c_unitAllianceAlly);

//    //  Cast on strongest enemy that can attack our allies
//    filter = AIFilter(player);
//    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, c_AB_FungalGrowth, c_fieldTargetFiltersAB)));
//    AISetFilterCanAttackAlly(filter, groundAllies, c_airAlliesNearby);
//    AISetFilterLife(filter, c_noMin, c_noMax);
//    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, mark);
//    AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, c_BF_FungalGrowth);
//    enemyGroup = AIGetFilterGroup(filter, scanGroup);
//
//    AISetFilterMarker(filter, c_noMarkersMin, c_noMarkersMax, gameMark);
//    enemyGroup = AIGetFilterGroup(filter, scanGroup);
//
//    return AIUnitGroupGetValidOrder(enemyGroup, ord, aiUnit, c_forwards);
//}

//--------------------------------------------------------------------------------------------------
//const int c_minAlliesCanAttack = 3;

//order AcidSpores (int player, unit aiUnit, unitgroup scanGroup) {
//    order ord;
//    aifilter filter;
//    bool groundEnemies;
//    bool airEnemies;
//    region r;
//    unitgroup allyGroup;
//    int allyCount;
//    point position;
//
//    ord = AICreateOrder(player, c_AB_AcidSpores, 0);
//    if (!UnitOrderIsValid(aiUnit, ord)) {
//        return null;
//    }
//
//    DebugVarInt("const", c_planeAir);
//
//    position = UnitGetPosition(aiUnit);
//    //  Only use it if we have some units that will be able to attack the enemy.
//    groundEnemies = AINearbyPlaneTest(position, player, AIRangeOverseer(player, aiUnit), c_planeGround, c_unitAllianceEnemy);
//    airEnemies = AINearbyPlaneTest(position, player, AIRangeOverseer(player, aiUnit), c_planeAir, c_unitAllianceEnemy);
//
//    //AIEffectFixed(player, c_EF_AcidSporesArea, c_fieldAreaRadius0)
//    //UnitFilterStr(AIAbilityStr(player, c_AB_AcidSpores, c_fieldTargetFiltersAB)
//    DebugVarBool("grnd", groundEnemies);
//    DebugVarBool("air", airEnemies);
//
//    r = RegionCircle(UnitGetPosition(aiUnit), AIRangeOverseer(player, aiUnit));
//    allyGroup = UnitGroupAlliance(player,
//                                  c_unitAllianceAlly,
//                                  r,
//                                  null,
//                                  c_noMaxCount)
//                                  ;
//
//    AISetFilterCanAttackAlly(filter, groundEnemies, airEnemies);
//    allyGroup = AIGetFilterGroup(filter, allyGroup);
//    allyCount = UnitGroupCount(allyGroup, c_unitCountAll);
//
//    DebugVarInt("allyCount:", allyCount);
//
//    if (allyCount < c_minAlliesCanAttack) {
//        return null;
//    }
//
//    return null;
//}