[progress] Making the Zerg Queen Stay and Inject Larva at Local Limit

Started by Kernel64, March 12, 2010, 04:08:21 PM

Previous topic - Next topic

Kernel64

The problem with the default Queen is that if you have multiple queens, they are bunched together at the main. And if you have >10 drones anywhere, even if say your expo has only 3 drones, it will not cast spawn larva.

This one fixes that, so you can train Queens at expos or other bases, and have that queen spawn larva and only keep count of the larva in front of her. Also, this allows your Queens to stay where they're trained from unless grabbed and given an order somewhere else.

at TactZergAI.galaxy replace the respective code with this:

Quote
order SpawnLarva (int player, unit aiUnit) {
    order ord;
    unitgroup hatcheries;
    unitgroup larvas;
    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;
    }

    larvas = AIFindUnits(player,
                             c_ZU_Larva,
                             UnitGetPosition(aiUnit),
                             15,
                             c_noMaxCount)
                             ;
    //  Don't cast if we already own at least 10 larva
    //
    larvaCount = UnitGroupCount(larvas, c_unitCountAll);
        //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);
}

This will make the engine scan larva count local to where the Queen was trained. Remember to train Queen at specific town since our next code prevents the grouping and taking home of all queens:

At Zerg.galaxy, find the function "void AINewUnitZerg (int player, unit u)"
and comment out the line which adds queens into a wave.

it should say something like:
Quote
    if (type == c_ZU_Queen) {
        //AIWaveAddUnitPriority(AIWaveGet(player, c_waveHome), u, c_prioWavePeon);c_prioWaveIdle doesn't work
        return;
    }

Perhaps through here we can code so that ovies are not automatically added as scouts, but positioned somewhere at edges of creep. Then we can add a Think for the overlord and give it an order to drop creep.

edit: It would be nice to have the limit of counting of larva multiplied by the number of hateries/lair/hive within range of the queen. This way, one queen can cast spawn larva on two hatcheries. Coding where to cast the order depending on the number of larva of that specific grabbed hatchery would be the awesome.

Astazha

<3


I've been wanting this.


You can change the units that are set up as scouts in Zerg.galaxy.  The AIGetScoutZerg() function picks a scout - just go down to the bottom and change the default result from Overlord to Drone.  Making them go out to the perimeter would be nice, but milling about seems good enough.


I would love to see that hatchery in range code.  I've found if I want more than 1 hatchery at a location to get larva that I have to have 1 queen per hatchery.  2 queens should be sufficient for 3 hatcheries.

Kernel64

Mind coding one? This is a big task really. And I'm working on feeding the Zerg strats and making it use them wisely.

Also, who's doing terran? Has there been progress on making it wall-in? There's a ton of beautiful strats born from having walled yourself in. It would be awesome to have the Terran AI wall in and go double factory with reactor and burn through with hellions.

Astazha

I'll try to look into it.  I've been playing with expansion/base development stuff.  Really just trying to figure out what is possible at this point... so many functions without clear documentation...


In the BuildAI.galaxy file there are some constants for build locations, and the protoss Starcrack code makes some use of them to put the pylons next to "factories" once a reasonable powerfield has been established.  One of the locations you can specify is choke point, but I haven't done any testing to see whether telling the Terran AI to build supply depots and barracks and bunkers at the choke point will actually result in a wall in or if they will just put it in the vicinity.  I fear the latter.

Aeg1s

Quote from: Astazha on March 13, 2010, 08:58:47 PM
I'll try to look into it.  I've been playing with expansion/base development stuff.  Really just trying to figure out what is possible at this point... so many functions without clear documentation...


In the BuildAI.galaxy file there are some constants for build locations, and the protoss Starcrack code makes some use of them to put the pylons next to "factories" once a reasonable powerfield has been established.  One of the locations you can specify is choke point, but I haven't done any testing to see whether telling the Terran AI to build supply depots and barracks and bunkers at the choke point will actually result in a wall in or if they will just put it in the vicinity.  I fear the latter.


They'll just put it in the vicinity; I doubt we'll be able to get actual wallins working unless we can manually order them to build at a specific point.

Astazha

I just discovered that using c_ZB_Hatchery_Alias in the AIFindUnits() function isn't working properly.  It appears to be an unrecognized parameter because the group that gets returned actually contains everything in the vicinity - the hatchery, drones, overlords, larva, all of it - which is the behavior I'd expect if the value was null.


