Function to check if a building has units/upgrades in production queue?

Started by 1337, March 06, 2010, 08:32:41 PM

Previous topic - Next topic

1337

Name says it all, I want to know how to check how many units/upgrades are in the build queue at a particular building. I'm writing a Chrono Boost func that prioritizes based on what units are being built.

hd

Updated with more accurate reflection of what would need to be done.

Quote from: 1337 on March 06, 2010, 08:32:41 PM
Name says it all, I want to know how to check how many units/upgrades are in the build queue at a particular building. I'm writing a Chrono Boost func that prioritizes based on what units are being built.

void ProtossCastChronoBoost(int player)
{
    if (AITechCount(player, c_PU_Zealot, c_techCountQueuedOrBetter) > 0)
    {
        // cast chrono boost on gateway
        return;
    }
    if (AITechCount(player, c_PU_Stalker, c_techCountQueuedOrBetter) >0)
    {
        // cast chrono boost on gateway
        return;
    }
}

ProtossCastChronoBoost(player);


Essentially, you check what's being built in order of what's important and exit the function once the criteria has been met.

Otherwise, you'd have to grab each building individually (which is harder than it sounds) and probably do a property check on the building to see if it's doing something and then if it meets your criteria, cast chrono boost.

void ProtossChronoBoostCheck(int player)
{
    bool bFound = false;
    int iBuildingCount = 0;
    int iCount = 0;
    string sBuildingList[x];
    unit uChronoBoost;
    order oChronoBoost;

    sBuildingList[0] = c_PB_Gateway;
    sBuildingList[1] = c_PB_RoboticsFacility;
    sBuildingList[2] = c_PB_Nexus;
    iBuildingCount = 3;

    while (iCount < iBuildingCount && !bFound)
    {
        uChronoBoost = AIGrabUnit(player, sBuildingList[iCount], c_prioNone, null);
        iCount = iCount + 1;

        // get unit property and check it
        // maybe try UnitOrderCount(aiUnit) > 0
        if (UnitOrderCount(uChronoBoost) > 0)
        {
            bFound = true;
        }
    }

    if (bFound)
    {
        oChronoBoost = AICreateOrder(player, "TimeWarp", 0);
        OrderSetTargetUnit(oChronoBoost, uChronoBoost);

        if (UnitOrderIsValid(uChronoBoost, oChronoBoost))
        {
            // casting chrono boost
            AICast(uChronoBoost, oChronoBoost, c_noMarker, c_castHold);
        }
    }
}

siman


I think you need c_techCountQueuedOnly for the AITechCount,
c_techCountQueuedOrBetter will over-count the completed units as well

Kernel64

Behavior data are found in BehaviorData.xml Buffs are here too. TimeWarpProduction and TimeWarpResearch are here.

figuring out if this:
        AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, "TimeWarpProduction");

succeeds in filtering out a gateway that already has chrono boost on it.

Edit: Success. If you can remember the code that was for chrono boosting the gate way and it boosted it twice if the nexus had 50 energy or 3x if 75, this one fixes that:

Quote
static order ChronoBoostGate (int player, unit aiUnit) {     // gateway boosting
    unitgroup scanGroup;
    int scanCount;
    unit depot;
    order ord;
    aifilter filter;

    ord = AICreateOrder(player, "TimeWarp", 0);

    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }
   scanGroup = AIFindUnits(player, c_PB_Gateway, UnitGetPosition(aiUnit),
                        AIUnitFixed(player, c_PB_Nexus, c_fieldRadius)
                        + AIAbilityFixed(player, "TimeWarp", c_fieldRange0),
                        c_noMaxCount
   );

   filter = AIFilter(player);
   AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, "TimeWarp", c_fieldTargetFilters)));
   AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, "TimeWarpProduction"); //behavior data xml other: TimeWarpResearch (for research structures like Cybernetics Core, forges, etc.)

   scanGroup = AIGetFilterGroup (filter, scanGroup);
   return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_backwards);
   
}
[/qoute]

Going to find how to check if a structure is doing something.

Aeg1s

