//==================================================================================================
//  Protoss Hard Skirmish AI
//  This is a generated file. Do not edit directly.
//==================================================================================================

//--------------------------------------------------------------------------------------------------
//  ProtossUpgradesHard
//--------------------------------------------------------------------------------------------------
void ProtossUpgradesHard (int player, int phase) {
    int buildType;

    if (phase < c_ldPhaseLate) {
        buildType = AIGetUserInt(player, c_openingBuildType);
    } else {
        buildType = AIGetUserInt(player, c_lateGameBuildType);
    }

    if (buildType != e_buildType_Air) {

        if (AITechCount(player, c_PB_CyberneticsCore, c_techCountCompleteOnly) > 0) {
            AISetStock( player, 1, c_PR_WarpGateResearch );
        }

        if (phase >= c_ldPhaseMid) {

            AISetStock( player, 1, c_PR_GroundWeapons1 );
            AISetStock( player, 1, c_PR_GroundArmor1 );
            if (AITechCount(player, c_PU_Zealot, c_techCountInProgressOrBetter) >= 8) {
                AISetStock( player, 1, c_PR_ZealotCharge );
            }
            if (AITechCount(player, c_PU_Stalker, c_techCountInProgressOrBetter) >= 6) {
                AISetStock( player, 1, c_PR_StalkerBlink );
            }

            if (phase >= c_ldPhaseLate) {

                AISetStock( player, 1, c_PR_GroundWeapons2 );
                AISetStock( player, 1, c_PR_GroundArmor2 );
                AISetStock( player, 1, c_PR_Shields2 );
                AISetStock( player, 1, c_PR_ObserverSpeed );
                AISetStock( player, 1, c_PR_WarpPrismSpeed );
                AISetStock( player, 1, c_PR_AirWeapons1 );
                AISetStock( player, 1, c_PR_AirArmor1 );

                if (phase >= c_ldPhaseFinal) {

                    if (AITechCount(player, c_PU_Colossus, c_techCountInProgressOrBetter) >= 2) {
                        AISetStock( player, 1, c_PR_ColossusRange );
                    }
                    if (AITechCount(player, c_PU_HighTemplar, c_techCountInProgressOrBetter) >= 1) {
                        AISetStock( player, 1, c_PR_HighTemplarPsiStorm );
                    }
                    if (AITechCount(player, c_PU_Sentry, c_techCountInProgressOrBetter) >= 6) {
                        AISetStock( player, 1, c_PR_SentryHallucination );
                    }
                    AISetStock( player, 1, c_PR_GroundWeapons3 );
                    AISetStock( player, 1, c_PR_GroundArmor3 );
                    AISetStock( player, 1, c_PR_Shields3 );
                    if (AITechCount(player, c_PU_Phoenix, c_techCountInProgressOrBetter) >= 4) {
                        AISetStock( player, 1, c_PR_PhoenixRange );
                    }
                    if (AITechCount(player, c_PU_Carrier, c_techCountInProgressOrBetter) >= 1) {
                        AISetStock( player, 1, c_PR_CarrierLaunch );
                    }
                    AISetStock( player, 1, c_PR_AirWeapons3 );
                    AISetStock( player, 1, c_PR_AirArmor3 );

                }
            }
        }
    }
    else {  // (buildType == e_buildType_Air) 


        if (phase >= c_ldPhaseMid) {

            AISetStock( player, 1, c_PR_AirWeapons1 );
            AISetStock( player, 1, c_PR_AirArmor1 );

            if (phase >= c_ldPhaseLate) {

                AISetStock( player, 1, c_PR_ObserverSpeed );
                AISetStock( player, 1, c_PR_WarpPrismSpeed );
                if (AITechCount(player, c_PU_Phoenix, c_techCountInProgressOrBetter) >= 4) {
                    AISetStock( player, 1, c_PR_PhoenixRange );
                }
                if (AITechCount(player, c_PU_Carrier, c_techCountInProgressOrBetter) >= 1) {
                    AISetStock( player, 1, c_PR_CarrierLaunch );
                }
                AISetStock( player, 1, c_PR_AirWeapons2 );
                AISetStock( player, 1, c_PR_AirArmor2 );
                AISetStock( player, 1, c_PR_Shields2 );

                if (phase >= c_ldPhaseFinal) {

                    AISetStock( player, 1, c_PR_AirWeapons3 );
                    AISetStock( player, 1, c_PR_AirArmor3 );
                    AISetStock( player, 1, c_PR_Shields3 );
                    AISetStock( player, 1, c_PR_WarpGateResearch );
                    if (AITechCount(player, c_PU_Zealot, c_techCountInProgressOrBetter) >= 6) {
                        AISetStock( player, 1, c_PR_ZealotCharge );
                    }
                    if (AITechCount(player, c_PU_Stalker, c_techCountInProgressOrBetter) >= 6) {
                        AISetStock( player, 1, c_PR_StalkerBlink );
                    }
                    if (AITechCount(player, c_PU_Colossus, c_techCountInProgressOrBetter) >= 2) {
                        AISetStock( player, 1, c_PR_ColossusRange );
                    }
                    if (AITechCount(player, c_PU_HighTemplar, c_techCountInProgressOrBetter) >= 1) {
                        AISetStock( player, 1, c_PR_HighTemplarPsiStorm );
                    }
                    if (AITechCount(player, c_PU_Sentry, c_techCountInProgressOrBetter) >= 6) {
                        AISetStock( player, 1, c_PR_SentryHallucination );
                    }
                    AISetStock( player, 1, c_PR_GroundWeapons3 );
                    AISetStock( player, 1, c_PR_GroundArmor3 );

                }
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
//  ProtossOpenArmyInitHard
//--------------------------------------------------------------------------------------------------
int ProtossOpenArmyInitHard (int player, int buildType) {
    int armyRoll = 0;
    int duration = 0;

    if (buildType == e_buildType_Rush) {
        // next attack should be around 12 foodCost of about 640 resCost after 350 seconds
        armyRoll = RandomInt(1,6);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 3);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            // validation: 6 units above with average cost 12 food and 862 resources
            duration = 368 + RandomInt(-12, 18);
        }
        else if (armyRoll <= 5) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 4);
            // validation: 6 units above with average cost 12 food and 900 resources
            duration = 376 + RandomInt(-12, 18);
        }
        else if (armyRoll <= 6) {
            AIAddToStockArmy(player, c_PU_Zealot, 5);
            AIAddToStockArmy(player, c_PU_Stalker, 2);
            // validation: 7 units above with average cost 14 food and 850 resources
            duration = 373 + RandomInt(-12, 18);
        }
    }
    else if (buildType == e_buildType_Timing) {
        // next attack should be around 17 foodCost of about 1020 resCost after 400 seconds
        armyRoll = RandomInt(1,6);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 3);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            // validation: 9 units above with average cost 18 food and 1237 resources
            duration = 404 + RandomInt(-13, 20);
        }
        else if (armyRoll <= 5) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            // validation: 9 units above with average cost 18 food and 1349 resources
            duration = 420 + RandomInt(-14, 21);
        }
        else if (armyRoll <= 6) {
            AIAddToStockArmy(player, c_PU_Zealot, 5);
            AIAddToStockArmy(player, c_PU_Stalker, 3);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            // validation: 10 units above with average cost 20 food and 1299 resources
            duration = 424 + RandomInt(-14, 21);
        }
    }
    else if (buildType == e_buildType_Power) {
        // next attack should be around 24 foodCost of about 1400 resCost after 520 seconds
        armyRoll = RandomInt(1,6);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            // validation: 13 units above with average cost 26 food and 1825 resources
            duration = 544 + RandomInt(-18, 27);
        }
        else if (armyRoll <= 5) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 7);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            // validation: 12 units above with average cost 24 food and 1799 resources
            duration = 530 + RandomInt(-17, 26);
        }
        else if (armyRoll <= 6) {
            AIAddToStockArmy(player, c_PU_Zealot, 7);
            AIAddToStockArmy(player, c_PU_Stalker, 4);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            // validation: 14 units above with average cost 28 food and 1811 resources
            duration = 553 + RandomInt(-18, 27);
        }
    }
    else if (buildType == e_buildType_Macro) {
        // next attack should be around 12 foodCost of about 700 resCost after 240 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 1);
            // validation: 6 units above with average cost 12 food and 824 resources
            duration = 238 + RandomInt(-7, 11);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 1);
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            // validation: 6 units above with average cost 12 food and 975 resources
            duration = 256 + RandomInt(-8, 12);
        }
    }
    else if (buildType == e_buildType_Air) {
        // next attack should be around 14 foodCost of about 1400 resCost after 540 seconds
        armyRoll = RandomInt(1,3);
        if (armyRoll <= 2) {
            AIAddToStockArmy(player, c_PU_VoidRay, 2);
            AIAddToStockArmy(player, c_PU_Phoenix, 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Phoenix), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Phoenix), 1);
            // validation: 5 units above with average cost 13 food and 1550 resources
            duration = 554 + RandomInt(-18, 27);
        }
        else if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_VoidRay, 4);
            AIAddToStockArmy(player, c_PU_Phoenix, 1);
            // validation: 5 units above with average cost 14 food and 1650 resources
            duration = 580 + RandomInt(-19, 29);
        }
    }
    else {
        return ProtossOpenArmyInitHard(player, e_buildType_Macro);
    }

    AISetUserInt(player, c_ldNextArmyRoll, armyRoll);
    return duration;
}