I suspect we haven't seen the problem come up because a valid order isn't generated when she tries to spawn mutant larva on an invalid target, so only the hatchery gets done out of the unitgroup. 

Doix

c_ZB_Hatchery_Alias doesn't include the hatchery, just lair and hive. At least that's what I remember from testing.

Astazha

I did some more testing on this.


This code:
QuoteAISetStock(player, 2, c_ZB_Hatchery_Alias);
   AISetStock(player, 1, c_ZB_Hive);


will produce 1 Hive and 1 additional Hatchery.


This code:
QuoteAISetStock(player, 2, c_ZB_Hatchery);
   AISetStock(player, 1, c_ZB_Hive);


will produce 1 Hive and 2 additional Hatcheries.


Based on that, I would say that c_ZB_Hatchery_Alias does include the basic Hatchery.  It appears to me that aliases are just not appropriate for us in AIFindUnit() even though they work correctly in the Stock functions.

Aeg1s

Quote from: Astazha on March 14, 2010, 11:27:41 AM
I did some more testing on this.


This code:
QuoteAISetStock(player, 2, c_ZB_Hatchery_Alias);
   AISetStock(player, 1, c_ZB_Hive);


will produce 1 Hive and 1 additional Hatchery.


This code:
QuoteAISetStock(player, 2, c_ZB_Hatchery);
   AISetStock(player, 1, c_ZB_Hive);


will produce 1 Hive and 2 additional Hatcheries.


Based on that, I would say that c_ZB_Hatchery_Alias does include the basic Hatchery.  It appears to me that aliases are just not appropriate for us in AIFindUnit() even though they work correctly in the Stock functions.


I've seen some strange behavior using the Alias types; it seems they were designed for the SetStock functions but a lot of other functions don't use them correctly.

Astazha

Ok, it's done.


Quote//--------------------------------------------------------------------------------------------------