Quote from: Kernel64 on March 07, 2010, 04:04:43 AM
Behavior data are found in BehaviorData.xml Buffs are here too. TimeWarpProduction and TimeWarpResearch are here.

figuring out if this:
        AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, "TimeWarpProduction");

succeeds in filtering out a gateway that already has chrono boost on it.


Nice thanks, I was looking for how to do that too.

Kernel64

You guys have figured this out yet? Checking a structure and what it is doing?

I want to filter out structures that are not producing/researching something. Anyone?

hd

Well, if we had access to the AITechCount function to see how it checks if something is queued, or in progress then it would be a simple task of cycling through each building in order of importance, seeing if it has stuff in queue in order of priority and then checking to see if it's already time warped and if not, casting it. :\

Kernel64

Hd, this is the gateway only Chronoboost order I have:
Quote
static order ChronoBoostGate (int player, unit aiUnit) {     // gateway boosting
    unitgroup scanGroup;
    int scanCount;
    unit depot;
    order ord;
    aifilter filter;

    ord = AICreateOrder(player, "TimeWarp", 0);

    if (!UnitOrderIsValid(aiUnit, ord)) {
        return null;
    }
   
    scanGroup = AIFindUnits(player, c_PB_Gateway, UnitGetPosition(aiUnit),
                        AIUnitFixed(player, c_PB_Nexus, c_fieldRadius)
                        + AIAbilityFixed(player, "TimeWarp", c_fieldRange0),
                        c_noMaxCount
    );

    filter = AIFilter(player);
    //filter out gates that are not producing
    //filter out gates that have timewarp on them; take only those who have none
    AISetFilterBits(filter, UnitFilterStr(AIAbilityStr(player, "GatewayTrain", c_fieldFilters))); //does nothing?
    AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, "TimeWarpProduction");

    scanGroup = AIGetFilterGroup (filter, scanGroup);
    return AIUnitGroupGetValidOrder(scanGroup, ord, aiUnit, c_backwards);
   
}

My AIThinkNexus will fire a cast with the above as order if there is at least one zealot in production. The problem is, it casts CB on another gateway that is not producing anything.

The above can filter out any gateway that already has CB casted on it. However, I cannot find a way to filter out Gateways that are not producing something.

This:
AISetFilterBehaviorCount(filter, c_noBehaviorMin, c_noBehaviorMax, "TimeWarpProduction");

Includes gateways that have no CB on them. Thus, the filtering.

If we could find the same filter code with the purpose of listing those gateways that are idle, then everything will be a lot better.

hd

Well, you could use th AINewUnitProt or whatever function which is called the moment a unit is complete or you start building a building.

Then test to see if the order in which it tries to build units from gateways is in the same order in which it indexes the gateways. Basically, if you have 2 gateways and you tell it to build 2 zealots, it should attempt to build one at the gateway in index position 0 / 1. Then it probably checks if any other buildings exist that can build the desired unit and then tries to build one there.

If it holds true, then you could make manual calls in the build order as to when to use chronoboost until we can find a way to know if a unit is being built at a structure.

edit for clarity, you make your own index of the gateways and then chrono boost in order based on the units in production. if 2 zealots are in progress, it would be 1 at each, thus chronoboost index 1 & 2, if only 1 is in production it's probably at index 1 (1 by the way, is normally 0) and thus you can control which ones get chronoboosted.

yeah it's not a sure-fire fix but it's only temporary until we can figure out how to know if something specific is being built at a specific building, which right now we can't.

1337


Yeah I haven't been able to figure this out. For now my ChronoBoost code prioritizes Nexi based on a global boolean "weNeedProbes" and once that boolean becomes false, then we switch to WarpGates and Gateways. The "weNeedProbes" flag is set in the main AI code once probes exceeds 30*(towns-1) + 24. (all towns have 30 probes except for the newest one which has 24). If the beAggressive flag is false then it always prioritizes Nexi.



Here is my ChronoBoost function (from the soon-to-be-released 1337 AI v0.4):