//--------------------------------------------------------------------------------------------------
//  ProtossOpenInitHard
//--------------------------------------------------------------------------------------------------
void ProtossOpenInitHard (int player) {
    int buildType = 0;

    AIChooseNextOpeningBuild(player);
    buildType = AIGetUserInt(player, c_openingBuildType);
    AISetupNextArmy (player, buildType, c_ldPhaseOpen, ProtossOpenArmyInitHard);

    AISetMainState(player, e_mainState_Open, e_mainSubState_Build);
}

//--------------------------------------------------------------------------------------------------
//  ProtossOpenBuildHard
//--------------------------------------------------------------------------------------------------
void ProtossOpenBuildHard (int player) {
    int buildType = AIGetUserInt(player, c_openingBuildType);
    int armyRoll = AIGetUserInt(player, c_ldNextArmyRoll);
    int timeOut = AIGetUserInt(player, c_nextArmyTime);
    bool nextState = false;

    if (AIHasPlayerChangedCurrentOpening(player)) {
        AISetMainState(player, e_mainState_Open, e_mainSubState_Init);
        return;
    }

    AIClearStock(player);

    if (buildType == e_buildType_Rush) {
        AISetStock( player, 1, c_PB_Nexus );
        AISetStock( player, 9, c_PU_Probe );
        AISetStock( player, 1, c_PB_Pylon );
        AISetStock( player, 13, c_PU_Probe );
        AISetStock( player, 1, c_PB_Gateway );
        AISetStock( player, 14, c_PU_Probe );
        AISetStock( player, 1, c_PB_Assimilator );
        AISetStock( player, 16, c_PU_Probe );
        AISetStock( player, 2, c_PB_Pylon );
        AISetStock( player, 18, c_PU_Probe );
        AISetStock( player, 1, c_PB_CyberneticsCore );
        AISetStock( player, 1, c_PU_Zealot );
        AISetStockUnitNext( player, 20, c_PU_Probe, c_stockIdle );
        AISetStockFarms( player, c_PB_Pylon, c_stockNormalFarms);
        AISetStock( player, 1, c_PU_Stalker );
        AISetStock( player, 2, c_PB_Gateway );
    }
    else if (buildType == e_buildType_Timing) {
        AISetStock( player, 1, c_PB_Nexus );
        AISetStock( player, 9, c_PU_Probe );
        AISetStock( player, 1, c_PB_Pylon );
        AISetStock( player, 13, c_PU_Probe );
        AISetStock( player, 1, c_PB_Gateway );
        AISetStock( player, 14, c_PU_Probe );
        AISetStock( player, 1, c_PB_Assimilator );
        AISetStock( player, 16, c_PU_Probe );
        AISetStock( player, 2, c_PB_Pylon );
        AISetStock( player, 18, c_PU_Probe );
        AISetStock( player, 1, c_PB_CyberneticsCore );
        AISetStock( player, 1, c_PU_Zealot );
        AISetStockUnitNext( player, 20, c_PU_Probe, c_stockIdle );
        AISetStockFarms( player, c_PB_Pylon, c_stockNormalFarms);
        AISetStock( player, 1, c_PU_Stalker );
        AISetStock( player, 2, c_PB_Gateway );
        AISetStock( player, 2, c_PB_Assimilator );
    }
    else if (buildType == e_buildType_Power) {
        AISetStock( player, 1, c_PB_Nexus );
        AISetStock( player, 9, c_PU_Probe );
        AISetStock( player, 1, c_PB_Pylon );
        AISetStock( player, 13, c_PU_Probe );
        AISetStock( player, 1, c_PB_Gateway );
        AISetStock( player, 14, c_PU_Probe );
        AISetStock( player, 1, c_PB_Assimilator );
        AISetStock( player, 16, c_PU_Probe );
        AISetStock( player, 2, c_PB_Pylon );
        AISetStock( player, 18, c_PU_Probe );
        AISetStock( player, 1, c_PB_CyberneticsCore );
        AISetStock( player, 1, c_PU_Zealot );
        AISetStockUnitNext( player, 20, c_PU_Probe, c_stockIdle );
        AISetStockFarms( player, c_PB_Pylon, c_stockNormalFarms);
        AISetStock( player, 1, c_PU_Stalker );
        AISetStock( player, 2, c_PB_Gateway );
        AISetStock( player, 2, c_PB_Assimilator );
    }
    else if (buildType == e_buildType_Macro) {
        AISetStock( player, 1, c_PB_Nexus );
        AISetStock( player, 9, c_PU_Probe );
        AISetStock( player, 1, c_PB_Pylon );
        AISetStock( player, 13, c_PU_Probe );
        AISetStock( player, 1, c_PB_Gateway );
        AISetStock( player, 16, c_PU_Probe );
        if (ProtossMacroExpand(player)) { return; }
        AISetStock( player, 1, c_PU_Zealot );
        AISetStock( player, 2, c_PB_Pylon );
        AISetStock( player, 1, c_PB_Assimilator );
        AISetStock( player, 1, c_PB_CyberneticsCore );
        AISetStock( player, 18, c_PU_Probe );
        AISetStockFarms( player, c_PB_Pylon, c_stockNormalFarms);
        AISetStockUnitNext( player, 30, c_PU_Probe, c_stockIdle );
        AISetStock( player, 1, c_PU_Stalker );
        AISetStock( player, 2, c_PB_Gateway );
        AISetStock( player, 2, c_PB_Assimilator );
    }
    else if (buildType == e_buildType_Air) {
        AISetStock( player, 1, c_PB_Nexus );
        AISetStock( player, 9, c_PU_Probe );
        AISetStock( player, 1, c_PB_Pylon );
        AISetStock( player, 16, c_PU_Probe );
        if (ProtossMacroExpand(player)) { return; }
        AISetStock( player, 1, c_PB_Assimilator );
        AISetStock( player, 17, c_PU_Probe );
        AISetStock( player, 1, c_PB_Gateway );
        AISetStock( player, 18, c_PU_Probe );
        AISetStock( player, 2, c_PB_Assimilator );
        AISetStock( player, 2, c_PB_Pylon );
        AISetStockUnitNext( player, 30, c_PU_Probe, c_stockIdle );
        AISetStockFarms( player, c_PB_Pylon, c_stockNormalFarms);
        AISetStock( player, 1, c_PB_CyberneticsCore );
        AISetStock( player, 2, c_PB_Stargate );
        AISetStock( player, 3, c_PB_Pylon );
    }

    ProtossBasicDefenseStock(player);

    AISetStockArmyScale(player, 0.35);

    ProtossGeneralBuild(player, c_ldPhaseOpen);

    AISetStockArmyScale(player, 0.70);

    ProtossUpgradesHard(player, c_ldPhaseOpen);

    AISetStockArmyScale(player, 1.00);

    AILDStockWorkers( player, 30, c_PU_Probe);

    ProtossTechUp(player, c_ldPhaseOpen);

    if (buildType == e_buildType_Air) {
        AISetStock( player, 1, c_PU_WarpPrism );
    }

    AIEnableStock(player);

    AILDGeneralSharedLogic(player);

    nextState = AITestTimeout(player, timeOut, e_mainState_Mid, e_mainSubState_Init);
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidArmyInitHard
//--------------------------------------------------------------------------------------------------
int ProtossMidArmyInitHard (int player, int buildType) {
    int armyRoll = 0;
    int duration = 0;

    if (buildType == e_buildType_Rush) {
        // next attack should be around 25 foodCost of about 1500 resCost after 300 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 4);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            // validation: 13 units above with average cost 26 food and 1900 resources
            duration = 307 + RandomInt(-10, 15);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 6);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            // validation: 12 units above with average cost 24 food and 1875 resources
            duration = 300 + RandomInt(-10, 15);
        }
    }
    else if (buildType == e_buildType_Timing) {
        // next attack should be around 30 foodCost of about 1800 resCost after 320 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 3);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            // validation: 15 units above with average cost 30 food and 2225 resources
            duration = 320 + RandomInt(-10, 16);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 8);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            // validation: 15 units above with average cost 30 food and 2325 resources
            duration = 326 + RandomInt(-10, 16);
        }
    }
    else if (buildType == e_buildType_Power) {
        // next attack should be around 32 foodCost of about 1920 resCost after 340 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Zealot, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 1);
            // validation: 16 units above with average cost 32 food and 2412 resources
            duration = 343 + RandomInt(-11, 17);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 9);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            // validation: 16 units above with average cost 32 food and 2500 resources
            duration = 349 + RandomInt(-11, 17);
        }
    }
    else if (buildType == e_buildType_Macro) {
        // next attack should be around 42 foodCost of about 2700 resCost after 540 seconds
        // note: mid macro build did not send opening attack at 240 time
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 2) {
            AIAddToStockArmy(player, c_PU_Zealot, 5);
            AIAddToStockArmy(player, c_PU_Stalker, 7);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 3);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 3);
            // validation: 20 units above with average cost 45 food and 3562 resources
            duration = 576 + RandomInt(-27, 40);
        }
        else if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 9);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            // validation: 18 units above with average cost 42 food and 3575 resources
            duration = 568 + RandomInt(-26, 40);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_Immortal, 3);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            // validation: 16 units above with average cost 43 food and 3625 resources
            duration = 578 + RandomInt(-27, 40);
        }
    }
    else if (buildType == e_buildType_Air) {
        // next attack should be around 22 foodCost of about 2200 resCost after 320 seconds
        armyRoll = RandomInt(1,5);
        if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_VoidRay, 4);
            AIAddToStockArmy(player, c_PU_Phoenix, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Phoenix), 1);
            // validation: 7 units above with average cost 19 food and 2200 resources
            duration = 310 + RandomInt(-10, 15);
        }
        else if (armyRoll <= 5) {
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            AIAddToStockArmy(player, c_PU_VoidRay, 2);
            AIAddToStockArmy(player, c_PU_Phoenix, 2);
            AIAddToStockArmy(player, c_PU_VoidRay, 1);
            // validation: 10 units above with average cost 23 food and 2425 resources
            duration = 337 + RandomInt(-11, 16);
        }
    }
    else {
        return ProtossMidArmyInitHard(player, e_buildType_Macro);
    }

    AISetUserInt(player, c_ldNextArmyRoll, armyRoll);
    return duration;
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidInitHard
//--------------------------------------------------------------------------------------------------
void ProtossMidInitHard (int player) {
    int buildType = 0;

    AIChooseNextOpeningBuild(player);
    buildType = AIGetUserInt(player, c_openingBuildType);
    AISetupNextArmy (player, buildType, c_ldPhaseMid, ProtossMidArmyInitHard);

    AISetFlag(player, e_flagsScouting, true);
    AISetFlag(player, e_flagsLateScout, true);
    AISetFlag(player, e_flagsDetect, true);

    AISetMainState(player, e_mainState_Mid, e_mainSubState_Build);
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidBuildHard
//--------------------------------------------------------------------------------------------------
void ProtossMidBuildHard (int player) {
    int buildType = AIGetUserInt(player, c_openingBuildType);
    int armyRoll = AIGetUserInt(player, c_ldNextArmyRoll);
    int timeOut = AIGetUserInt(player, c_nextArmyTime);
    bool nextState = false;

    if (AIHasPlayerChangedCurrentOpening(player)) {
        AISetMainState(player, e_mainState_Mid, e_mainSubState_Init);
        return;
    }

    AIClearStock(player);

    ProtossBasicEconomy(player, c_ldPhaseMid, 20);
    AISetStock( player, 3, c_PB_Pylon );
    if (buildType == e_buildType_Air) {
        AISetStock( player, 1, c_PU_WarpPrism );
    }
    ProtossBasicExpansion(player, c_ldPhaseMid, 5500, 1500);

    ProtossBasicDefenseStock(player);

    AISetStockArmyScale(player, 0.35);

    ProtossGeneralBuild(player, c_ldPhaseMid);

    AILDStockWorkers( player, 30, c_PU_Probe);

    if (buildType == e_buildType_Rush) {
        AISetStock( player, 1, c_PR_WarpGateResearch );
        AISetStockUnitNext( player, 2, c_PB_Gateway, c_stockIdle );
    }
    else if (buildType == e_buildType_Timing) {
        AISetStock( player, 1, c_PR_WarpGateResearch );
        AISetStockUnitNext( player, 3, c_PB_Gateway, c_stockIdle );
    }
    else if (buildType == e_buildType_Power) {
        AISetStock( player, 1, c_PR_WarpGateResearch );
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 3, c_PB_Gateway, c_stockIdle );
    }
    else if (buildType == e_buildType_Macro) {
        AISetStock( player, 1, c_PR_WarpGateResearch );
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 3, c_PB_Gateway, c_stockIdle );
    }
    else if (buildType == e_buildType_Air) {
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 3, c_PB_Stargate, c_stockIdle );
    }

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

    AISetStockArmyScale(player, 0.70);

    AILDStockWorkers( player, 60, c_PU_Probe);

    ProtossUpgradesHard(player, c_ldPhaseMid);

    AISetStockArmyScale(player, 1.00);

    AISetStock( player, 2, c_PU_Observer );

    ProtossTechUp(player, c_ldPhaseMid);

    AIEnableStock(player);

    AILDGeneralSharedLogic(player);

    nextState = AITestTimeout(player, timeOut, e_mainState_Late, e_mainSubState_Init);
}