//  SpawnLarva() - original by Blizzard, modified by Astazha and Kernel64
//
//  Checks for hatcheries in a radius of 25 around the queen
//  Checks for larva in a radius of 3.6 around each hatchery.
//  Spawns larva if less than 16 are found at a hatchery.
//  Some overlap will occur between very close hatcheries but the function works very well regardless.
//--------------------------------------------------------------------------------------------------
order SpawnLarva (int player, unit aiUnit) {
    order ord;
    unitgroup hatcheries;
    unitgroup lairs;
    unitgroup hives;
    unitgroup larvas;
    unitgroup vacancyHatches;
    int larvaCount;
    int numHatcheries;
    int numLairs;
    int numHives;
    int i;
    unit thisHatchery;


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


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


   
    //Count the various types of Hatcheries.  Hatchery_Alias cannot be used here.


    hatcheries = AIFindUnits(player,
                             c_ZB_Hatchery,
                             UnitGetPosition(aiUnit),
                             25,
                             c_noMaxCount)
                             ;
    lairs = AIFindUnits(player,
                             c_ZB_Lair,
                             UnitGetPosition(aiUnit),
                             25,
                             c_noMaxCount)
                             ;
    hives = AIFindUnits(player,
                             c_ZB_Hive,
                             UnitGetPosition(aiUnit),
                             25,
                             c_noMaxCount)
                             ;
   
    numHatcheries=UnitGroupCount(hatcheries, c_unitCountAll);
    numLairs=UnitGroupCount(lairs, c_unitCountAll);
    numHives=UnitGroupCount(hives, c_unitCountAll);


    //Create an empty unitgroup
    vacancyHatches = UnitGroup(null, -1, RegionCircle(null, 0), null, 0);


    //iterate through the groups.  For each Hatchery find the larva within a small radius around it.
    //if the number of larva is small enough, add it to the group of Hatcheries that may receive
    //a spawn larva.  This group will also include Hatcheries under construction but the queen will not spawn
    //on them anyway.


    //Hatcheries*************************
    i=1;
    while (i<=numHatcheries)
    {
     thisHatchery=UnitGroupUnit(hatcheries,i);
     larvas = AIFindUnits(player,
                             c_ZU_Larva,
                             UnitGetPosition(thisHatchery),
                             3.6,
                             c_noMaxCount)
                             ;
      larvaCount = UnitGroupCount(larvas, c_unitCountAll);
      if (larvaCount<16)
      {
      UnitGroupAdd(vacancyHatches,thisHatchery);
//      TriggerDebugOutput(1, StringToText("Adding a Hatchery to unitgroup vacancyHatches"), true);
      }
//      TriggerDebugOutput(1, StringToText("Hatchery"+ IntToString(i)+" has "+IntToString(larvaCount)+" within 3.5"), true);
   i=i+1;
      }

    //Lairs*****************************
    i=1;
    while (i<=numLairs)
    {
      thisHatchery=UnitGroupUnit(lairs,i);
      larvas = AIFindUnits(player,
                             c_ZU_Larva,
                             UnitGetPosition(thisHatchery),
                             3.6,
                            c_noMaxCount)
                             ;
      larvaCount = UnitGroupCount(larvas, c_unitCountAll);
      if (larvaCount<16)
      {
      UnitGroupAdd(vacancyHatches,thisHatchery);
//      TriggerDebugOutput(1, StringToText("Adding a Lair to unitgroup vacancyHatches"), true);
      }
//      TriggerDebugOutput(1, StringToText("Lair"+ IntToString(i)+" has "+IntToString(larvaCount)+" within 3.5"), true);
   i=i+1;
    }


    //Hives******************************
    i=1;
    while (i<=numHives)
    {
      thisHatchery=UnitGroupUnit(hives,i);
      larvas = AIFindUnits(player,
                             c_ZU_Larva,
                             UnitGetPosition(thisHatchery),
                             3.6,
                             c_noMaxCount)
                             ;
      larvaCount = UnitGroupCount(larvas, c_unitCountAll);
      if (larvaCount<16)
      {
      UnitGroupAdd(vacancyHatches,thisHatchery);
//      TriggerDebugOutput(1, StringToText("Adding a Hive to unitgroup vacancyHatches"), true);
      }
//      TriggerDebugOutput(1, StringToText("Hive"+ IntToString(i)+" has "+IntToString(larvaCount)+" within 3.5"), true);
   i=i+1;
    }


//    TriggerDebugOutput(1, StringToText("There are "+ IntToString(UnitGroupCount(vacancyHatches, c_unitCountAll))+" hatches with vacancy."), true);


    //do not spawn larva if there are no hatcheries that can receive it (if unitgroup vacancyHatches is empty.)
    if (UnitGroupCount(vacancyHatches, c_unitCountAll)<1) {
        return null;
    }


    return AIUnitGroupGetValidOrder(vacancyHatches, ord, aiUnit, c_backwards);
}


It checks for hatcheries with in a radius of 25 from the queen.  This keeps it to one base but is wide enough that she'll pick up hatches that are completely off screen if she's centered.  I've run a base with as many as 7 hatcheries and she worked all of them.


Larva within a small radius of the hatchery are counted (I don't know how to select the larva from a specific hatch.)  This results in some inaccuracy in the count.  A few (generally no more than 5) larva can be picked up from nearby hatcheries that are very close, and occasionally a larva may momentarily squirm out of range and not get counted.


The actual max for a hatchery is 19 larva, so I've set the function to spawn if there are 15 or less.  This means the only time that a larva is wasted is if a lone hatchery has exactly 19 and one squirms out of range.  The range of the search can be extended to 3.6 and this will stop almost all of the "missing a larva" but will result in it being easier to pick up larva from neighbors.  The neighbors' larva squirming out of range tends to mean that overcounts only prevent spawning in the short term.  It's not a real problem anyway - no serious build or AI is ever going to have 7 hatcheries with 15-19 larva each at one base.


The order that things get done in means that Hives tend to get larva spawned before Lairs, which tend to get them before Hatcheries.  Beyond that it appears to operate in a radius from where the queen was standing when the search was run for the hatcheries - with the closer ones getting larva 1st.


Overall the function does a good job of keeping all hatcheries in the high teens without being wasteful.


Also of note:  I was surprised to find that the groups are indexed starting at 1 rather than 0.

Kernel64

itWorks = true;

if (itWorks == true){
  AstazhaRep = AstazhaRep + 100000000000000000000000000000000000;
  UIDisplayMessage(PlayerGroupAll(), 1, StringToText("It's Beautiful."));
}