void ChronoBoost(int player, bool beAggressive)
{
   unitfilter uf1;
   aifilter af1;
   unitgroup g1;
   unitgroup g2;
   unit u1;
   unit u2;
    order o1;
   string firstprio;
   string secondprio;
   string thirdprio;
   int i;
   int j;
   fixed r = AIUnitFixed(player, c_PB_Nexus, c_fieldRadius) + AIAbilityFixed(player, "TimeWarp", c_fieldRange0);
   
   //Setup target filters
   uf1 = UnitFilterStr(AIAbilityStr(player, "TimeWarp", c_fieldTargetFiltersAB));         //only valid Time Warp targets
   UnitFilterSetState(uf1, c_targetFilterUnderConstruction, c_unitFilterExcluded);         //dont attempt to target buildings under construction
   af1 = AIFilter(player);
   AISetFilterBits(af1, uf1);
   AISetFilterBehaviorCount(af1, c_noBehaviorMin, c_noBehaviorMax, "TimeWarpProduction");  //Filter out targets that are already buffed.
   
   g1 = AIFindUnits(player, c_PB_Nexus, PlayerStartLocation(player), 9999, c_noMaxCount);
   j = UnitGroupCount(g1,c_unitCountAlive);
   i = 1;
   while(i <= j)
   {
      u1 = UnitGroupUnit(g1,i);
      if (u1)
      {
         if (UnitGetPropertyInt(u1, c_unitPropEnergy, c_unitPropCurrent) >= 25)
         {
            o1 = AICreateOrder(player, "TimeWarp", 0);
           
            if(!weNeedProbes && beAggressive)   //only prioritize gateways if we don't need probes
            {
               firstprio = c_PB_Gateway;
               secondprio = c_PB_WarpGate;
               thirdprio = c_PB_Nexus;
            }
            else                        //always prioritize nexi if on non-aggressive setting
            {
               firstprio = c_PB_Nexus;
               secondprio = c_PB_Gateway;
               thirdprio = c_PB_WarpGate;
            }
            //Find targets
            g2 = AIFindUnits(player, firstprio, UnitGetPosition(u1), r, c_noMaxCount);
            g2 = AIGetFilterGroup (af1, g2);
            if(UnitGroupCount(g2,c_unitCountAlive) == 0)
            {
               g2 = AIFindUnits(player, secondprio, UnitGetPosition(u1), r, c_noMaxCount);
               g2 = AIGetFilterGroup (af1, g2);
            }
            if(UnitGroupCount(g2,c_unitCountAlive) == 0)
            {
               g2 = AIFindUnits(player, thirdprio, UnitGetPosition(u1), r, c_noMaxCount);
               g2 = AIGetFilterGroup (af1, g2);
            }
            if(UnitGroupCount(g2,c_unitCountAlive) != 0)
            {
               u2 = UnitGroupUnit(g2,1);
               OrderSetTargetUnit(o1,u2);
               AICast (u1, o1, c_noMarker, false);
            }
         }
      }
      i = i + 1;
   }
}


It loops through all Nexi on each call and selects a good target for each one.

Kernel64

How do I test this? I placed everyone's suggested code in Protoss0.galaxy with declarations and then the main code at the bottom of the file.

I always get an empty map.

Anything I'm doing wrong?

1337

To use my code, add it to the beginning of Protoss0. I'm not sure but Galaxy may require that prerequisite functions be declared before functions that use them. Add a "bool weNeedProbes = true;" global variable declaration at the top of the Protoss0 file.


Then somewhere in the ProtossOpenGnd0 and ProtossOpenMidA/B etc calls, add in "ChronoBoost(player, true);" and it ought to work...

Aeg1s

Quote from: 1337 on March 07, 2010, 04:07:24 PM
To use my code, add it to the beginning of Protoss0. I'm not sure but Galaxy may require that prerequisite functions be declared before functions that use them.


Yeah, you need a function prototype above anything that uses it if the function is declared below them.

Kernel64

Oh, thanks so much.

So OrderSetTargetUnit must also receive a unitgroup variable right? Like, AIGrabUnit wont work with it?

Again, this is cool.

1337


It just takes an order and a single unit as the target.


order o;
unit u;OrderSetTargetUnit(o,u);