//--------------------------------------------------------------------------------------------------
//  ProtossLateArmyInitHard
//--------------------------------------------------------------------------------------------------
int ProtossLateArmyInitHard (int player, int buildType) {
    int armyRoll = 0;
    int duration = 0;

    if (buildType == e_buildType_Core) {
        // next attack should be around 41 foodCost of about 2600 resCost after 270 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Phoenix, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            // validation: 15 units above with average cost 33 food and 2700 resources
            duration = 262 + RandomInt(-8, 13);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 10);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Immortal, c_PU_VoidRay), 2);
            // validation: 18 units above with average cost 39 food and 3150 resources
            duration = 290 + RandomInt(-9, 14);
        }
    }
    else if (buildType == e_buildType_HighTech) {
        // next attack should be around 44 foodCost of about 2800 resCost after 290 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 1);
            AIAddToStockArmy(player, c_PU_Stalker, 3);
            AIAddToStockArmy(player, c_PU_Sentry, 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 3);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 3);
            // validation: 12 units above with average cost 41 food and 3637 resources
            duration = 321 + RandomInt(-10, 16);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 2);
            AIAddToStockArmy(player, c_PU_Sentry, 1);
            AIAddToStockArmy(player, c_PU_Archon, 4);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            // validation: 11 units above with average cost 36 food and 3250 resources
            duration = 296 + RandomInt(-9, 14);
        }
    }
    else if (buildType == e_buildType_Special) {
        // next attack should be around 46 foodCost of about 3000 resCost after 310 seconds
        armyRoll = RandomInt(1,5);
        if (armyRoll <= 2) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 6);
            AIAddToStockArmy(player, c_PU_Sentry, 6);
            AIAddToStockArmy(player, c_PU_HighTemplar, 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            // validation: 19 units above with average cost 43 food and 3650 resources
            duration = 332 + RandomInt(-11, 16);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 4);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_HighTemplar, 4);
            AIAddToStockArmy(player, c_PU_Archon, 3);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            // validation: 18 units above with average cost 44 food and 3800 resources
            duration = 341 + RandomInt(-11, 17);
        }
        else if (armyRoll <= 5) {
            AIAddToStockArmy(player, c_PU_Zealot, 1);
            AIAddToStockArmy(player, c_PU_Stalker, 2);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_HighTemplar, 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, c_PU_DarkTemplar, 4);
            // validation: 14 units above with average cost 33 food and 3250 resources
            duration = 299 + RandomInt(-9, 14);
        }
    }
    else if (buildType == e_buildType_Mixed) {
        // next attack should be around 49 foodCost of about 3200 resCost after 330 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 3);
            AIAddToStockArmy(player, c_PU_Stalker, 5);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Phoenix, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            // validation: 17 units above with average cost 47 food and 4100 resources
            duration = 365 + RandomInt(-12, 18);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 1);
            AIAddToStockArmy(player, c_PU_Stalker, 2);
            AIAddToStockArmy(player, c_PU_Sentry, 1);
            AIAddToStockArmy(player, c_PU_Archon, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            // validation: 11 units above with average cost 39 food and 3600 resources
            duration = 330 + RandomInt(-11, 16);
        }
    }
    else if (buildType == e_buildType_Air) {
        // next attack should be around 32 foodCost of about 3200 resCost after 290 seconds
        armyRoll = RandomInt(1,4);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_VoidRay, 3);
            AIAddToStockArmy(player, c_PU_Phoenix, 1);
            AIAddToStockArmy(player, c_PU_Carrier, 4);
            // validation: 8 units above with average cost 35 food and 3700 resources
            duration = 315 + RandomInt(-10, 15);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_VoidRay, 3);
            AIAddToStockArmy(player, c_PU_Phoenix, 1);
            AIAddToStockArmy(player, c_PU_Carrier, 3);
            AIAddToStockArmy(player, c_PU_VoidRay, 1);
            // validation: 8 units above with average cost 32 food and 3450 resources
            duration = 299 + RandomInt(-9, 14);
        }
    }
    else {
        return ProtossLateArmyInitHard(player, e_buildType_Core);
    }

    AISetUserInt(player, c_ldNextArmyRoll, armyRoll);
    return duration;
}

