Discussion Tatical AI - Make AI autoload firebats into bunkers

Dave312

Censored for your safe viewing
Reaction score
269
I have started looking into creating AI systems and unfortunately there seems to be very little information around on the subject. I have an AI controlled player which starts the game with some bunkers under its control and some marines and firebats placed next to them for the AI to autoload. The problem I'm having is that the marines are loaded into the bunkers but the firebats aren't. There is room left in the bunkers for the firebats to jump in.

I have managed to figure out that the autoload script for the bunker AI is specified in the AI: Tactical AI Function property on the bunker unit in the Data Editor. The value for this property is AIThinkBunker which references this function in the TactTerrAI.galaxy file (I have pasted the relevant script into the spoiler tags below). While I have no problem reading the galaxy script syntax, I can't seem to make head or tails as to why the marines will autoload but not the firebats?

Code:
void AIThinkBunker(int player, unit aiUnit, unitgroup scanGroup) {
    CargoDefend(player, aiUnit, scanGroup, 8, 10, c_emptyString, c_AB_BunkerChange);
}
 
bool CargoDefend (int player, unit aiUnit, unitgroup scanGroup, int searchRange, int loadRange, string wanted, string command) {
    aifilter filter;
    unitgroup nearBunkerGroup;
    int bunkerCount;
    unit unitToCheck;    
    order ord = null;
    bool autoLoad = false;
    bool wantsToBeInBunker;
    unitgroup targetGroup;

    targetGroup = UnitGroupFilterThreat(scanGroup, aiUnit, null, 0);
    targetGroup = UnitGroupFilterRegion(targetGroup, RegionCircle(UnitGetPosition(aiUnit), searchRange), 0);
    
    if (UnitGroupCount(targetGroup, 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) {
                nearBunkerGroup = AIFindUnits(player, wanted, UnitGetPosition(aiUnit), c_campaignBunkerLoadRange, c_noMaxCount);
                if (wanted == c_emptyString) {
                    filter = AIFilter(player);
                    AISetFilterMelee(filter, c_onlyRanged);
                    AISetFilterValidPassenger(filter, aiUnit);
                    nearBunkerGroup = AIGetFilterGroup(filter, nearBunkerGroup);
                }

                unitToCheck = CampaignWantsToBeInBunker(player, aiUnit, nearBunkerGroup, 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);
        if (wanted == c_emptyString) {
            filter = AIFilter(player);
            AISetFilterMelee(filter, c_onlyRanged);
            AISetFilterValidPassenger(filter, aiUnit);
            nearBunkerGroup = AIGetFilterGroup(filter, nearBunkerGroup);
        }

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

            if (!UnitIsAlive(unitToCheck)) {
                continue;
            }
            if (AIIsScriptControlled(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;
}
 
unit CampaignWantsToBeInBunker (int player, unit aiUnit, unitgroup bunkerGroup, bool unload) {
    int bunkerCount;
    unit unitToCheck;
    bool wantsToBeInBunker;
    order unitOrder;

    //  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;
        }
        if (AIIsScriptControlled(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;

        if (AIControlForceToMove(unitToCheck)) {
            // If the unit is forced to move, it shouldn't be in the bunker even in combat
            wantsToBeInBunker = false;
        }
        else if (!AIUnitIsInCombat(unitToCheck) && !AIUnitIsInCombat(aiUnit)) {
            // Otherwise the unit will only want to be out of the bunker if not combat is happening
            
            //  The unit wants to execute a non attack order.
            unitOrder = UnitOrder(aiUnit, 0);
            if (unitOrder != null && !AIIsAttackOrder(unitOrder)) {
                wantsToBeInBunker = false;
            }
            //  The unit wants to move.
            else if (AIControlWantsToMove(unitToCheck)) {
                wantsToBeInBunker = false;
            }
            // Unit has no home point
            else if (AIGetHomePosition(unitToCheck) == c_nullPoint) {
                wantsToBeInBunker = false;
            }
            // Unit's home point is too far away
            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;
}
 
Hmm - I'm not 100% into the galaxy script but I cant see any reason why it should difer between the two units. Have you checked the data editor to see if there is something under the unit that makes it not be handled by the AI? (Maybe the AI: AI Evaluation)?
 
Thanks for the suggestion. I have had a look through all the AI related data properties I can think of but nothing seems to help. I tried changing the AI: AI Evaluation property to Marine thinking that it might make the AI think the Firebat was a Marine and load the unit into the bunker but with no success.

The reason why I think the problem has something to do with the script functions above is that it seems to determine whether the selected unit can be loaded by running through various conditions. I have a feeling that one of these conditions is causing the Firebat to be rejected. However the only method I know of debugging the above functions is by trying to recreate them in the trigger editor, which is a fair amount of work.
 
You're welcome. I'm sorry I cant help, but recreating it might be the only way to solve this, othervise you could ask Blizzard on their forum? (If that is still possible)
 
I ended up having to recreate it and I managed to trace it down to this section of the code where it finds suitable nearby units to load into the bunker:
Code:
filter = AIFilter(player);
AISetFilterMelee(filter, c_onlyRanged);
AISetFilterValidPassenger(filter, aiUnit);
nearBunkerGroup = AIGetFilterGroup(filter, nearBunkerGroup);
On the second line, it filters the unit group to find only units with a ranged attack. I thought this would have been ok given the Firebat has a range of 2 and the Melee flag is not checked. It turns out that any weapon with a range of 2 or less is considered melee regardless of whether the melee flag is checked. So if I increase the Firebats range to 2.1 it works without any problems.

Using the weapon range to determine if the unit is melee rather than the melee flag seems very backwards to me. Infact, I now have no clue as to what the melee flag actually does. I might post this on the blizzard forums and see if I can get the developers to clarify the reasoning behind this.
 
I ended up having to recreate it and I managed to trace it down to this section of the code where it finds suitable nearby units to load into the bunker:
Code:
filter = AIFilter(player);
AISetFilterMelee(filter, c_onlyRanged);
AISetFilterValidPassenger(filter, aiUnit);
nearBunkerGroup = AIGetFilterGroup(filter, nearBunkerGroup);
On the second line, it filters the unit group to find only units with a ranged attack. I thought this would have been ok given the Firebat has a range of 2 and the Melee flag is not checked. It turns out that any weapon with a range of 2 or less is considered melee regardless of whether the melee flag is checked. So if I increase the Firebats range to 2.1 it works without any problems.

Using the weapon range to determine if the unit is melee rather than the melee flag seems very backwards to me. Infact, I now have no clue as to what the melee flag actually does. I might post this on the blizzard forums and see if I can get the developers to clarify the reasoning behind this.
Good to know :) I'm glad you figured it out, this might help some one at a later point :)
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • seph ir oth seph ir oth:
    Potential problem for a wealth tax for the US would be the ultra rich just offshoring assets in another country without a wealth tax. Those that would eat the wealth tax on, say, stocks, would be the middle class that puts money slowly into the market via the likes of funds.
  • seph ir oth seph ir oth:
    happy US election day btw!
  • Varine Varine:
    Oh election day
  • The Helper The Helper:
    Election day has come and gone now it is time to sit back and watch the liberals melt down. At least it should be peaceful around my house as my roommate is as trumper as you can get.
    +2
  • jonas jonas:
    curious what the next years will bring
  • jonas jonas:
    I think at least it's good that Trump has a solid lead in the popular vote
  • The Helper The Helper:
    It will depend on what happens with the house race. The Conservatives have the president and the senate, I believe the house majority is up too. With the conservatives stacked in the supreme court now would be the time they could really do something
  • The Helper The Helper:
    I just hope its not anything stupid like banning abortion nationally or doubling down on the border wall
  • jonas jonas:
    Well, the border wall at least doesn't really harm anyone. It's true that Trump only managed to add about new 80 miles to the 650 miles built by Obama and Bush, and that it didn't do much to reduce the number of illegal immigrants, but still, it's construction jobs and doesn't hurt anyone.
  • jonas jonas:
    One can of course argue whether the money is better spent somewhere else, like reducing taxes
  • jonas jonas:
    I'm more curious what Trump will do in foreign policy and how that will affect the US/European coalition, and the balance of power in europe and ME
  • The Helper The Helper:
    I am more of a fan of fixing the broken immigration laws and looking at protecting border with drone tech or some other way I mean I just think its ridiculous to build a great wall of mexico
    +2
  • Ghan Ghan:
    Trump's foreign policy was fantastic in his last term. More of that, please. Finish the Abraham Accords. Get Saudi in. Build the coalition against Iran.
    +1
  • Lord of Bourbon Lord of Bourbon:
    Just a friendly reminder to watch your sodium intake, too much can result in harmful effects
    +1
  • The Helper The Helper:
    Thank you for the health tip Lord of Bourbon!
  • The Helper The Helper:
    I hate to even say anything because it always seems to start again after I do it but the bots have been gone for like 3 days now. Looks like the AI scraping bots are onto better pastures, are done sucking us dry, or are taking a vactation :)
  • Lord of Bourbon Lord of Bourbon:
    The bots are probably mining salt else where right now, given that the mines have reopened.
  • The Helper The Helper:
    Got a user online that has like 10 different accounts dating back like 7 years and he is cycling through them. I have too much time on my hands LOL
    +1
  • The Helper The Helper:
    like half of them are banned with no notes why
  • The Helper The Helper:
    I made this the other day it was Awesome! Chicken Pot Pie Pasta - https://www.thehelper.net/threads/chicken-chicken-pot-pie-pasta.197065/
  • The Helper The Helper:
    New Thanksgiving Holiday Appetizer recipe Turking Stuffing Balls - https://www.thehelper.net/threads/appetizer-turkey-stuffing-balls.197078/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top