//--------------------------------------------------------------------------------------------------
//  ProtossFinalArmyInitHard
//--------------------------------------------------------------------------------------------------
int ProtossFinalArmyInitHard (int player, int buildType) {
    int armyRoll = 0;
    int duration = 0;

    if (buildType == e_buildType_Core) {
        // next attack should be around 57 foodCost of about 3900 resCost after 250 seconds
        armyRoll = RandomInt(1,3);
        if (armyRoll <= 2) {
            AIAddToStockArmy(player, c_PU_Zealot, 6);
            AIAddToStockArmy(player, c_PU_Stalker, 8);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Phoenix, c_PU_Stalker), 2);
            // validation: 22 units above with average cost 47 food and 3750 resources
            duration = 236 + RandomInt(-7, 11);
        }
        else if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 6);
            AIAddToStockArmy(player, c_PU_Stalker, 8);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            // validation: 21 units above with average cost 55 food and 4500 resources
            duration = 263 + RandomInt(-8, 13);
        }
    }
    else if (buildType == e_buildType_HighTech) {
        // next attack should be around 61 foodCost of about 4200 resCost after 270 seconds
        armyRoll = RandomInt(1,3);
        if (armyRoll <= 2) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 8);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 4);
            // validation: 22 units above with average cost 63 food and 5325 resources
            duration = 302 + RandomInt(-10, 15);
        }
        else if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 3);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_Archon, 4);
            AIAddToStockArmy(player, c_PU_HighTemplar, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 2);
            // validation: 19 units above with average cost 57 food and 5025 resources
            duration = 287 + RandomInt(-9, 14);
        }
    }
    else if (buildType == e_buildType_Special) {
        // next attack should be around 64 foodCost of about 4500 resCost after 290 seconds
        armyRoll = RandomInt(1,5);
        if (armyRoll <= 2) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 6);
            AIAddToStockArmy(player, c_PU_Sentry, 6);
            AIAddToStockArmy(player, c_PU_HighTemplar, 2);
            AIAddToStockArmy(player, c_PU_Archon, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            // validation: 25 units above with average cost 67 food and 5750 resources
            duration = 326 + RandomInt(-10, 16);
        }
        else if (armyRoll <= 4) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 4);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_HighTemplar, 4);
            AIAddToStockArmy(player, c_PU_Archon, 4);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            // validation: 21 units above with average cost 63 food and 5800 resources
            duration = 323 + RandomInt(-10, 16);
        }
        else if (armyRoll <= 5) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 6);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_HighTemplar, 2);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, c_PU_DarkTemplar, 6);
            // validation: 23 units above with average cost 51 food and 4750 resources
            duration = 282 + RandomInt(-9, 14);
        }
    }
    else if (buildType == e_buildType_Mixed) {
        // next attack should be around 67 foodCost of about 4800 resCost after 310 seconds
        armyRoll = RandomInt(1,3);
        if (armyRoll <= 2) {
            AIAddToStockArmy(player, c_PU_Zealot, 4);
            AIAddToStockArmy(player, c_PU_Stalker, 7);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_Archon, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Sentry, c_PU_Stalker), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Phoenix, c_PU_Stalker), 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            // validation: 22 units above with average cost 59 food and 5112 resources
            duration = 310 + RandomInt(-10, 15);
        }
        else if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_Zealot, 2);
            AIAddToStockArmy(player, c_PU_Stalker, 2);
            AIAddToStockArmy(player, c_PU_Sentry, 2);
            AIAddToStockArmy(player, c_PU_HighTemplar, 4);
            AIAddToStockArmy(player, c_PU_Archon, 2);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_VoidRay, c_PU_Immortal), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            AIAddToStockArmy(player, AIPickFrom2(c_PU_Colossus, c_PU_Carrier), 1);
            AIAddToStockArmy(player, c_PU_Archon, 1);
            // validation: 19 units above with average cost 61 food and 5550 resources
            duration = 323 + RandomInt(-10, 16);
        }
    }
    else if (buildType == e_buildType_Air) {
        // next attack should be around 45 foodCost of about 4500 resCost after 270 seconds
        armyRoll = RandomInt(1,6);
        if (armyRoll <= 3) {
            AIAddToStockArmy(player, c_PU_VoidRay, 8);
            AIAddToStockArmy(player, c_PU_Phoenix, 2);
            AIAddToStockArmy(player, c_PU_Carrier, 3);
            // validation: 13 units above with average cost 46 food and 5100 resources
            duration = 286 + RandomInt(-9, 14);
        }
        else if (armyRoll <= 5) {
            AIAddToStockArmy(player, c_PU_VoidRay, 3);
            AIAddToStockArmy(player, c_PU_Phoenix, 1);
            AIAddToStockArmy(player, c_PU_Carrier, 6);
            // validation: 10 units above with average cost 47 food and 4900 resources
            duration = 282 + RandomInt(-9, 14);
        }
        else if (armyRoll <= 6) {
            AIAddToStockArmy(player, c_PU_Stalker, 12);
            AIAddToStockArmy(player, c_PU_VoidRay, 3);
            AIAddToStockArmy(player, c_PU_Phoenix, 1);
            AIAddToStockArmy(player, c_PU_Carrier, 1);
            AIAddToStockArmy(player, c_PU_VoidRay, 1);
            // validation: 18 units above with average cost 44 food and 4350 resources
            duration = 265 + RandomInt(-8, 13);
        }
    }
    else {
        return ProtossFinalArmyInitHard(player, e_buildType_Core);
    }

    AISetUserInt(player, c_ldNextArmyRoll, armyRoll);
    return duration;
}

//--------------------------------------------------------------------------------------------------
//  ProtossLateInitHard
//--------------------------------------------------------------------------------------------------
void ProtossLateInitHard (int player) {
    int lateLoop = AIGetUserInt(player, c_ldNumLateLoops);
    int curPhase = c_ldPhaseLate;
    int buildType = 0;

    AIChooseNextLateGameBuild(player);
    buildType = AIGetUserInt(player, c_lateGameBuildType);

    if (lateLoop <= 0) { // First Late Loop
        AISetupNextArmy (player, buildType, curPhase, ProtossLateArmyInitHard);
    } else {
        curPhase = c_ldPhaseFinal + lateLoop;
        AISetupNextArmy (player, buildType, curPhase, ProtossFinalArmyInitHard);
    }

    AISetFlag(player, e_flagsScouting, true);
    AISetFlag(player, e_flagsClearObs, true);
    AISetFlag(player, e_flagsLateScout, true);
    AISetFlag(player, e_flagsDetect, true);

    AISetMainState(player, e_mainState_Late, e_mainSubState_Build);
}

//--------------------------------------------------------------------------------------------------
//  ProtossLateBuildHard
//--------------------------------------------------------------------------------------------------
void ProtossLateBuildHard (int player) {
    int lateLoop = AIGetUserInt(player, c_ldNumLateLoops);
    int buildType = AIGetUserInt(player, c_lateGameBuildType);
    int armyRoll = AIGetUserInt(player, c_ldNextArmyRoll);
    int timeOut = AIGetUserInt(player, c_nextArmyTime);
    bool nextState = false;

    if (AIHasPlayerChangedCurrentLateGame(player)) {
        AISetMainState(player, e_mainState_Late, e_mainSubState_Init);
        return;
    }

    AIClearStock(player);

    ProtossBasicEconomy(player, c_ldPhaseLate, 20);
    AISetStock( player, 3, c_PB_Pylon );
    if (buildType == e_buildType_Air) {
        AISetStock( player, 1, c_PU_WarpPrism );
    }
    ProtossBasicExpansion(player, c_ldPhaseLate, 14000, 4500);

    ProtossBasicDefenseStock(player);

    AISetStockArmyScale(player, 0.35);

    ProtossGeneralBuild(player, c_ldPhaseLate);

    AILDStockWorkers( player, 30, c_PU_Probe);

    if (buildType == e_buildType_Core) {
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 5, c_PB_Gateway, c_stockIdle );
        AISetStockUnitNext( player, 2, c_PB_RoboticsFacility, c_stockIdle );
        AISetStockUnitNext( player, 3, c_PB_Stargate, c_stockIdle );
    }
    else if (buildType == e_buildType_HighTech) {
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 5, c_PB_Gateway, c_stockIdle );
        AISetStockUnitNext( player, 2, c_PB_RoboticsFacility, c_stockIdle );
        AISetStockUnitNext( player, 3, c_PB_Stargate, c_stockIdle );
    }
    else if (buildType == e_buildType_Special) {
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 5, c_PB_Gateway, c_stockIdle );
        AISetStockUnitNext( player, 2, c_PB_RoboticsFacility, c_stockIdle );
        AISetStockUnitNext( player, 3, c_PB_Stargate, c_stockIdle );
    }
    else if (buildType == e_buildType_Mixed) {
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 5, c_PB_Gateway, c_stockIdle );
        AISetStockUnitNext( player, 2, c_PB_RoboticsFacility, c_stockIdle );
        AISetStockUnitNext( player, 3, c_PB_Stargate, c_stockIdle );
    }
    else if (buildType == e_buildType_Air) {
        if (ProtossMacroExpand(player)) { return; }
        AISetStockUnitNext( player, 3, c_PB_Gateway, c_stockIdle );
        AISetStockUnitNext( player, 2, c_PB_RoboticsFacility, c_stockIdle );
        AISetStockUnitNext( player, 5, c_PB_Stargate, c_stockIdle );
    }

    AISetStock( player, 2, c_PU_Observer );

    AISetStockArmyScale(player, 0.70);

    AISetStock( player, 3, c_PU_WarpPrism );
    AISetStock( player, 3, c_PU_Observer );
    AISetStock( player, 2, c_PB_PhotonCannon );

    AISetStockArmyScale(player, 1.00);

    AILDStockWorkers( player, 60, c_PU_Probe);

    if (lateLoop <= 0) { // First Late Loop
        ProtossUpgradesHard(player, c_ldPhaseLate);
        ProtossTechUp(player, c_ldPhaseLate);
    } else { // Remaining Loops
        ProtossUpgradesHard(player, c_ldPhaseFinal);
        ProtossTechUp(player, c_ldPhaseFinal);
    }

    AIEnableStock(player);

    AILDGeneralSharedLogic(player);

    nextState = AITestTimeout(player, timeOut, e_mainState_Late, e_mainSubState_Init);
    if (nextState) {
        lateLoop += 1;
        AISetUserInt(player, c_ldNumLateLoops, lateLoop);
    }
}

//--------------------------------------------------------------------------------------------------
//  ProtossOpenHard
//--------------------------------------------------------------------------------------------------
void ProtossOpenHard (int player) {
    int mainSubState = AIState(player, e_mainSubState);

    if (mainSubState != e_mainSubState_Build) {
        ProtossOpenInitHard(player);
        mainSubState = AIState(player, e_mainSubState);
    }

    if (mainSubState == e_mainSubState_Build) { ProtossOpenBuildHard(player); }
    else { ErrorMeleeScript(player, "Invalid Open MainSubState ProtossHard"); }
}

//--------------------------------------------------------------------------------------------------
//  ProtossMidHard
//--------------------------------------------------------------------------------------------------
void ProtossMidHard (int player) {
    int mainSubState = AIState(player, e_mainSubState);

    if (mainSubState != e_mainSubState_Build) {
        ProtossMidInitHard(player);
        mainSubState = AIState(player, e_mainSubState);
    }

    if (mainSubState == e_mainSubState_Build) { ProtossMidBuildHard(player);  }
    else { ErrorMeleeScript(player, "Invalid Mid MainSubState ProtossHard"); }
}

//--------------------------------------------------------------------------------------------------
//  ProtossLateHard
//--------------------------------------------------------------------------------------------------
void ProtossLateHard (int player) {
    int mainSubState = AIState(player, e_mainSubState);

    if (mainSubState != e_mainSubState_Build) {
        ProtossLateInitHard(player);
        mainSubState = AIState(player, e_mainSubState);
    }

    if (mainSubState == e_mainSubState_Build) { ProtossLateBuildHard(player);  }
    else { ErrorMeleeScript(player, "Invalid Late MainSubState ProtossHard"); }
}

