Uploaded by imtheseekr

M28 Devlog.docx

advertisement
M28AI – Devlog
1) Introduction/background
This is a devlog for the development of a new AI for forged alliance forever, named M28.
Having developed M27 for about 18 months, I was running into a number of issues that wouldn’t be
easy to solve with the way M27 was built, in particular:
●
●
●
Late game slowdown
ACU being too easy to flank/ambush with tanks
General land combat unit management (for non-skirmisher units) having poor decisions on
where to send the units.
This is partly because M27 was built bit by bit, starting off as an AI intended to use T1 land, only for
5km land maps, with more logic added piecemeal as it got built up to a fully functional AI. In
addition, some of the fundamental logic it was built off (how it reads maps and pathing) was based
on a simple check of whether a unit could path from A to B, and straight line distances ignoring any
blocking terrain. Meanwhile Jip has recently released integrated pathfinding logic into the core FAF
for AI, which while not perfect allows calculation of pathing routes from A to B and how long it would
take a land unit to travel between them.
I was originally considering creating a basic AI to test out redoing how M27 manages its land units,
but in the course of doing this I decided there were so many fundamental changes I wanted to try
out with how the AI logic worked that it would make more sense to do it as a standalone AI.
The M27 devlog charting how it developed can be viewed here:
https://docs.google.com/document/d/1uQlEoN-kti7G2MnhwD60uaaHwVevPNYH/edit?usp=sharing
&ouid=100973959280546778272&rtpof=true&sd=true
Development on M28 started around 13 November 2022, although for a couple of weeks little was
done as I was focusing mostly on M27 updates to resolve significant issues highlighted by replays,
with most of my AI development time spent on M28 from around 26 November 2022.
This is intended to document my progress and some of my thought process with creating M28, as
well as functioning as a todo list of ideas of potential things to look at in the future for M28 (although
many of these won’t be implemented – M27 has a vast todo list of points that are now unlikely to be
implemented).
To help track things for myself, I colour code items with yellow for htings I want to work on soon, red
for points Im focused on immediately, green for points I’ve implemented but haven’t tested fully, and
blue for things I’ve implemented and have tested to confirm they’re working (many changes will stay
at the green state as I rely on general running of games to hopefully flag up errors with them, usually
because I’m too lazy to create a replay scenario and lots of logs just to test one point).
M28 v1 ended up being released on 12 May 2023.
2) Pre-v1 release
My hope is to get a reasonably functioning AI in 3-6 months.
2.1)
Pre-release goals
In contrast to the approach taken for M27, I don’t think I can get away with releasing a really basic AI
that can only do T1 tank landspam with a guncom since the moment I release it it will likely be
1
compared to M27. I therefore want it to be able to cover some of the main things M27 can handle,
even if it wont do everything (and also is unlikely to do these things as well as M27), with the hope of
the AI on initial release being relatively compatible with most maps and also being a better than
average AI (i.e. able to beat the AI other than DilliDalli, RNG, and M27).
2.1.1) Map related
●
●
Generate land zones
Reclaim is recorded for the map and incorporated into land zones to be accessible for all M28
AI
2.1.2) ACU related
●
●
●
ACU builds initial build order
ACU used offensively initially and defensively later, building on mexes, getting reclaim and
upgrading
If ACU attacked or has nearby enemies while doing initial build order it will temporarily stop
to deal with attackers
2.1.3) Engineer related
●
●
●
●
●
Engineers build mexes based on land zones
Engineers build basic buildings (factories, power)
Engineers reclaim, with logic linked to land zones (eg. Reclaim safe zones first)
Experimental construction
Special engineer logic for:
o Extra factories
o Radar
o TMD
o SMD
o SAMs
o Shields to defend from arti/novax
2.1.4) Factory/other building related
●
●
●
Mex and factory upgrade basic logic
Factory build decisions are linked to land zones
Build logic for:
o Factoring in ‘teammate can build this unit and its better so get them to build it’
o Engineers
o Land tanks
o Skirmishers
o Mobile shields
o Land scouts
o MAA
2.1.5) Land unit management
●
●
●
Basic land unit management by land zone
Land tanks basic unit management
MAA management
2
●
●
Mobile shield and mobile stealth unit management
Skirmisher unit management
2.1.6) Intel – land scouts
●
●
Patrol paths for each land zone recorded
System of requesting land scouts for a particular zone and then having the land scout patrol
that zone while avoiding enemies.
2.1.7) Experimental level and building unit management
●
●
●
●
●
●
●
●
Fatboy
GC/Monkey/Ythotha/Megalith
Novax
Nuke launcher
TMLs
Rushing T2 land and getting T2 PD in response to an approaching enemy guncom
Ahwassa
T3 and experimental arti
2.1.8) Naval unit management
●
●
●
●
●
Rework M27 naval logic to work on a subteam or water zone basis instead of a centralised
team basis
Use water zones for naval logic similar to land zones
Implement basic unit management
Naval factory production to make use of the above
Better use of amphibious and hover units as part of naval logic (i.e. integrated with naval
forces and assaulting islands in groups instead of single file).
2.1.9) Air unit management
●
●
●
●
●
●
AirAA management
Gunship management
Torpedo bomber management
Air scout management
Transports for plateaus
Air factory production to make use of the above
2.1.10) Compatibility
●
●
●
●
●
●
●
Support for norush
Support for survival gamemode where no enemy
Support for maps where cant path to enemy amphibiously (e.g. air wars) in terms of more
refined build order
Unit cap considered/used to adjust what units are built
No issues on maps with partial playable area (e.g. the circle type playable area maps, and air
scouting logic)
Support for water start points and maps with no water
Playing with M28 enabled but no M28AI doesn’t result in LOG error messages/warnings
2.1.11) Other
3
●
●
Effective island attacks and building logic
Dodge shot micro
2.1.12) Competitiveness and performance
CPU performance
● Runs significantly faster than M27 on an Africa UEF vs UEF mirror AI game (i.e. vs the M27
performance benchmarks on this map)
Competitiveness
● Manage to beat RNG and DilliDalli at least once on every M27 pre-release map plus a
selection of maps where RNG is expected to do well (Crash site, Inquisition, Osiris)
● Beat Sorian AIX and adaptive AIX with max AiX build and eco boost as AiX nomads
● Beat M27 on Astro Craters
2.1.13) Possible future goals
●
●
●
●
●
●
●
2.2)
Confirming 100% win rate vs adaptive AI on every M27 pre-release map
>50% win rate vs RNG and vs DD on every M27 pre-release map (plus Osiris (won UEF mirror)
– to avoid too many games, if I win the first time then I’ll treat it as a win, if I lose then I’ll do
a ‘best of 3’. – Im not sure if this was met or not, but possibly not and I don’t have the
motivation to spend weeks making tiny tweaks to potentially achieve – instead I’ll just assess
this at a later date
o At release M28 was capable of beating RNG and DD, but only had >50% win rate on
the majority of maps tested (rather than all of them).
Beating M27 on more maps
T1 bomber engi hunters
T3 strat mex hunters
Czar
Re-running the AI tourney for CPU benchmarking to see how M28 compares
Land zone generation:
Note that the approach used for land zone generation changed slightly just prior to release (see the
Pathing Optimisation section) – it mostly followed the same logic, except for the logic for recording
the area around a mex as being part of the same zone as the mex (where a faster method was used).
●
●
Incorporate M27 logic for recording plateaus:
o https://github.com/FAForever/fa/blob/deploy/fafdevelop/lua/sim/NavUtils.lua
o Make use of Jips tool for determining pathing
o Check navgen not already generated before running again
o Integrate M27 reclaim segment recording (since reclaim is required to record
plateaus)
Record all mexes that are near each other in the same plateau group:
o I’ll cycle through each plateau, then through each mex that is part of that plateau,
and then check the distance to each other mex in the plateau and assign it to the
same land zone as the first mex.
o This requires making sure I don’t add the same mex multiple times, and also avoiding
adding a mex already assigned to a land zone
4
o
●
Since I’m dividing first by plateau and then by land pathability, it also means I need
to check the mexes are pathable between each other by land (since they may only
be pathable by hover).
o I need to do this via a recursive method to ensure mexes in a similar area are all
grouped together.
o I also need to import an alternative to M27’s approach of determining the water
height so I can determine if mexes are underwater for these purposes (in part
because I’ll be using this functionality later on for other code, as I could just
determine if a mex is pathable by land for now), so will cycle through every point on
the map to identify those where the surface height is greater than the terrain height.
Divide map into grids, and assign each grid either to a land zone, or nil if it isnt pathable
by land:
o When assigning a mex to a land zone, also assign the segment the mex is in to that
same zone.
o Decide on a segment size to search (roughly equal to a distance of 50x50).
o Cycle through each mex, and for all the segments around it if the pathing distance is
less than approximately 50, then assign the segment to the zone that is closest to it.
o Go through each segment in map; if midpoint is landpathable and it has no assigned
land zone, then record in temporary table
o Then go through the segments with no zone that are land pathable (ie rhe temprary
rable) and search +\- higher of 4 and 50/segment size times; if no nearby land zones
are identified, then create a new zone
o Then go through each segment with no land zone that is pathable by land, and assign
it to the nearest zone.
o This should mean every segment is assigned a land zone
Doing some testing with my initial approach, it took 75.6s to complete.
I then tried adjusting the approach so I only consider creating new land zones at the corners of each
large square on the map (e.g. at position 14,14; 14, 28, etc.), and automatically adding locations
within a short area around these to the same pathing group. This reduced the time from 75.6s to
64.0s.
Analysing this, by the time it had finished assigning land zones to mexes and the ‘corner’ places it
had taken 20.9s, and then the step when it had created land zones for any remaining locations with
no zone got to a time of 63.99s, i.e. 43s of time was caused by this final step (after creating land
zones for both mexes and the corners of each large square), i.e. of going through all segments on the
map, checking if they had an assigned land zone, and if not then searching for the nearest pathable
land zone and assigning them to this.
Changing the logic to allow me to send a true/false variable on whether to use the distance
determined by the FAF built in pathing function (instead of manually recalculating) and then using
this ‘rough’ calculation for this final step that took longer changed the overall time to 119.8 (which I
don’t really understand as it is doing fewer calculations for the distance – I’m assuming that the
system time is starting from when FAF is loaded, and if I’ve had it on the menu screen for a while it
means it gives a longer value). Checking the detail, instead of 20.9 and 63.99s it took 85.48 and
119.79, i.e. 34.31s (so an improvement on the previous 43s).
5
Changing the segment size so it targets a table size of 25k (instead of 50k). This gives the following
results:
●
●
●
Pre long step: 29.019920349121
Long step: 40.017868041992
End: 40.022773742676
i.e. this has reduced the delay from 34.31s to just 11s for this step which is acceptable for me,
although this is only on a 10km map so I expect it will be slower on a larger map.
I also then discovered a bug where I was only covering ¼ of the area around a mex (i.e. within a 90
degree range), so fixing this will impact on the times I expect.
The results of the draft after fixing this are:
Open palms
Vya-3 Protectorate:
6
Frostmill Ruins:
7
Function localisations and subfunctions
One side-note – I wanted to try and write this where possible in a format that would make it easier
to have as a base FAF AI (as Jip had posted various criteria for this).
A few suggestions Jip made on the initial draft were:
●
●
●
Localising functions that aren’t used outside the file (by putting local at the start of them)
Adding comments with @param VarName VarType and @return VarType to give more
information on the function
Not using subfunctions
While I’ve done the first two, the third caused a massive performance hit, resulting in the land zone
creation logic taking more than 4 times as long as before, so I’ve kept the subfunctions (I only use
them sparingly anyway for the most intensive functions, so I hope this wont be a blocker given the
massive performance impact from it).
8
2.3)
Unit orders
I want to have custom functions for issuing any orders, to ensure I can track orders effectively and
avoid reissuing an order if a unit has already been given it.
Relent0r pointed me towards spread attack for how to translate a unit’s current commands:
https://github.com/FAForever/fa/blob/deploy/fafdevelop/lua/spreadattack.lua
I wasn’t able to make this work in that while I could get a unit’s command queue and see how many
entries it had (i.e. how many queued up orders), I couldn’t get details on the order type and position.
Jip mentioned the code is a mix of sim side and UI side so I don’t know if this is the reason why, but I
can still do what I want I think with a mix of tracking when a unit is given an order, tracking when its
orders are cleared, and comparing the size of its command queue to the size of the tracked order
queue.
2.4)
Information – economy
Gross income
M27 would update information on each brain’s economy every second, in order to have values that
wouldn’t be affected by reclaim.
However, I want to use a more efficient approach, and update the economy information for mass and
energy separately, and to have these update both once every 30s (as a general backup) and
whenever we construct an energy or mass producing unit or have one destroyed that was
constructed.
This way I can hopefully be quicker and more accurate most of the time, while the 30s update should
serve as a backup.
I’ll also have the 30s update only check if each mex/PGen/Mass fab/RAS SACU’s brtain that it was
updated against is not the same as its current brain owner, to cover scenarios where they are gifted.
Net income
My net income will still need to be updated every second, but hopefully will be quicker than before.
2.5)
Predefined locations to build
One issue M27’s engineer build logic had would be that it would for many buildings default to
looking at random locations around the start position. While this had some advantages, it also led to
potential performance issues where it was unable to find somewhere to build a large building, which
would get progressively worse as the game goes on (which is when performance is needed more).
My hope is that by using predefined locations to build and frontloading some of the logic for
identifying these, it might be better (although it could also be significantly worse performance wise –
e.g. if a land zone is 70x70 size, that could be almost 5k locations to consider. If I am considering 10
different building sizes, then I could end up doing 50k calculations every time I try to find somewhere
to build). One downside of this approach is it also will (initially unless I figure out a better approach)
lead to buildings being clumped around land zone centres.
Recording locations by size
● When building construction started check for every size in the same radius as above for every
size that has a non nil value, and see if the location is still valid for a unit of the relevant size
9
●
●
●
Also search more segments for the land zone in question to increase the number of buildable
locations.
Redundancy: refresh over a long period of time, only for land zones where we have a -1
result for valid locations of a particular size, only for size 8 and lower, and only every 5m:
o cycle through each plateau and land zone
o wait 1 tick between each land zone even if no values
o if have a zone we want to refresh (ie it has -1 size values) then wait 1 tick every 8
segments considered
o Track the time waited in aggregate; if it is less than 1000 then halve the number of
segments before waiting, rounded up, to min of 2
o If it is more than 3000 then double the segments considered to a maximum of 32
When a building is destroyed:
o Cycle through each size that we have previously search for for the land zone
o Figure out the segments that if we were to try and building something of that size
would be affected by the building being destroyed
o Create a shortlist of locations that are now buildable for that size
o Cycle through each location in the shortlist and see if it is already recorded for that
land zone as buildable; if it isnt, then insert it
Selecting a location to build at
● Use similar approach to currently for adjacency, but only for buildings in the land zone we
are in, i.e.:
o If we want to build adjacent:
▪
Identify all buildings of the desired adjacency in the land zone that we own,
and consider each of these for adjacency where they are also within the
distance required.
▪
Run through similar logic re checking if the planned build location is valid
and generate a shortlist of locations
o
▪
Run through logic to pick the best of these locations
▪
If cant find any location, then refer to the zone’s assigned build location of
that size
If we don’t want to build adjacent:
▪
2.6)
Refer to the zone’s assigned build location of that size
Land Zones – recording units by zone
My initial logic for finding adjacent locations wants to refer to units recorded against the land zone in
question and cycle through these for adjacency. This means I need to record these units against each
land zone.
My plan was to do something similar to M27’s naval logic where it will record units when they’re first
detected by any brain. However M27 took a shortcut and assumed all M28 brains are on the same
team (so only recorded a single ‘last known position’ variable for each unit) whereas I want to go
with the more accurate approach of recording the last known position for each team (with an active
M28 brain) so if a human allies with M28 against other M28, the M28 shouldn’t get an unfair intel
advantage.
10
For now I’ll record units by team (one possibility I was considering was to split teams into subteams
based on their location on the map, but the main use case would be assessing how important
something is and I could use subteam just for that part, since if I have a far away allied units in a land
zone I want to factor them in to decisions on whether that zone is safe/has enough force to beat
nearby enemies.
2.6.1) Setting up teams
For now this will be a copy of M27’s logic for creating teams and subteams.
Subteams will be setup based on how close they are to each other and the direction to the nearest
enemy, so if M28 players are in opposite corners of the map they should end up on different
subteams.
2.6.2) Recording and updating units
Whenever the intel event is triggered on a unit, it should record it in a land zone/other table based
on the type of unit, if it hasn’t previously been recorded for that team/subteam (depending on which
is being used).
I expect air and navy will use subteams, while land will use teams
Similarly, when construction is started or completed on a unit, it should trigger an update.
In addition to recording the unit in a table of units based on the particular type (if it hasn’t previously
been recorded), I also want it to update the unit’s last known position if we can see it (independent
of a non-event based check).
For this, every second I’ll loop through every land zone and if there are enemy or allied units
recorded then the last known position of these units will be refreshed, the unit’s plateau and land
zone will be checked and if there’s a difference then the unit will be moved to a different land zone.
2.7)
ACU – initial build order
I want my ACU to have its own dedicated logic since I ended up with lots of ACU specific logic before
that cluttered the main land unit logic. However I’ll want to take the ACU into account when
deciding land unit orders in a zone, so I expect there will be some overlap with the zone logic.
2.7.1) Overview
This will be similar to M27 in that the ACU should try and build a land factory, and then build PGens,
Mexes and/or assist construction of hydro based on what is most appropriate.
This makes use of the logic noted previously for identifying preferred adjacency locations and having
predefined locations to build.
For now I’ll set it up so every second I consider the ACU’s best action, and if it doesn’t have that
action it will be refreshed (similar to how M27 works with platoons).
To also help keep the code a bit tidier, I’ll separate it out so early game it has a specific initial build
order set of actions to consider, and once these are complete it switches to a more combat focused
set of orders.
11
While logic for building factory and power gens is relatively straightforward once the underlying
engineer location logic is setup, building mexes and hydros requires more work on underlying
systems.
2.7.2) Building initial mexes
For the initial build order, the decision on whether to bild a mex should be based on whether a hydro
is nearby and how much power the AI has (similar to M27).
●
●
●
●
If there is a nearby hydro then up to 4 mexes in the land zone should be built on before
assisting the hydro with the ACU
If the ACU is outside build range then it should be given a combined order,
‘MoveNearTargetAndBuild’, so it can check that it only has 2 orders and both of these align.
If instead the ACU is within build range it should just be given an order to build (or to assist if
the unit it wants to build is already being built)
If the ACU doesn’t have any orders after trying to build a mex, then resort to backup logic (as
it may be the mexes are too far away)
2.7.3) Assisting hydro – initial logic
If the ACU is in build range then it should repair the hydro if it’s under construction
If there’s no hydro under construction then it should wait up to 20 seconds and then give up on
assisting the hydro where it isnt under construction
To progress the ACU logic further, I first need to get some basic logic in place for land factories to
build engineers, and engineers to build hydrocarbons.
For the land factory I will just use a placeholder and adopt a similar approach to M27 except I’ll scrap
the ‘check every tick’ approach for all land factories and work mainly off callbacks, and just have a
backup that cycles through a max of 1 factory a tick to check if they are idle.
If a factory is identified with no building or upgrading status, and no active command queue, and it is
completed, then the logic should see if it wants to build something, and if so it should be sent the
order to build the unit.
To get engineers to build hydrocarbons will require a lot more work, as I need to setup the
framework for managing engineers and giving them orders, which in turn requires setting up more of
the framework for managing land zone units.
2.8)
Land Zones – initial management/information
In order for my engineer logic to work, I want to manage engineers via my land zone logic.
I’ve already got logic for recording all enemy and allied units by land zone, so now want to expand
this to eventually handle engineer usage.
To do this I want to be able to do the following:
●
●
On creation of land zones, record all adjacent land zones (so I can consider these for threat)
Once per tick cycle through a land zone, and record its value to my team, based on the
mexes, reclaim and mass cost of friendly buildings; Also record if the land zone is considered
part of the ‘core’ base
o To be part of a core base, the LZ must either be in the starting point, or both
adjacent to a core base LZ and contain a factory of our team’s highest tech level.
12
o
●
i.e. on large maps this means I’ll refresh land zones much less frequently, which
should hopefully help with performance
Cycle through each land zone and record the threat values for enemy units and my units for
that land zone, split between mobile and fixed threats, and between direct fire and indirect
fire (with mobile and fixed AA grouped together), and split by enemy and ally.
Available hydro locations
M27s engineer logic would spend a lot of time each cycle identifying mex and hydro locations that
are available to build on. However I want to come up with an efficient way of doing this since I’ll be
running the logic on every zone, so I’ll use events/callbacks to flag when a hydro is built on, and
when a hydro dies, and toggle the hydro location as being available/unavailable accordingly.
I’ll also run logic for deciding what tobuild for each land zone separately, based on the engineers it
has.
2.9)
Engineers
2.9.1) Initial approach
I may well change how this works, but for a starting point will introduce engineer build logic as
follows:
●
●
●
●
As part of logic of cycling through zones and deciding what to do with units in them, decide
on what engineer actions the zone wants to do (regardless of whether it has engineers in it).
The zone will record the build power that it wants where it doesn’t have enough engineers
available in the zone to carry out the actions that it wants.
o [Flaw with current approach – wont be factoring in build power already assigned by
action?]
Any available engineers will be assigned to actions, and reduce the build power that the zone
wants.
Logic for what to build at a zone will depend on if it is a ‘core base’ zone, or not.
o For core bases, a more detailed set of engineer actions should be considered to
decide on engineers wanted. Initially I’ll just set these up to build mexes or power
with engineers.
Other misc changes
● If a unit is given order to repair a building, I’ll record against the building to be repaired the
unit that is assisting it; if that unit is then built or dies, it will clear orders from all the units
logged as repairing it.
2.9.2) Unclaimed mexes and hydros
M27 used an approach of checking every ‘cycle’ where it was assigning engineers (/every second)
which mexes and hydros on the map were ‘unclaimed’.
I want something hopefully quicker, so instead I’ll record at the start of the game which mexes and
hydros can be built on. Then whenever a mex or hydro starts construction it’ll flag the location as
unavailable, and then when a mex/hydro dies it’ll flag that location as being available again.
I’ll assign this information by land zone, so I can then refer for any land zone to whether that zone
has mexes/hydro locations that have no buildings on, and if so have an engineer action to build a
mex/hydro accordingly.
13
2.9.3) Moving engineers between land zones
I want to have a pull type system for allocating engineers to adjacent land zones – each land zone
should record the total build power by tech that it wants for engineers. I then want my normal land
zone engineer assignment logic to check for land zones that want an engineer, and to send an
engineer on assignment to move to that land zone.
●
●
This requires tracking the target land zone, and treating the engineer as available as soon as
it reaches the target land zone.
Introduce tracking to see how many engineers assigned to a LZ (that aren’t in the LZ), and
then update the BP wanted for ‘move to LZ’ to be based on the tech level wanted, and the
number already assigned for that action from the cur LZ, so we can have multiple adjacent
LZs all receiving engineers at the same time.
Pathing backup
● Pathing backup – the pathing generater Jip’s implemented’s resolution isn’t as good as 1x1
meaning units can pass over a location that is pathable but that the marker generator shows
as unpathable. As a basic backup, I’ll take the following approach:
o If the unit already has a plateau group and land zone assigned, and its location shows
as having no plateau, then it will assume it is part of its previous plateau group, and
record the location in a table of ‘pathing overrides’
o If a unit has no plateau, it will then first check to the table of pathing overrides and if
an entry has been made there, it’ll use that plateau and land zone.
o If no land zone is found still, then tracking will be done of the unit every second to
see if it gets an actual land zone.
o As part of this, if the unit has no orders, and is an M28 unit, then it will be told to
move to a random location.
● So that further away land zones can be built on, I also want to have a lower priority ‘builder’
that will check for any land zones in the plateau that have requested engineers, and will send
an engineer to this directly.
● Similarly, non-core zones will send engineers to adjacent zones and then as a lower priority
further away zones.
Progress illustration
As an illustration of where m28 is with its expansion logic, running a simple replay (where it’s against
an opponent that does nothing) on Theta passage, this was the map position after 4m08:
14
I’m fairly happy with this – an engineer is queued up to cover every land zone, while the nearest land
zones are generally being prioritised first.
For contrast, M27 at 4m08 is:
15
Obviously M27 is better with building more factories and combat units (whereas M28 is just building
engineers and not even bothering with extra power), and M28 actually only has a 1 mex lead
(although it has more engineers about to build another mex), but as a general sense check that it can
expand at a reasonable rate it looks like it’s doing ok.
Related changes
●
●
If unit given order to repair a building, record against the building to be repaired the unit that
is assisting it; if that unit is then built or dies, it will clear orders from all the units logged as
repairing it.
When choosing what non-adjacent zone to send an engineer to, the closest zone should be
chosen (based on travel distance).
16
Nearby enemies
● For both non-core and core basis, if there are enemies in the current or adjacent zone, then
for each engineer check for nearby enemies, and reclaim if the enemy is nearby, otherwise:
o If we are within 10 of being in range of static enemy, or 50 of being in range of a
mobile enemy, then run to an adjacent zone that has no enemies in it; if all adjacent
zones have enemies then just try running away from the nearest enemy.
o Otherwise, if enemy in current zone and non-core zone, and adjacent zone with no
enemies, move to that adjacent zone
o Otherwise, proceed as normal.
o Also adjust BP wanted in a non-core LZ: If enemies in curzone, set to 0; if in adjacent
then cap at 5 BP.
● If we have no BP wanted for a LZ before factoring in engineers traveling to it, then clear all
those travelling engineers (assuming their action is still to travel there) – so e.g. if we send an
engineer to build a mex far away, and then later come across enemies in that land zone that
makes it dangerous, the engineer will stop trying to go there straight away instead of waiting
until enemies get near it.
● When picking a LZ to send an engineer to, first check that if we get the full movement path
we wont pass through any land zones with enemy direct or indirect fire units.
2.9.4) Misc changes related to above
●
If a structure completes the construction of a building, check if there are enemy M28Brains
that had ever seen the completed unit, and if so then add the newly constructed unit to the
land zone (so e.g. we will know if the enemy has got t2 air when they upgrade a t1 air
factory).
2.10) Economy and upgrades
One thing I want to try and get working fairly well on first release is M28’s economy – since I don’t
yet have combat logic I’ll be limited in some of the things I can do, but it also makes for an easy
sandbox testing environment for how well my AI can do when under 0 pressure and trying to eco, so
hopefully I can get its ecoing ability (when under no pressure) to a similar level as M27s (or possibly
even better, but Im not sure yet if ill want to introduce some of the ‘tryhard’ M27 mechanics such as
ctrl-King mexes or reclaiming old engineers).
2.10.1) Overview
●
●
●
●
I’m not entirely sure on the best way to approach economy upgraders. M27’s approach
works relatively well where it is brain based. However, with my engineer logic being team
and land zone based I want to try a more team based approach.
For now much rough plan is to decide on upgrades based on my overall team eco, and to
consider the threat of land zones that units are in when deciding if they are safe to upgrade.
I’ll also structure it differently to M27 – M27 split up logic into one part that would decide
how many units to be upgrading at once, and then a separate piece of code to decide what
category to upgrade (if we wanted to upgrade more than we were), and then a separate
piece of code to get a unit of that category.
This led to a fair amount of duplication by the end, so now I’ll divide things up as follows:
o Do a basic resource check (i.e. that we aren’t stalling energy)
o Consider high priority cases where we want an upgrade – for now these will
consider, land, air and mex upgrades.
17
o
After this, consider a more general upgrade logic which will look to upgrade if we
have spare resources, and not if we don’t.
2.10.2) Factory tech levels
●
●
●
One key piece of information when deciding on whether to get an upgrade and what
upgrade to get is what tech level I have for land and air.
I’ll track this both at a brain level, and also at a team level (with the team tracking the brain
with the best and worst HQ levels).
This will be done via callbacks, so whenever a factory HQ is created or destroyed it will
trigger a refresh/check of the values.
2.10.3) Priority upgrades
●
●
●
Land factories – prioritise if the enemy can path to us with land units, and has a higher land
factory HQ than we do
Air factories – prioritise if the enemy has T3 air and we don’t
Mexes – If the enemy has T2/T3 mexes, then make sure all our core land zone mexes are at
T2/T3, with 1 mex in the team upgrading regardless of how badly we are mass stalling, and
low thresholds for having 1 mex in each active M28 brain’s start position upgrading.
2.10.4) General upgrade logic
I highly expect I’ll need to refine my logic for upgrading a mex vs a factory, but for now I’ll go with
something simple:
●
●
●
●
Always try to be upgrading 1 mex
If we’re already upgrading a mex and not upgrading a factory, and have a certain level of
mass income (40 for T2, 80 for T3, adjusted for player count if we already have an active
upgrade) then upgrade a factory.
Don’t upgrade a factory if we are already upgrading a factory of that type.
‘Safe’ units should be prioritised first (based on if enemies are in the current or adjacent land
zone for now), with unsafe units only considered if we have a high % of mass stored.
2.10.5) Improvements to ecoing
Limiting engineer production when we have idle engineers
● Record for each zone how much idle BP we have in that zone by tech level
● For factories, don’t build engineers if we have idle engineers in the existing LZ of the same
tech level
Build more power sooner if needed for upgrades
● If we don’t have low mass, look to get a slight power buffer, where the aim is to get net
power income of the following:
o Highest tech is T1: E: 10%
o Highest tech is T2: E15%
o Highest tech is T3: E20%
● If we aren’t considering upgrades due to lack of energy and have at least 15% mass stored,
then more power should be built as well.
18
Engineer higher tech power
● Prioritise highest tech power - Once I have access to T2+ tech on the team, I want to stop
building T1 power and only build T2 power, and similarly only build T3 power once I have T3
tech.
Assisting upgrades
● If there is an active Mex or factory upgrade in the land zone then assist the unit that is
closest to completion.
Building mass storage
● When land zones are setup, and any time a building dies in a land zone, I’ll refresh all the
buildable locations for that land zone.
● Whenever a building is built in a land zone, then I’ll check if all of the previously recorded
locations are still valid.
● If a land zone’s mexes are all T2+, then engineers should try and build mass storage.
o The mass storage should only be built on one of these defined locations.
● I also don’t want to assist an upgrade if I’m about to power stall.
Reclaiming wrecks
● Similar to M27, I’ll divide the map into ‘reclaim segments’, whose size is chosen to mean that
most of the time an engineer at the middle of the segment can reclaim anything in the
segment.
● I’ll record the reclaim segments whose midpoints are in a land zone against each land zone,
and also record the total mass of these reclaim segments against the land zone, meaning I
can quickly work out how much reclaim is in a land zone.
● Since I spent a lot of time with M27 coming up with a reclaim system that I reworked a few
times to improve its performance/speed for recording what reclaim is on the map, I’ll use
something similar:
o As before, whenever a wreck is destroyed or created I want to flag the segment it is
in to be updated for reclaim, and cycle through these segments each tick.
o I’ll scrap M27’s process of backup logic that will refresh each zone in addition to this,
and instead replace by just delaying the logic for updating reclaim segments at the
start of the game, in the hopes of improving performance slightly.
● However I’ll be scrapping M27’s logic of prioritising reclaim areas to identify those of
interest, and instead relying on the land zone logic (and later when I implement navy I’ll need
to figure out a way of factoring in reclaim in the sea).
● For each land zone assigning engineers, if the land zone has reclaim mass/energy, then
engineers should be ordered to reclaim, with tracking done of which reclaim segments have
engineers assigned so hopefully they don’t all go to the same place (unless it has lots of mass
reclaim).
● For giving orders, for now I’ll experiment with just telling engineers to reclaim one item in
the segment, and then using a callback to reclaim more – I may end up switching back to
M27’s approach of checking every second/tick for if there’s enough reclaim in the engineer
build range to stop and reclaim it all manually (which I expect will be better overall, but I fear
the performance implications).
● As a backup, if I want to get energy/mass reclaim but cant find any, then I’ll revert to trying
mass/energy reclaim respectively in the same area.
19
I considered having backup logic where if my engineers have nothing to do in a non-core land zone
and there was reclaim then they’d get the reclaim, but this ended up producing a worse outcome
overall so I’ll leave it for now.
●
One issue that this revealed witih the integrated FAF pathfinding was that reclaim is typically
scattered on cliff edges, and these are often shown as impathable. Toe nsure engineers are
likely to try and obtain reclaim that they can reach, where I record reclaim that appears to be
in an impathable segment, I’ll see if the reclaim itself is impathable; if the reclaim is also
impathable, then I’ll look in a 2x2 square around that reclaim to see if any of those points are
pathable; if so, then the reclaim segment will be assigned to the land zone for that position
(if there is a land zone).
Reclaiming T1 PGens
● Add logic to reclaim T1 PGens (and later T2):
● Unlike M27, I want to run logic to consider reclaiming PGens only when T2+ power is
constructed, and to then loop every second if I have enough gross income to reclaim but I’m
not low on mass and/or I need the energy, and I don’t already have an active loop.
● The list of units to consider reclaiming should be assigned against each land zone.
Energy storage
● For now I’ll just build 1 energy storage (since I don’t yet have combat logic for my ACU and
want to link the decision to build extra storage to whether my ACU is being used in combat).
2.10.6) Power and mass stall managers
●
●
●
●
These will be similar to M27 but on a team basis instead of aiBrain basis.
One improvement I make is to calculate more accurately the likely energy usage of engineers
based on what they are building (previously hardcoded values were used estimating this).
When an engineer has its orders cleared, if it was paused then it should be unpaused.
T2 upgrading mexes should be paused in priority to T1 upgrading mexes.
2.10.7) Team resource sharer
●
●
●
Given how much of my logic is based on the team position, sharing resources is far more
important than with M27.
I therefore want to reassess resource values every tick, instead of every second, due to the
worry late-game that the amount of power generated could be greater than an AI’s
individual storage capacity, and so waiting an entire second could lead to
unexpected/incorrect conclusions and resource allocations.
I’ll also go with a simpler approach than M27 for now – instead of sharing with non-M28
teammates and considering prioritising certain scenarios, I’ll just share to even out mass and
energy to M28 teammates, but ignoring giving resources to an M28 brain that has at least
95% storage.
2.10.8) Misc
●
●
Prioritise 1st T2 land: Made a land factory more likely to be prioritised, and for a T1 land
factory to be upgraded to T2 even if we have low(ish) power depending on total energy and
mass levels.
A base level of power by tech level is required before considering upgrades
20
●
●
●
●
●
●
Lower tech engineers can assist a higher tech engineer even if a minimum engineer
requirement has been specified.
Wanting more power and building more power – if have just built power of a tech level equal
to our highest tech, and which represents >=15% of our gross energy, then flag that we have
just built a lot of power, and turn the flag off after 3 seonds. During that time, have any
‘need more power’ checks act as though we have enough energy.
When a unit is upgraded, clear any engineers that were assisting it (e.g. engis assisting a
factory)
When mass storage is built, check if it is adjacent to a mex owned by another player on the
same team (doesn’t have to be M28AI), and if so gift the storage to them.
When mex is built, check if it has any adjacent storage owned by another M28AI on the same
team and if so gift the storage over.
Only allow engineers to go to a core base zone if they aren’t adjacent to the LZ to the extent
that the core LZ needs more than 3 * EngBP * its cur tech level, or there are no engineers
already travelling to the LZ
2.10.9) Tracking ecoing progress:
Theta passage as Seraphim: 15m46:
● M27 (when giving enemy a shielded minibase): M173 E2800, T3 land
o M27 also had as part of this an ACU with gun+T2, T2 radar, T2 air, a handful of T2
MAA, energy storage, and land scouts at almost every mex (in addition to some less
relevant stuff such as a couple of tanks, inties, bomber and air staging), with a T2
mex 45% of thew ay to T3.
M28 eco progress during various changes:
●
●
●
●
●
●
●
●
●
●
After got factory and mex upgrades implemented (first draft): M113 E920, T2 land
After not overbuilding engineers: M113 E860, T2 land – at this point I was overflowing mass
most of the time and not building enough power.
After increasing power production: M133 E1400; After fixing a bug on starting mex upgrades:
M181 E1500, 1 T2 Land
Changing mass income check to use a 6s average instead of 1s snapshot: M205, E2000 (T2
land?)
Tweaks to have a land factory upgrade start sooner: M180 E2000, T3 at 98% (while at face
value this is worse than before, previously the AI was power stalling much more and
overflowing mass).
After assisting upgrades: M169, E1400, T3 Land
After assisting upgrades outside the core zone: M119, E1400, T3 land! Checking the replay I
think the issue was lots of engineers started assisting a T2 mex upgrading to T3, while T1
mexes elsewhere on the map were still trying to upgrade to T2.
After adding mass storage: M180 E1900, T3 Land
After adding reclaim, and limiting assistance of upgrades and storage during power stall:
M113 E3500, T3 land (which changed to M174 E1500 T3 Land after requiring a greater level
of power before starting a T3 upgrade)
After fixing a but that was causing build locations to be overwritten (meaning the furthest
away locations from a LZ that had been identified would be built on), this changed to M221
E1500, T3, with a T3 PGen 40% done. Allowing T1 engineers to assist T2 etc. changed to
M225 E2000 with T3 (but no T3 PGen – as had enough power)
21
●
●
●
●
●
●
After fixing some bugs, this changed to M182 E3500 with T3
o Adding in logic to reclaim T1 PGens didn’t change this noticeably (it became M182
E3300); making some minor change I couldn’t even remember resulted in M213
E3200
After building an energy storage relatively early, this changed to M200 E1700 (the T3 PGen
was 70% done) – this was expected as the main reason for energy storage is to support the
ACU overcharging.
After adding a power stall manager: M182, E3700 (and T3 HQ)
After adding mass stall manager: M234, E3700 (and T3 HQ)
After other tweaks (including fixing a bug with identification of some reclaim): M205 E3700
(with T3 HQ)
To show how fickle this is, I re-worked my logic for assigning engineers to adjacent zones to
in theory take an almost identical approach but in a much more performant way, and the
result was M254 E1700 (and T3 HQ), with another minor tweak resulting in M216 E4200.
2.11) Performance check and optimisation
One thing I noticed with the above approach is that performance is terrible on larger maps, and
much moreso than with M27.
On twin rivers, there was noticeable stuttering when running at +10 speed.
Trying on point of reach, by the 5m mark (1v1) the game was stuttering to the extent it was slower
than once per second (despite showing as +15 sim rate speed).
After investigation (and integrating M27’s profiling logic), it was clear the cause of this was the
engineer assignment logic for sending engineers to alternative land zones.
I therefore reworked the approach, to record at the start of the game ‘land zone travel paths’ – for
land zones where a player starts, these will consider the pathing travel time and zomes passed
through for every land zone. For other land zones, it’ll consider up to 3 adjacencies away.
The results before and after this change are shown:
2.11.1) Before change
Extracts of the profiling logs are below – the manage land zone function callse the function
considerlandzoneengineerassignment, which then calls a function depending on whether it is dealing
with a ‘core’ land zone or a non-core land zone.
Theta passage: 5m into game
● info: ProfilerOutput: No.3=ManageLandZone; TimesRun=8112; Time=1.1920318603516
● info: ProfilerOutput: No.4=ConsiderLandZoneEngineerAssignment; TimesRun=8112;
Time=1.0997047424316
● info: ProfilerOutput: No.5=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=311;
Time=0.89172172546387
● info: ProfilerOutput: No.6=ConsiderMinorLandZoneEngineerAssignment; TimesRun=7801;
Time=0.18868446350098
22
Just over 5m into point of reach:
● info: ProfilerOutput: No.1=ManageAllLandZones; TimesRun=3170; Time=48.018821716309
● info: ProfilerOutput: No.2=ManageLandZone; TimesRun=26939; Time=47.939094543457
● info: ProfilerOutput: No.3=ConsiderLandZoneEngineerAssignment; TimesRun=26939;
Time=47.746963500977
● info: ProfilerOutput: No.4=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=313;
Time=33.53755569458
I added further profiling within the function on point of reach which confirmed the cause as the land
zone travel time:
●
●
●
●
info: ProfilerOutput: No.4=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=311;
Time=30.959344863892
info: ProfilerOutput: No.5=ConsiderCoreBaseLandZoneEngineerAssignmentPreTravel;
TimesRun=622; Time=30.9339427948
info: ProfilerOutput: No.6=IsItSafeToPathBetweenLandZones; TimesRun=2314;
Time=21.246597290039
I.e. considering core base engineer assignment from the point of considering other land
zones took 30.93s of the 30.96 total time (so everything else was taking just 0.03s), and
calculating whether it was safe to path between land zones took 21.2s of time.
2.11.2) After change
I checked on theta passage to make sure I was getting similar eco performance to before (which I
was). The profiling results are:
Theta passage:
info: ProfilerOutput: No.3=ManageLandZone; TimesRun=8237; Time=0.28305816650391
info: ProfilerOutput: No.4=ConsiderLandZoneEngineerAssignment; TimesRun=8237;
Time=0.19707679748535
info: ProfilerOutput: No.5=ConsiderMinorLandZoneEngineerAssignment; TimesRun=7921;
Time=0.12686347961426
Point of reach:
● info: ProfilerOutput: No.1=DecideAndBuildUnitForFactory; TimesRun=61;
Time=1.4297122955322
● info: ProfilerOutput: No.2=ManageAllLandZones; TimesRun=3561; Time=0.63579940795898
● info: ProfilerOutput: No.3=ManageLandZone; TimesRun=22665; Time=0.57160568237305
● info: ProfilerOutput: No.4=ConsiderLandZoneEngineerAssignment; TimesRun=22665;
Time=0.43894386291504
● info: ProfilerOutput: No.5=ConsiderMinorLandZoneEngineerAssignment; TimesRun=22344;
Time=0.32414436340332
I.e. the time taken to manage all land zones has decreased from 48s to just 0.6s.
23
2.12) Land scouts
M27 took the approach of having a line of land scouts that would provide (in theory) 100% coverage
across the land section of an island. It’d then supplement this with land scouts for every mex (e.g.
for if the enemy got units past its land scout line).
For M28 I want a ‘land zone’ based approach instead – each land zone should ideally have a land
scout assigned to it, which will patrol that land zone to pick up on enemy movements.
Ideally I want to also have it prioritise land scouts of a particular faction, since Aeon land scouts are
far superior to seraphim land scouts for gathering intel.
2.12.1) Land zone patrol paths
●
At the start of the game, for each land zone calculate a path to patrol the land zone outer
segments:
o Travel in a straight line from the land zone midpoint to the midpoint of each adjacent
land zone at intervals of 4
o Record the 3rd last valid location that is in the desired land zone
o If there is more than a 90 degree angle from the midpoint that is not covered by any
of the points from above, then starting with the middle of the zone move in this
direction in intervals of 4 starting at 12, keeping track of the 3 valid locations. If at
least 3 valid locations are found, then add the 3rd last valid location to the path (i.e.
the idea is that in a land zone/island with no adjacent zones, land scouts will still
patrol the zone rather than staying in the centre)
o To avoid this resulting in pathing locations by the edge of the map, if the land zone is
within 80 of a map edge then I’ll ignore looking at a pathing point in that direction.
o Have the start point as the middle of the land zone
o Have the next point as the closest of the recorded locations to this, and repeat until
have allocated all of the patrol locations
2.12.2) Land scout assignment and orders
●
●
●
●
●
●
I’ve decided for managing units traveling to a land zone I’ll group the tables by unit type to
avoid needing to filter them each time, and because the logic for handling them will probably
be different, so I’ll have a table just for land scouts moving between land zones.
I’ll also have the logic work similarly to engineer assignment – each land zone will identify
any land scouts in the zone, and then determine which of these are ‘available’ to the land
zone.
Any available land scouts should be assigned to adjacent land zones if they are flagged as
wanting a land scout.
Any remaining land scouts should then patrol the current land zone (which should be flagged
as not then wanting any land scouts).
If there are enemies nearby, then the scout should instead run away in the opposite
direction (or run to base if it cant find anywhere after a brief bit of searching).
To patrol the land zone:
o If the scout’s last order is to the first patrol point, then do nothing; otherwise:
o clear the scout’s orders, check which point in the patrol path it is closest to, and then
issue every point in the patrol path from this point to the end, and then issue the
first point in the patrol path
24
o
●
●
To help with this I add a new order type to patrol a path, which will hopefully avoid
reissing patrol orders when theyre still being carried out.
Don’t assign land scouts to land zones with no mexes and a small number of segments.
If a land zone doesn’t have a path for it (e.g. a very small one) then backup logic will be used
to send the land scout elsewhere.
2.12.3) Land scout production
●
●
If our current land zone wants a land scout, then build one
Have a high priority engineer builder if we don’t have x number of engineers of that tech
level yet (which takes priority over land scouts).
Prioritising a particular faction’s land scouts
● One option – whenever a factory is built for a particular faction, check if that land zone has
considered unit preferences for that particular faction and tech level (or better than that tech
level).
● If not, then consider whether we want to limit what we build at other factories nearby in the
same land zone:
o Consider any land zone in the same plateau that is within 300 of the land zone where
the factory was built.
● When a factory is destroyed, then if we have no other factoires of that faction and >= that
tech in the same land zone or within 300 of the factory (using getunitsaroundpoint), then
revisit the unit restrictions.
This feels like it could get complicated (e.g. if a factory is destroyed, I also need to consider if I still
have another factory of that type nearby), so an alternative is to use subteams on the same plateau,
e.g.:
●
●
●
●
For each subteam record the number of factories by plateau, faction, factory type, and tech
level
Whenever we increase this from 0 to 1, or 1 to 0, then refresh logic of any unit BP overrides
to do for that plateau and subteam
Only consider factory HQs for this purpose, not support factories
Whenever choosing a BP tobuild, check if there are any faction overrides present, and
change the BP to nil if there are., with an optional flag to ignore faction overrides (so e.g. if
an air unit is threatening our base we will build AA regardless of faction override)
Initial prioritisation:
I want to prioritise land scouts in the following order:
●
●
●
●
Aeon
Cybran
UEF
No prioritisation
This is done in the above way so I can easily add to it in the future for other units where one faction
has a far superior option.
2.12.4) Land subteam
25
As part of the above unit prioritisation logic, I will create ‘land subteams’ and ‘air subteams’ (instead
of just subteams). The land subteams will be for any players that form part of an air subteam and are
also on the same island.
I’ll then consider blueprint restrictions based on the tech available by faction in the land
subteam/island.
Other changes
● Only allow engineers to go to a core base zone if they aren’t adjacent to the LZ to the extent
that the core LZ needs more than 3 * EngBP * its cur tech level, or there are no engineers
already travelling to the LZ
2.13) Land Zone tank management
2.13.1) Planning/overview
Land zone management goals
I want to come up with some way of managing land units that ideally achieves the following:
●
●
●
●
●
Fights that should be easily won are taken
Fights that are likely to be lost are avoided
Skirmisher units kite the enemy even when heavily outnumbered (if they outrange the
enemy)
More valuable land zones are prioritised by units when deciding where to
reinforce/attack/defend
The land zones track what units are needed and land factories take this into account when
deciding on the unit mix, so e.g. if the enemy sets up a firebase near M28’s base, then M28
will try and build MMLs/Mobile arti to try and break the firebase.
I went through a few different ideas on how to approach this, and I expect once implemented
significant revisions will be required as there will likely be flaws/unintended results from the planned
approach.
Potential approach 1 (discarded)
Sort land zone by highest value to lowest value/priority
Decide on the zone strategy based on available air and land options and enemy threat. Potential
zone strategy options:
●
●
●
●
Consolidate in zone – e.g. for where the nearby enemy threat isnt so great that we want to
run, but where the current force isnt strong enough to attack an adjacent zone. Can pull in
all forces from this and adjacent zones that are available to help with preparing for the
attack.
Fight – where enemy has units in this zone and our force is strong enough (factoring in
enemies in adjacent zones)
Retreat – enemy force is too great so want to fall back to a zone that is closer to a core base
o e.g. could have zone ‘rally points’ for a plateau, and will look for the nearest rally
point and retreat to this
Attack adjacent zone – pull in all available units from this and adjacent zones to attack the
enemy zone.
26
●
Move to zone – e.g. if there are no threats in this zone or an adjacent zone, then pick the
best priority zone to send troops to, and move to an adjacent zone that is closer to that? Will
need more thinking on how this will work
Preferred approach for combat unit management
Come up with order references for each scenario below, record against the land zone, and then
implement these orders when come to the main manage specific land zone logic.
In terms of deciding on approach to take, either:
1) Cycle through land zones sorted by priority in turn, and decide the approach to be taken for
units; or
2) Decide on the approach for the land zone, and units as available to that land zone based on if
our priority is greater than the priority ranking those units were previously assigned to zone’s
previous priority ranking
a. i.e. When a unit is given orders, it is assigned a plateau, land zone and value
reference
b. When a LZ then considers if that unit is available, it treats it as available if the value is
< the current value, or alternatively the land zone and plateau reference is equal to
the current plateau and land zone reference
c. The benefit of this approach is I don’t need a separate loop between land zones in
order of value, and so
● If we outrange enemy mobile and fixed DF with our DF (or they have defensive structures in
the LZ which we outrange with our indirect units):
o Have our DF units that outrange the enemy attack-move in range of the closest
enemy unit, or do a kiting retreat if they are already in range, and to stay out of
range of any enemy direct fire structures.
o Other DF units that have <= the range of enemy: If enemy units are within 2 of
getting in range of our higher range DF units, then move these to be inbetween the
enemy units and our higher range DF units, otherwise keep them 10 behind our
higher range DF units.
o If we have IF units that outrange enemy DF structures, have those IF units with
sufficient range try to get in range of enemy direct fire structures, and to retreat if
they are about to get in range of enemy mobile units.
o Other indirect fire units: If outrange enemy DF then kite enemy, otherwise treat the
same as ‘Other DF units that have <= the range of enemy’
● If we don’t outrange with DF, but we have significantly more threat than them, then attack
move to the nearest enemy unit.
● Otherwise (i.e. we don’t outrange enemy, and we don’t have more mobile threat than them)
retreat to the nearest land zone rally point
Moving units between land zones
● If instead there are no enemies: Decide on best adjacent land zone to move units to:
o If a land zone is set to retreat, or has no friendly units, then record it in a table of
land zones wanting support:
▪
If the enemy has structures in it, then flag that it wants indirect fire support
▪
If the enemy has mobile DF units that we don’t outrange, or that have
significantly more threat, flag that we want DF support
27
o
o
o
o
Even if we have units that outrange the enemy, if they have more threat than us also
flag that we want support.
If a land zone has an adjacent land zone that wants support, then send the units here
If there are no adjacent land zones wanting support, then search for the highest
priority land zone in our land subteam wanting support, and send the units here.
If there are no land zones wanting support, then send the units to the nearest rally
point.
Nearest rally point
For now (as I suspect I’ll want to see how the AI performs before spending too long writing code that
may need rewriting anyway) I’ll keep things simple and just have the rally points as being the core
bases of the team.
Identifying available units for use by a land zone
For now units will be included for combat logic as follows:
●
●
●
●
Only include units that are mobile with a DF or IF range, whose fraction complete is >= 1
Record such units in a table for the land zone just for the combat units
For any adjacent land zones with a lower value than our land zone:
o Cycle through any units listed in the table, and add to our land zone as available units
if they have a higher priority
I’ll therefore use two tables, one for all combat units to be considered (which will include
adjacent zone units with a lower priority assignment), and one for combat units to be
considered that are just in the current land zone.
Other related changes
● Land travel distances between each land zone on the same plateau should be calculated
upfront at the start of the game and recorded in a table, so I can do more accurate distance
checks when e.g. getting the nearest rally point.
2.13.2) Linking in to production
I want some sort of logic that flags what types of units we need to defend/take a particular land
zone, so those units can be built, e.g. factoring in if we need more indirect fire units to break the
enemy, or more indirect fire units.
For now I’ll try a very simple approach:
●
Check both the current land zone, and then the pre-recorded engineer zones that are
considered in order of distance to see if any of them need direct fire or indirect fire units.
o If we can path to enemy base with land and the LZ is within half the distance to the
enemy base, or we don’t have low mass, or the LZ is in the same island and we cant
path to the enemy base with land, then build indirect fire if it needs it, and if not
then direct fire.
Land factory production
Again for now I want something simple which I expect will need expanding greatly as I refine the
logic:
●
If it’s early game (<10m and no T2) and I have at least 5% mass stored, or I don’t have low
mass and (I either need more units to secure 50% of the map, or I have at least 40% mass
stored) then I want to build another factory.
28
●
●
●
Since I don’t yet have air logic integrated, this will be a land factory.
If I have a higher tech factory in the same land zone, I don’t want to build tanks (so T1
factories are idle). However if I have fairly high mass then the factory should be upgraded to
a support factory.
Units an adjacent zones shouldn’t be given an order if the order would be to retreat.
2.13.3) Logic refinements based on in-game results
Now that I have the barebones functionality in place (which works against the base game AI, but not
well), I want to try and refine it so that it’s in a good state before worrying about adding in other logic
such as air and PD – i.e. one flaw M27 has is it’s core land logic is weak at the T1-T2 stage (excluding
cybran T2), and it survives vs other AI such as DilliDalli by relying on other areas where it’s much
better (ACU management, navy, air, turtling). However, I want M28 to be decent at land combat
before I work on integrating logic for other areas.
Since my AI currently is only building tanks, engineers and land scouts, and for its base not much
other than factories and power, DilliDalli should prove a decent benchmark, since DilliDalli wont
attack with air units and doesn’t use its ACU aggressively that much (particularly on maps like Theta
Passage).
Therefore, if I can get M28 to a position where it can consistently beat DilliDalli without using its
ACU, air or PD, on an early game map like theta passage, I will hopefully have it in a good state (and
can start to test on other maps to cover any flaws in the logic).
I also want to start by making Aeon work, since their increased range should lend themselves to
skirmisher/kiting tactics. I’ll try vs Cybran, since the mantis have the best speed so Im assuming their
mantis will be better than other factions at dealing with auroras.
DilliDalli progress tracking – initial check
● Initially: Survived c.20m – at the start of the game M28 did ok, getting favourable trades, but
it was slow to press the advantage where it had significantly more tanks, leading DD to
outpace it quickly on eco and overwhelm.
Reviewing in more detail though, M28 suffers from a few flaws:
●
●
●
●
It’s reliance on a unit’s last known position leads to it trying to kite a unit that isn’t there – I
need some logic that will recognise if it has intel of a unit’s last known position, but it can’t
see that unit, and hence treat the last known position as actually being further away than it
is.
It currently sends every unit in the current land zone and all adjacent land zones to deal with
a threat. While this means it takes good fights, it fails to fight on multiple fronts and ends up
building a large force that can alternate between chasing one enemy, and (when that enemy
retreats) chasing another enemy.
It fails to target vulnerable enemy mexes, instead focusing on the units
The approach of attack-moving results in T1 arti crushing units like auroras which end up
tighly packed where 5+ can die to a single t1 arti shell.
Sending all units to deal with a threat is by far the biggest issue, and is more pronounced when I tried
testing with AiX 1.0 (to remove the intel issues as a factor). However, the danger going too far the
29
other way is that I end up thinking I’ll win a fight, only sending some of the tanks, and losing due to
the enemy having a concealed force.
One potential approach for this:
●
Calculate the maximum amount of threat that we want to commit to an attack for a land
zone
o This will be 3x the enemy mobile DF threat for DF units in that zone only, and 1.5
times their structure threat for indirect units in that zone only.
▪
●
●
●
i.e. we shouldn’t factor in enemy threat in adjacent land zones, since we can
always send units directly to that land zone
Don’t requisition adjacent land zone units if we already have this amount, but still send all
units in a land zone to attack the nearest enemy threat.
To the extent we have units exceeding this amount in our current zone, then send them to an
adjacent zone that needs units
The DF and IF units wanted for a LZ should be reduced by the amount of threat already in
that LZ (but not in adjacent LZs)
DD progress tracking – post update
The revised approach above achieved a dramatic turnaround in M28’s fortunes on Theta passage,
with a replay where it was utterly crushed changing into M28 dominating DD by the 15m mark (when
the replay ended) with DD shut into its base with ¼ the eco and fewer tanks. Interestingly prior to
this there was a bug causing M28 to build lots of T1 arti with a few tanks, and this got beaten (not
quickly but gradually), but I think this is partly due to DD’s micro proving very effective against T1 arti
as few shots ever land on tanks.
Most obvious was after making the above changes M28’s forces split into two large masses
(previously there was a single mass), and attacked DD from both sides.
I should note M28 still has an unfair advantage in that it wont build any AA or air units whereas DD
builds inties, but it at least gives me hope that the work to date on M28 hasn’t been wasted (as if I
couldn’t beat DD in a pure tank battle then one of the biggest points for M28 outside of possibly
running faster which may not even be achieved would have been lost).
Re-doing the replay as a full length game M28 again won (so it wasn’t a fluke).
Misc changes/enhancements based on replays
Further changes made based on checking replays against DD on Theta Passage and Eye of the Storm:
●
●
●
●
●
‘Shot is blocked’ logic added:
o If we are managing combat units for a land zone, we have twice the enemy threat,
and the unit’s a DF unit whose last shot was blocked, and it isn’t a skirmisher unit,
then move towards the enemy instead of attack-moving
Land scouts were’t retreating properly (due to some bugs in the code)
Testing against Cybran M28 initially got slaughtered, leading me to tone down the values for
taking a battle where we don’t have a range advantage so the units are more likely to attack.
On Eye of the Storm M28 threw away a winning position by upgrading a T2 factory to T3
when it only had T1 factories (who built nothing as they thought the tech level had moved
on), this was resolved by having T1 factories upgrade to T2 support factories even if we don’t
have high mass.
A few bugs were fixed relating to identifying land zones, and building skirmisher units.
30
●
●
●
●
●
●
●
●
●
●
●
On theta passage my tanks would run away despite outnumbering the enemy more than 2:1
– checking the logs it turned out they were including the enemy ACU in an adjacent land
zone, despite the enemy ACU being miles away from battle. I’ve improved on this by only
including enemy units in adjacent land zones as part of the threat calculation if they are
relatively close in distance to the current land zone.
When deciding whether to retreat, the threat of friendly combat units in the current land
zone that are on orders from an adjacent land zone should still be included (to avoid cases
where e.g. we outnumber the enemy but still run because some of the units are heading to a
nearby land zone).
When considering if a mex is safe to upgrade, mexes in the core base will be considered safe
even if there are enemies in an adjacent land zone, to reduce the risk (which happened on
one replay on theta passage) where a T2 mex well away from the main base was upgraded to
T3 ahead of all the core T2 mexes (because of a nearby enemy unit in an adjacent land zone
to the core base).
Engineers shouldn’t retreat from the core zone
Added tracking of engineer reclaim unit orders so all reclaiming engineers can be reclaimed
when the reclaim order finishes (in part to fix a strange bug where engineers could end up
reclaiming a cybran flying building drone).
Factories will build T1 arti if there are enemies near an adjacent land zone and we don’t have
low mass (mainly to deal with scenarios where we have T2/T3 factories and the T1 factories
sit idle while the enemy pushes back the main forces, since T1 arti can do ok against poorly
micro’d units even in the mid-game).
The low mass flag will now factor in how long since we last mass stalled.
Sniper bots wont be built if the enemy is still at T2 and we have built fewer than 15 T3 tanks.
A % of units should be indirect fire units to help with things like cliffs where both armies cant
hit with direct fire units and/or the enemy getting PD to defend; while this makes
performance vs DilliDalli slightly worse (since it doesn’t built PD), I’m hopeful it’ll make it
slightly better vs players.
After seeing the repeated poor performance of loyalists compared with bricks, I’ll now only
build 2 before moving on to bricks as Cybran.
T3 land factory should be prioritised after we have built 15+ T2 combat units even if the
enemy doesn’t have T3.
2.14) Construction – radar and queued buildings
2.14.1) Radar coverage
Testing out how my AI does vs DilliDalli with and without skirmishers, Cybran performs similarly,
whereas Aeon (with sniperbots) does much worse. One reason is that sniper bots need much better
intel and are punished far more for getting near the front line/near an unseen enemy.
One solution to this hopefully will be to build radar, and have T3+ skirmishers stay within radar
coverage.
M27 had a fairly poor performance approach to this where it would get every radar building and land
scout unit within a certain range, check its intel coverage, and use that to decide what intel coverage
a particular unit had. I want to for now just have something that tells me my radar coverage, and I
could potentially expand to also allow T3 skirmishers to consider attacking if there’s a land scout in
both the current and all adjacent land zones where radar coverage is lacking.
31
Tracking radar coverage
To track radar coverage, I’ll use callbacks/events as follows:
●
●
Whenever a radar is built by a player with M28 active players on its team, get the radar intel
range. Cycle through every land zone on the map for every plateau, calculating the distance
of the land zone midpoint to the radar. Record that land zone intel coverage as being the
higher of its existing coverage, and the radar. Also record against the land zone the radar
providing the best intel for it, and against the radar the land zones that it is affecting.
o If the land zone already had a radar unit recorded, then update that radar to remove
that land zone from the list of land zones that it was affecting .
When a radar is destroyed, go through the list of land zones that it was affecting (as
providing the highest radar coverage), for each one clear the list of the radar giving the best
intel, then get all radar buildings within a 600 radius (size of omni) and calculate which one
provides the best intel (if there are any), and record the intel coverage and best radar (if any)
accordingly.
Building radar
For now I’ll build radar as a lower priority similar to M27’s lower priority builders, which require a
certain level of power and mass before building.
2.14.2) Queued buildings by land zone
One thing I’d been meaning to add for a while when I started on my engineer logic was similar
functionality to M27 where I can consider queued orders when choosing where to build (so I don’t
queue two buildings at the same location).
I’ll do this on a land zone basis (in contrast to M27 which did it on an aiBrain basis), so each land zone
will track any queued buildings, and clear this tracking when construction is started.
This way I hopefully will have a fairly small number of queued buildings to consider, so I’ll just do a
distance check with each building whenever checking a location for now (M27 went with the
potentially quicker approach of recording every square that a queued building covered, and marking
such locations as having a queued building, and then checking for all squares that a new planned
building would cover to see if they were available).
2.15) Pathfinding optimisation – Winter Duel
For some reason winter duel takes ages to complete its pathfinding, compared with Theta Passage
(despite being half the map size). The below is the profiling results of the most expensive functions
at the start of the game (with the function RecordTemporaryTravelDistanceForBaseSegment being
the cause):
info: ProfilerOutput: No.1=SetupMap; TimesRun=1; Time=62.206245422363
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=62.002670288086
info: ProfilerOutput: No.3=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=55.184494018555
info: ProfilerOutput: No.4=RecordTemporaryTravelDistanceForBaseSegment; TimesRun=1161;
Time=54.731185913086
32
info: ProfilerOutput: No.5=AssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=6.5957374572754
info: ProfilerOutput: No.6=RecordPathingBetweenZones; TimesRun=1; Time=0.110107421875
The first potential solution I tried was to simplify the approach for plateaus with no mexes. If a
plateau has no mexes, then every segment on that plateau will be assigned to a land zone based on
the NavUtils label result for land pathing (i.e. each unique island on the plateau). To do this I
temporarily store a table showing how to get from this land pathing label to the land zone count
recorded against the plateau. This means I can avoid using the temporary travel distance checks for
any such plateaus (since I suspect the cliffs around the edge of winter duel are causing a strain).
This helps, but not by much, as the below log (after this change) shows – it’s reduced the time by
about 20%:
info: ProfilerOutput: No.4=RecordTemporaryTravelDistanceForBaseSegment; TimesRun=604;
Time=44.68595123291
I then increase the ‘abort threshold’ on this logic which was brought in to optimise it – essentially the
function is searching for multiple adjacent segments that have been assigned a land zone, calculating
how long it takes to path to each of them, and if it finds a ‘close enough’ match to a likely fast result
it stops looking. The higher the abort threshold, the more of a detour it allows to be ‘close enough’.
I change this threshold from adding an additional travel time of half of a segment size, to adding an
additional travel time of 30% of the total segment size * the segment search range (so if we are
looking 10 segments away, it will allow a detour of 3 segments to there to be acceptable). This
improves things by a similar amount to before:
info: ProfilerOutput: No.4=RecordTemporaryTravelDistanceForBaseSegment; TimesRun=604;
Time=31.810440063477
Increasing this slightly more so it will be 30% * segment search size + 1 improves things slightly more
to:
info: ProfilerOutput: No.4=RecordTemporaryTravelDistanceForBaseSegment; TimesRun=604;
Time=28.87996673584
I don’t want to increase the value too much since it’s intended to stop places with cliffs between
them being assigned to the same land zone.
Analysing map, this may take a minute message takes too long to appear
2.15.1) Other changes
No enemy support
When testing on winter duel I forgot to setup the teams correctly (having M28 with no enemies)
which unsurprisingly resulted in errors, and caused M28 to do nothing. It will now display a chat
message to the player warning them if it can’t find any enemies, and will operate as though the
furthest away player is the nearest enemy to it in order that some of the basic logic (mainly base
building) works.
Debuging – structure names
Structures should now display their plateau and land zone to make debugging slightly easier
33
Power stall timing is taken into account when deciding how many units to be upgrading if we already
have 1 mex (so if we have e..g 90% mass stored we wont get 4+ mex upgrades when we suddenly
have energy income); in addition, mass upgrades should factor in how quickly we will use up the
stored mass and limit upgrades to 1 at a time if we don’t have enough stored mass to fund an
upgrade.
2.16) MAA, further map setup optimisation, and other tweaks
2.16.1) Tracking enemy air units and threat
My current tracking only handles units in land zones, and also refers to if the location is pathable
amphibiously, which is a problem for air units who can travel on unpathable areas.
Since games can potentially feature huge numbers of air units, I also need to be careful that any
tracking system wont be too heavy/intensive. However, I also like the idea of trying to use the last
known position as a more accurate version of M27’s IMAP approach to better assign MAA to areas
and calculate air threats (and whether they’re more urgent than land threats).
I’ll therefore try the following:
●
●
●
●
If dealing with an air unit for assignment, it should be assigned to both a table of enemy air
units (separate tables for AirAA, AirScouts, and OtherAir for enemy and ally air units), i.e.
globally on a team basis.
In addition, it should also be assigned to a land zone if it is in one.
If it isn’t in a land zone, then it should be assigned to a table of units without a land zone.
This table should then be checked every cycle to see if any of the units are now in a land
zone (similarly to how land zone units are updated).
This logic should all be done as part of the main land zone cycler, to try and spread out the
load of updating units.
2.16.2) MAA builder logic
●
●
●
Don’t consider getting MAA until either the enemy has air to ground threat, or we have T2
land.
Get a minimum level of MAA of 5% of the total DF and indirect threat (regardless of if a LZ
wants more MAA)
o Increase to 10% if enemy air to ground threat is 6k or greater
Also added a cpa on the ratio of MAA to have relative to other units
2.16.3) MAA management
●
●
If there are enemy air units in a land zone, then move any MAA towards them, unless the
enemy has a direct fire threat that is almost in range of the MAA.
If there are enemy air units in a land zone, and there are friendly units in the land zone, or no
enemy ground units, then request MAA equal to 50% of the air to ground threat + 20% of the
airaa threat, or (if higher) 10% of the DF+Indirect threat
After introducing this I tried M28 out against RNG and M27 – it got slaughtered, but against M27
after fixing some bugs with MAA over-production it managed to sneak in a win (it was behind on eco
34
about 2:1 on open palms but M27’s ACU got overexposed and died despite M28 not having any snipe
type logic).
2.16.4) Status and performance check
Testing the new logic against the 3 main AI (DilliDalli, RNG, M27) gave the following results:
Open Palms
Generally speaking M28 was outclassed by all 3 AI, suffering convincing defeats
●
●
●
Vs M27 – outclassed (e.g. gets to about 100 mass income to M27’s 200), but M28 was able to
sneak in a win once when M27’s ACU overextended
Vs RNG – outclassed, similarly to M27 it’s main chance is that RNG overextends its ACU.
Vs DilliDalli – Faction dependent, e.g. Aeon M28 can often beat Cybran DD, but other
matches favour DD
Refinements from replays
● Idle T1 facs – if no other T1 facs are upgrading in the LZ, and we have 2+ T1 facs in the LZ,
and we have 3+ buildings upgrading in the LZ, then upgrade a T1 fac regardless of mass
income.
● If have 3+ active upgrades per player and need more engineers for a land zone, and it would
otherwise have an idle factory, then engineers should be built.
● More engineers should assist upgrades in a land zone if there are 2+ units upgrading (and
even more if there are 3+ units upgrading)
● MAA shouldn’t be built until we have T2 land or enemy has an air presence.
● If we have mass to spare and the normal builders don’t build anything, instead of building T1
arti we should built direct fire/skirmisher units if we have the highest factory tech level.
CPU performance
Although I don’t have any air logic yet, I also want to get a sense of if my AI is likely to be better or
worse CPU performance wise than M27.
M27 would run a 4v4 UEF mirror against itself on the Africa map, and ignore the time taken to
generate the initial pathfinding.
Doing the same with M28, gave the result:
info: ProfilerOutput: Total time taken to get to 6001= 49.483154296875; Total time of any freezes =
0.092045590281487; Longest tick time=0.14840698242188; tick ref = 139 to 140
For comparison, M27 v61 was:
info: ProfilerOutput: Total time taken to get to 6001= 155.51567077637; Total time of any freezes =
19.898599624634; Longest tick time=0.43302917480469; tick ref = 4690 to 4691
It’s too early to say if M28 will remain faster, but it’s very reassuring to see it dramatically faster at
this stage, as I was worried the land management logic could end up by itself taking longer. However,
the biggest time requirements for M27 were its engineer logic and land combat unit management
(the latter of which is almost fully implemented for M28).
I then did 1 match with random factions for Eye of the Storm and Astro craters:
35
Astro craters
● Cybran vs UEF DilliDalli – Win in 31m19 - an incredibly close game where DilliDalli spent most
of it fighting in M28’s core base and M28 lost 1/3 of its starting mexes and almost all
engineers yet somehow held on and once its bricks hit the field was able to push DilliDalli
back
● UEF vs Seraphim RNG – Win in c.25m (after fixing a bug causing M28’s units to retreat from
wall segments)
● UEF vs Aeon M27 – Loss in 37m - M28 was actually ontop for much of the game, but failed to
produce MAA pre-emptively meaning M27 was able to wipe out most of M28’s army with a
Czar
Improvements made based on astro replay:
●
●
●
●
●
●
●
T3 MAA will be built once we have 50+ MAA
Wall segments shouldn’t be recorded by the land zone logic.
When deciding what units to get for a LZ, consider MAA as a higher priority than indirect fire
if enemy has air to ground threat (generally) and we have a low MAA to indirect ratio; also
treat MAA as higher priority if there is any air to ground threat in that land zone and MAA is
needed
Adjusted MAA reinforcement logic so if multiple adjacent zones want MAA they will go to the
one with the biggest need.
Added several higher priority factory and second factory builders to reduce the risk of mass
overflow, along with a lower priority power builder for if we have lots of mass (since if we
scale up our BP we are likely to want to have more power).
Engineers building a 2nd factory will no longer search for existing factories to assist
Aeon and UEF land scouts should be ignored when deciding if an enemy unit is almost in
range of our combat units.
2.16.5) Map pathing optimisation
Frostmill ruins optimisation detour
I want to have every land zone record the pathing to every other land zone given the limitations
revealed with the MAA logic. However to check I can afford to do this at the start of the game I need
to see how it works on the largest of maps.
Running on Frostmill ruins took the following time:
info: ProfilerOutput: No.1=SetupMap; TimesRun=2; Time=506.27749633789
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=494.5608215332
info: ProfilerOutput: No.3=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=435.4938659668
info: ProfilerOutput: No.4=AssignNearbySegmentsToSameLandZone; TimesRun=100;
Time=435.24127197266
info: ProfilerOutput: No.5=AssignMexesALandZone; TimesRun=1; Time=52.065399169922
info: ProfilerOutput: No.6=RecordPathingBetweenZones; TimesRun=1; Time=6.5401000976563
36
Optimisation 1 – increasing number of segments considered around mexes
I expect the RecordPathingBetweenZones would take much longer if I was to change from doing just
start zones to doing every zone. However I first need to improve things for larger maps by e.g.
making land zones larger, and any other adjustments I can think of (e.g. expanding the area around
mexes for a land zone by more than currently).
Increasing the threshold on larger maps from considering a minimum of 4 segments around a mex to
a minimum of 10 results in:
info: ProfilerOutput: No.1=SetupMap; TimesRun=2; Time=75.100631713867
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=63.43140411377
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=53.929103851318
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=4.9164123535156
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=1230; Time=4.9073867797852
info: ProfilerOutput: No.6=AssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=4.1664352416992
info: ProfilerOutput: No.7=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.39918518066406
info: ProfilerOutput: No.8=AssignNearbySegmentsToSameLandZone; TimesRun=15;
Time=0.3031005859375
Increased accuracy – calculating every pathing option
If I then change to consider the pathing from every land zone (as opposed to only adjacent land
zones) for non-start position locations (to allow better options for managing MAA units and
potentially other units) this results in:
info: ProfilerOutput: No.1=SetupMap; TimesRun=2; Time=147.52470397949
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=136.01202392578
info: ProfilerOutput: No.3=RecordPathingBetweenZones; TimesRun=1; Time=79.005805969238
info: ProfilerOutput: No.4=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=5550; Time=78.990165710449
info: ProfilerOutput: No.5=AssignMexesALandZone; TimesRun=1; Time=52.511100769043
info: ProfilerOutput: No.6=AssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=4.0892791748047
info: ProfilerOutput: No.7=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.38583374023438
info: ProfilerOutput: No.8=AssignNearbySegmentsToSameLandZone; TimesRun=15;
Time=0.29238128662109
info: ProfilerOutput: No.9=ManageAllLandZones; TimesRun=711; Time=0.28631591796875
info: ProfilerOutput: No.10=ManageSpecificLandZone; TimesRun=5950; Time=0.26889038085938
37
Optimisation – calculating pathing distance once instead of twice
The above is still too long, but I may be able to optimise slightly – currently this is achieved by
looping through every land zone in a plateau as the starting land zone, and then looping through
every land zone in that same plateau as the target land zone, and calculating the difference. This
means e.g. land zone 1 will calculate the travel path to land zone 2, and then land zone 2 will
calculate the travel path to land zone 1.
I should be able to instead have land zone 2 realise it has already calculated the travel distance and
obtain those results instead of recalculating it.
I also move some logic that checks for the furthest adjacent land zone each time such a check is done
and instead do this at the end which should reduce the number of times it’s called.
After making these changes, the results are:
info: ProfilerOutput: No.1=SetupMap; TimesRun=2; Time=115.82766723633
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=104.29391479492
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=58.265022277832
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=40.830474853516
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=5550; Time=40.816192626953
info: ProfilerOutput: No.6=AssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=4.7161407470703
info: ProfilerOutput: No.7=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.4591064453125
So the time required has almost halved (which is a bit better than I was hoping, as I had to add extra
logic/checks in the single cycle for it to cover both directions).
Although 2 minutes is a long time, this is on an 80km map and the AI isn’t really intended for such
maps.
20km timing check
Testing on a 20km map, Setons, gives the following results:
info: ProfilerOutput: No.1=SetupMap; TimesRun=2; Time=55.516708374023
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=54.269756317139
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=26.1360206604
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=18.954334259033
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=4160; Time=18.944271087646
info: ProfilerOutput: No.6=AssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=6.154712677002
info: ProfilerOutput: No.7=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=2.9932746887207
38
info: ProfilerOutput: No.8=RecordTemporaryTravelDistanceForBaseSegment; TimesRun=767;
Time=1.7424964904785
info: ProfilerOutput: No.9=AssignNearbySegmentsToSameLandZone; TimesRun=19;
Time=1.2180976867676
info: ProfilerOutput: No.10=ManageAllLandZones; TimesRun=1468; Time=0.59584045410156
This is under a minute, and of this calculating the distances and travel paths between every land zone
is taking 19s, so I think this is a price worth paying.
Consequential MAA changes and misc
● Now that I can consider every land zone when considering MAA support, I’ll change the logic
to only send MAA to a LZ that will result in it having enough threat, and then move onto the
next nearest land zone.
● MAA should also try to help a zone with any non-scout non-MAA units in it, or non-scout
units if we have MAA already in it and they have an air to ground threat in it
● T3 MAA are more likely to be built if the enemy has alive strats (as I found in live games that
the strats just wouldn’t die despite a massive number of T2 MAA, despite getting a different
result in sandbox).
● After these and bugfixes, M28 would kill the M27 Czar (but would still die to T3 arti, since it
had no shielding logic). In another replay it managed a lucky win when M27 ventured its
ACU out of its firebase briefly.
● Another change was to introduce a second mass storage builder for times where we end up
with a T3 mex but still have mexes in the land zone wanting mass storage.
● Land factory builders wont ignore queued orders if we have less than 100 mass per second.
2.17) Mobile shields
2.17.1) Shield assignment:
●
●
●
●
●
Cycling through LZs from the nearest first, record the first LZ for each of the following, where
the LZ wants more mobile shields, in order of priority:
o ACU LZ – I’ll look to add this in later once my ACU logic is in place
o Zones with an enemy combat threat or air to ground threat
o Zones with enemy units or adjacent enemies
o Closest LZ
o If no land zones can be found, then the closest LZ that has combat units in it and
wants more combat units will be chosen.
Assign available mobile shields to the highest priority of the above, until that LZ no longer
wants more mobile shields or we run out of mobile shields to assign.
When assigning a mobile shield, assign it to a particular unit
When evaluating whether a mobile shield is available:
o If shield is <=50% then the mobile shield isn’t available and should retreat
o If the shield is >50%, if the assigned unit is no longer valid then make the mobile
shield available
When assigning shields to units in a land zone, to keep things simple for now Ill just cycle
through the list of units recorded as wanting shields (rather than trying to sort by distance or
value).
39
2.17.2) Shield production and management
Shield production
● If we have any land zones in the plateau that are flagged as wanting mobile shields, and we
have at least 10 net energy income (15 for T3 factories) then build mobile shields, as a fairly
high priority.
Shield orders
● Calculate the angle from the target unit to the nearest enemy base (of the LZ the target unit
is in), and aim to be a distance that is max(50% of the shield radius – shield speed – unit
speed), 3) behind this.
2.17.3) Misc changes made
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
Adjusted the distances for grouping mexes together slightly, increasing htem for larger maps,
and decreasing the distance based on how many loops have been done for the same land
zone.
Adjusted the threshold for building a high priority land factory early on to make it slightly
more likely.
Fixed bugs with the energy and mass stall logic that caused everything to be paused, and
adjusted some of the thresholds and amounts to pause or unpause.
If we are upgrading more mexes than we have players on our team, then when having a mass
or energy stall the ones with the lowest progress (until we would be left with 1 per player)
are paused.
Increased mass threshold for MAA to be assisted by mobile shields to 400 if enemy air to
ground threat is less than 1k
Adjusted the threshold for higher priority indirect fire units to be built based on factory tech
level
Limited the number of indirect fire and MAA units that are built for certain builders based on
the number already being built in the same land zone.
Land zones can no longer control adjacent land zone units if the adjacent land zone contains
enemy direct fire units.
The highest power generator should no longer be built if we have low mass
Capped number of mobile shields per player to 35 or if lower
Various bug fixes
If we have 5+ engineers of our current tech level and fewer tanks than engineers then more
tanks should be built as a high priority.
Building MAA – if the enemy has no air to ground threat in the current or adjacent LZ, and
their overall air to ground threat is low, then the number of factories building MAA in the LZ
should be capped at the lower of 2 and half the number of land factories in the LZ, rounded
down.
Engineers should stop running if there are no longer any enemy units nearby or we have
significantly more friendly force.
Added backup logic for assigning a unit to a land zone where if it is an M28 unit and it has no
orders and isnt on a land zone it will move to the nearest land zone and be added to the list
of units for that land zone.
T3 mobile artillery will be built in priority to the normal suggested unit when defending the
main base or adjacent land zones if we are at T3 and have a high direct fire threat already
40
●
●
●
●
●
●
(i.e. if we’re Aeon or Seraphim with lots of sniper bots, then some T3 mobile arti may be
good in case the sniperbots’ shots are blocked or they face lots of lower level tank spam, or
enemy sniper bots).
Added a limit on MAA if we have the equivalent of 5 T3 MAA and enemy has no/minimal air
to ground threat.
Increased the ratio of indirect fire to direct fire once we reach a high unit count of direct fire
units, and also make the indirect fire builder activate even if we have low mass if we have
lots of T3 direct fire units and too few indirect fire units.
Adjusted MAA reinforcement logic to prioritise locations with enemy air to ground threats.
After RNG bombers wrecked havoc on M28’s units (which are much more vulnerable due to
the attack-move approach for their attacks) I decided to copy over M27’s bomb dodging
logic. For now it’ll work in a similar way to M27, where both bombs and shots are
considered for dodging.
Omni sensor will be built with less energy income requirement than normal if we have built a
large number of T3 tanks
If we have radar coverage of a land zone then units regorded against that land zone will have
their actual position updated 9so e.g. if an enemy has retreated out of the land zone and we
can tell they aren’t there due to radar coverage, units will no longer avoid advancing due to
incorrectly thinking the unit is still there).
Performance check vs DilliDalli on forbidden pass:
● UEF Mirror – M28 won in 52m17
● Aeon Mirror – M28 Won in 1 hour 28m – a very close game where M28 came back from a
more than 4:1 mass deficit (was under 100 mass to DD’s more than 400 mass at one point).
● Cybran Mirror – DD won in 23m52 initially, after various changes made from other replays
(noted above) M28 won in a close battle in about 53m
● Seraphim mirror: M28 won in 46m29
Although post-changes M28 won 4/4 games, they were all close with M28 behind at various points in
the map and a fair bit of back and forth.
Vs RNG on forbidden pass
● UEF Mirror – Win in 19m07 (fairly even battle but RNG was using its ACU in combat and it
got overwhelmed by a swarm of M28 gattling bots)
● Aeon Mirror – Loss in 42m57 – M28 had the upper hand until RNG sneaked a GC through the
water into M28’s base
● Cybran mirror – Win in 46m14
● Seraphim mirror – Loss in 41m39 – M28 lost to an RNG Ythotha
For reference, v41 of M27 had a 75%-100% win rate vs RNG and a 66% win range vs DilliDalli – i.e.
M28 is almost at a similar level, despite making no use of its ACU, and not having any PD or air logic.
Vs M27 v66 on forbidden pass
● UEF mirror: Loss in 41m32 – M28 was behind the entire game, although still managed some
successful raids against M27.
● Aeon mirror: Loss in 33m36 – M28 was struggling to keep up with M27, and then M27’s strat
arrived and wiped out most of M28’s eco.
2.18) ACU – after initial build order
2.18.1) ACU Upgrades
41
●
●
●
●
●
If not in initial build order mode, if have resources to get an initial gun upgrade then retreat
until no enemies in any adjacent zone or are within 1 zone of a core base, and then get
upgrade if we have the eco to support an upgrade.
For now the upgrades will be the standard guncom sequence that M27 initially favoured, but
without the late game upgrade options (since m27 could almost never get these to work
effectively and they were only left in for fun).
When deciding if we have the resources to get an upgrade, the number of active team
upgrades will be taken into account (so e.g. a team with 500 energy combined should try to
get 1 of the ACUs upgrading, wheras for M27 it may have waited until a single player had 500
energy on its own).
To avoid calculating the upgrades wanted each time, I’ll calculate the upgrade path at the
start of the game and then whenever an upgrade is completed.
Added better support for unexpected upgrade options/mods – similarly to M27 the normal
upgrade options are hardcoded in, but if they aren’t recognised, or the faction is different,
then M28 should instead pick the cheapest upgrade that changes its weapon damage or
range.
2.18.2) Nearby enemy logic
In the case of M27 it couldn’t win battles with its land alone so relied on an aggressive ACU to help
win it an advantage early on. My hope with M28 is that as its land logic is slightly better it can afford
to be a bit more cautious with the use of its ACU. For now I’ll take the approach of having it run if it
thinks it might lose a fight or is injured, or the enemy is in range of it.
Overcharge
● First check if there are any enemies that can be overcharged. M27 handled overcharges by
allowing it as a special additional action that would then have another action queued up
after it. I want to try just having it as the only action, and then when the overcharge shot is
fired a new action is obtained, to make the code a bit simpler.
● I’ll copy over the overcharge logic from M27 since it works fairly well, the only change being
to add in tracking on a land zone basis of wall segments (so it is only checking for blocked
paths from walls based on the number of walls in a land zone rather than globally)
● Logic to assess whether the ACU is currently running (when deciding whether to overcharge
PD) is also replaced by checking if we have run in the last 30s.
Deciding whether to run
● If the ACU doesn’t have more threat (taking into account only allied units in the same LZ as it,
but considering enemies in all adjacent LZs) then it should try to retreat. It should also
retreat if low on health.
Attacking
If the ACU doesn’t want to run and there are enemies nearby, then it should try and attack them; if
its shot is blocked, it should advance to the enemy, otherwise it should attack-move until in range
and then retreat.
2.18.3) Other actions when no enemies to attack
Building mexes
If the land zone has unclaimed mex points and there are no engineers assigned to build mexes, the
ACU should build mexes.
42
Getting reclaim
If there are any notable individual reclaim items, the ACU should reclaim these.
Moving to other land zones
If the ACU has no action for the land zone then it should find the land zone with the highest value
that wants support within 175 of it, and if there is none then it should retreat.
2.18.4) Other ACU related logic
●
●
●
●
●
●
Once the enemy has sniperbots or land experimental or T3+ air to ground unit, or we have
30+ T3 units, keep ACU at core base or 1 adjacent to core base – have a flag similar to the
early game flag that indicates ACU should be kept near base.
If the ACU cant find a LZ needing combat support nearby it should try going to an expansion
with unclaimed mexes or lots of reclaim.
ACU will consider if it is almost in range of an enemy or an enemy is almost in range of it
before considering logic such as building on a mex or reclaiming.
The ACUs should be tracked by land zone, with MAA factoring in if an ACU is present when
deciding how much MAA to allocate.
ACU is more aggressive if on our side of the map, near our base, and/or against the enemy
ACU.
Allied units should factor in a % of our ACU threat when deciding whether to attack.
2.18.5) Misc changes
●
●
●
●
●
●
●
●
●
●
●
Non-skirmisher units when underwater will switch from attack-move orders to move orders
(so they don’t get stuck underwater)
Engineers should now finish building mexes where the original engineer was killed with the
mex part-complete (thanks to Relent0r for highlighting)
Early game (first 10m) M28 should be much less likely to upgrade a mex if the map is 10km
or smaller and the enemy doesn’t have T2, instead preferring more factories.
Adjusted land zone recording to increase the distance to search from mexes before resorting
to creating mex-free land zones
Improved initial build order so a second land factory shouldn’t be started until a base level of
power has been built.
Selen combat scouts should attack isolated enemy engineers.
Added logic to track when a player dies, and copied over M27 logic for sharing resources on
death.
If one M28 has 2+ energy storage more than another (on building an energy storage) it
should gift that energy storage to the teammate.
If an enemy experimental is almost in range of direct fire units, then instead of running they
should turn and attack (as e.g. in one case before this a Ythotha was able to kill 40 percies
without taking any damage since the percies would try to run from it and die before they
could fire a shot one at a time)
Reduced the amount of power that gets built initially to try and avoid overbuilding it early
game.
Reduced the amount of engineers that get sent to a land zone to reclaim, particularly
early-game.
43
●
Added a cap on the builder that gets DF units equal to the number of engineers, to be
capped at 8 units (since this triggers before analysing what each zone individually needs).
Grouping mexes near start points
I want the start area to be larger than normal land zones, both to allow for nearby hydros to be
included, and to give more building space. I’ll therefore include any mex within 70 of a start zone to
that start zone, and have them assigned to the closest start point, while also assigning start points
near each other to the same land zone.
This means that on maps like theta passage (where previously one start position included the hydro
and the other didn’t) both start positions now include the hydro.
2.19) Mobile stealth, enemy firebase, and misc
2.19.1) Mobile Stealth
●
●
●
Added in mobile stealth, largely with a copy of the shield logic, but removing the ‘retreat
when low health’ part of it
It’ll also use higher mass thresholds, with a max of 1 mobile stealth every 3 lower mass units
(so e.g. MMLs should get some stealth coverage, but not 1:1 stealth coverage, since stealth
doesn’t double their effectiveness).
If enemy has built omni or has map-wide omni, then don’t bother building deceivers.
2.19.2) T2 Arti avoidance/enemy firebase
Planning
● If the enemy has 3+ T2 arti in a land zone, then I want to avoid that area unless I have an
overwhelming force equivalent to an experimental. Prior to this I want to try and overwhelm
the T2 arti with MMLs and T3 mobile arti though.
● I also want to adjust the threshold if the T2 arti get a high number of mass kills (to avoid e.g.
a constant stream of MML being sent against a shielded T2 arti covered in TMD that ends up
killing the MML without being in any danger of dying).
● However, I don’t want to apply this logic if the T2 arti is threatening a core base, since
otherwise I lose the game.
● However, my normal land zone management logic wont work well, as T2 arti will likely
outrange the land zone size, and I only consider adjacent land zone units when calculating
threats (meaning my units wouldn’t see the T2 arti even if they were looking for it).
● I see two potential approaches here:
o Whenver T2 arti is built (or destroyed) update every land zone in it’s range to record
that T2 arti against the LZ, with this table then being considered for a land zone
when deciding what to do and if it wants reinforcements.
o Treat 3+ T2 arti as being a ‘firebase’, and record similarly to the above, meaning I
would then apply logic to decide whether to try and assault the firebase or not (e.g.
with an experimental). This could mean recording the LZ containing the firebase
against each LZ in range of the T2 artillery.
● Thinking ahead, once M28 gets to the experimental stage of the game, it is likely to run into
scenarios where the opponent has both an experimental and T2 arti in the same area; if I
only record the T2 arti, then I risk thinking I can kill them by focusing only on the T2 arti, and
then once the LZ the T2 arti are in becomes adjacent I realise there’s an experimental there
44
as well and retreat (meaning I end up constantly taking fire from the T2 arti while doing
nothing). The second approach would probably be easier to deal with this situation since I
could factor in the threat of the land zone containing the T2 arti into any decision on
whether to attack or retreat.
Identifying and tracking a firebase
● Track the number of enemy T2 arti against a land zone for a team (with a table separately to
the enemy units table)
● When adding or removing a T2 arti to a land zone, check if there are 3+ T2 arti in the land
zone, and if so record it as a firebase (or remove it if there are too few T2 arti)
● Whenever a T2 arti gets a kill, and has a mass kill value of at least 500, check the total mass
kills of all T2 arti in the same team, and if it is >=2.5k or averages more than 1k per T2 arti,
then record a firebase.
● When a firebase is recorded, add it to a global table of firebases, that for each firebase
returns both the base land zone it is in, and then a table of all land zones in range of it. For
each unit in the firebase assign it this reference number (so when a firebase is destroyed, all
land zones can be updated)
● Get every adjacent land zone with a straight line distance of 35 above a T2 arti range; record
against a table of enemy firebases for that land zone the land zone reference that is in range
of it.
Adjusting behaviour for a firebase
Factory builders:
●
●
If we are in range of a firebase:
o If the firebase is in range of a core land zone, then request indirect fire units when
considering what units the land zone wants.
o Otherwise, request DF non-skirmisher units
If there’s a firebase threatening a non-core LZ, then units should only be built to support
other land zones if we have got every mex in the current land zone upgraded to T3.
Land zone unit management:
●
●
●
If the firebase is in range of a core land zone, then apply current logic (indirect fire units
attack and ignore T2 arti)
Otherwise, when deciding if there is enough threat to attack for a LZ in range of a firebase
that isn’t in range of a core land zone, factor in the threat of any T2 arti and shields in the
firebase, and require DF threat exceeding this value.
After seeing how this works in-game, I tweaked to only factor in the firebase threat to
decisions on what to do with units if athe unit closest to the firebase land zone is withi n140
of the firebase unit closest to the unit’s land zone.
2.19.3) Misc changes
●
Have MAA backup logic go through each LZ and give it three times the MAA wanted before
resorting to staying at rally point
2.19.4) Status check
45
I’ll now do a series of random faction matchups against DilliDalli and RNG on a variety of maps to see
how it performs now that it has the ACU logic (meaning in theory M28 should have a reasonable
chance on 5km maps). Some of the below results were before some of the above misc changes.
vs DilliDalli (random faction matchups)
● Open Palms: Aeon vs DD Cybran – Win in 22m; UEF vs UEF ● Forbidden pass: Cybran vs DD Aeon - Win in 48m59
● Astro craters: Aeon vs UEF - Win in 21m13
● Theta Passage: Seraphim vs Cybran – Win in 16m23
Vs RNG (random faction matchups):
● Forbidden pass: Aeon vs UEF – Loss in 44m39 – M28 was ontop for most of the game until
RNG managed to get a fatboy near M28’s ACU just before RNG’s base was overrun.
o Doing another game, this time Cybran vs UEF, M28 won in 39m15
● Theta Passage: Aeon vs UEF – Win in 32m55
● Astro Craters: Cybran vs Cybran – Win in 27m14
● Open Palms: Aeon vs Cybran – Win in 38m46
TeamGame scenarios
● Badlands: 4v4 vs DilliDalli: Win in 18m12
● Badlands: 4v4 vs RNG: Win in c.40m (after fixing some bugs with M28’s use of overcharge –
the first run of this M28 slowly pushed RNG back before RNG got a ythotha and wiped out all
the M28 bases one at a time)
● Badlands: 4v4 vs M27: - M28 did manage to kill 1 M27 ACU, but M27’s combined
gunship-land defence forces were too much for M28, along with M27 constantly taking out
M28’s mexes with TML (since M28 cant build TMD yet).
● Open Palms: 3v3 vs M27: Loss in 37m – a resounding loss, with M28 losing at all stages of the
game.
Vs M27:
● Theta Passage UEF vs UEF – Win in 15m41 – M28’s first convincing win against M27 (i.e.
where it hasn’t gotten lucky with killing an overextended M27 ACU). This was only after
running a number of games on theta passage and fixing some bugs/slight issues with the
ACU behaviour.
o Trying again (this time Cybran vs UEF) M28 managed another win – it was losing
initially, but M27 reduced the pressure allowing M28 to win back map control and
even with M27’s TML constantly sniping M28’s T2 mexes (due to no TMD) M28 was
still able to maintain a 2:1 eco lead throughout.
● Open Palms Aeon vs UEF – Loss in 34m44 – although M28 died to a nuke it was far behind
for most of the game except the initial T1 stage.
● Forbidden pass UEF vs UEF – Loss in 39m44 - M28 stayed competitive with M27 (even having
a slight eco edge for parts of the game) until the mid-T2 stage, but M27’s superior air and
navy meant its defeat was inevitable after failing to get a decisive advantage in the early
game.
● Astro Craters UEF vs UEF – Loss in 44m23
● Williamson’s Bridge Cybran vs Cybran – Draw in 9m06 – M28 had the upper hand on eco
until its ACU ended up sandwiched between M27’s base and M27’s ACU (with M28 having 24
mass to M27’s 14 when both ACUs died)
46
●
●
Cobalt Valley UEF vs Cybran – Loss in 7m16 – Unfortunately the ACUs get in combat too early
on and M27 is better at hunting down an ACU than M28 is at defending an ACU in trouble.
Desert Arena Seraphim vs UEF – Draw in 7m38 – M28 had the upper hand on eco but could
only manage a draw when M27 pushed with its ACU to get the kill.
2.19.5) Further misc changes
From the above, M28 has a reasonable (approximately 50%) win rate against M27 on many 5km
maps, but is outclassed on 10km and larger maps. I want to see if I can improve it so that it is able to
be competitive against M27 at least until the late T2 or early T3 stage on a map like open palms (M28
will suffer around this time due to no TMD logic or logic for sending transports to plateaus; however
it cant even get to that point most of the time currently).
●
●
Added a very high MAA builder for low levels of MAA if an enemy air unit is detected.
ACU will now try and build a 3rd or 4th land factory if it is still in the core land zone and has
some mass stored.
2.20) TMD, SMD and land experimentals
2.20.1) TMD
Tracking units that want TMD coverage
● Oriignally I was thinking of having similar logic to M27, but recording units against land
zones.
● In the end I went for a rewritten approach that while similar relies much more on callbacks.
● In summary, whenever a TML or TMD is detected or destroyed, then I’ll look for all units in
range and update whether or not they want a TMD building to protect them.
● I’ll base the decision on whether they want a TMD on if they have as many TMD as there are
TML in range of them.
● Where they want a TMD, then this gets recorded in a table for the land zone they’re in
● When the unit dies, it then gets removed from that land zone.
● This way, in theory I can just check for my engineer builder whether a land zone has units
wanting TMD, without needing to do any rechecking of this table, so while the logic for
identifying units wanting TMD is fairly heavy duty it should only be run at certain specific
times.
● A TMD will only be considered as covering a unit if it covers it from all enemy TML in range of
that unit.
Building TMD
The closest TML to the land zone should be selected, along with the closest unit wanting TMD to that
TML. The starting point for the TMD to be built should then be a position 10 towards the closest
TML from the closest unit wanting TMD (6 for Aeon TMD), with a build location near here being
chosen.
●
If the TMD ends up being built too far away from the unit, then a flag should be set that the
unit no longer wants any more TMD (to reduce the risk we build infinite TMD in response to
an enemy TML)
2.20.2) SMD
●
Track enemy SML in a global table for the team, along with other significant enemy threats
like land experimentals
47
●
●
●
Land zone valuation calculation – split into two parts, one which values the M28 buildings in
the LZ, and the other which gives the value for everything else.
If land zone value is >=12k (more for non-core LZs) then want an SMD for it
If the land zone contains an SMD which isn’t loaded, then assist it
o As part of this I copy over the M27 logic to pause SMD once it has 2 missiles loaded,
and to only assist it for the first missile and then clear all engineers (although the
latter is much easier to do now that I have order tracking in place, as I don’t need
M27’s more complicated tracking of what engineers are doing what action to find
the engineers to clear).
2.20.3) Land experimentals
For now I want some basic logic for M28 to build land experimentals, mainly for if the enemy has a
large firebase or an experimental of their own, and for now I’ll have the experimentals use the
default logic (partly to highlight the flaws in the core logic, and partly because I’m feeling lazy):
●
●
●
When a T3 direct or indirect fire unit is built, if the lifetime count is >=30 then flag that we
should consider experimentals.
Build an experimental if the flag for lots of T3 is true and we have no experimentals being
built by our team or we have at least 8k mass stored +8k per no. of experimentals being built
by our team, and our team mass income is at least 7 per tick. Also build if our team mass
income is more than 16 + (30 * no. of experimentals being built by our team)
When an experimental is being built, then don’t build units from a land factory for
non-adjacent LZ threats if we have low mass.
Tracking under construction experimentals
● The simplest approach would be to track when an experimental starts construction.
However, this has the drawback that I could have 4 M28 AI each decide to start their own
experimental at a similar time because none of the others has started.
● The alternative which is more complicated is to track the build power already assigned to
build an experimental; this has the drawback if the engineers try to build somewhere that
will take a long time to start construction (an issue M27 struggled with) but I’m hoping M28’s
pre-identified build locations will reduce this risk to some extent.
● One approach to tracking could be:
o When engineers are assigned an order to build an experimental or repair an
experimental, record the engineer in the main team table, and flag that it is
reecorded in such a table.
o When an engineer has its orders cleared, if the flag is active then check for it in the
team table and remove its entry.
o When checking whether to build an experimental, check for:
▪
If we already have any engineers assigned to build it in the current land zone
(apply lower thresholds if so); and
▪
The number of other land zones (rather than engineers) with orders to build
an experimental.
Other experimental related changes
● Units whose shield is as much as 80% of their health should retreat when their shield is low
2.20.4) Misc changes
48
●
●
●
●
T1 arti wont be built by lower tech land factories if we already have 60
When considering a firebase threat, it should be reduced by any friendly units between the
current land zone and the firebase land zone.
Increased the MAA allocation wanted if the enemy has air to ground threat and removed the
requirement for there to be no enemy units.
Increased the threshold for reclaiming T2 PGens to 600 energy per player
After making the above changes, M28 was able to beat M27 on astro craters as Aeon (in 26m22),
doubling the pool of maps where it has a reasonable chance (previously just Theta Passage).
●
●
●
MAA, mobile shields and mobile stealth should be built even if trying to conserve mass for an
experimental
Increased the prioritisation of build locations by distance to reduce the risk that we pick a far
away hydro location just because the nearby one has reclaim on it.
Ignored the eco threshold for ACU to retreat if the enemy doesn’t have T3
2.21) Shielding from arti and novax and SAM creep
2.21.1) Shielding to defend from T3 arti and novax
M27’s approach
M27’s approach can be summarised as follows:
●
●
When construction is started on a unit, if it is a high value unit it is flagged as wanting
shielding by recording in a table against the aiBrain.
Every time it considers what orders to give engineers, it refreshes this table, removing dead
units and those that are now covered by a shield.
I want to instead use a more callback and localised approach for M28 to hopefully improve
performance:
Tracking units wanting shielding
● Whenever construction is started on a unit, as per M27 decide if it is to be shielded based on
its mass cost.
● Use a higher threshold if the enemy doesn’t have novax or T3 arti
o As aprt of this, track if the enemy has T3 arti/novax/experimental arti as part of
adding such units to the big threats table.
● If the unit does want shielding, then check if it is near a shield already and if so then record
that shield as protecting it.
● When novax or T3 arti is first detected, then go through every unit and reassess if it should
be flagged as wanting shielding.
● When deciding to shield a unit, store it in a table for that team’s land zone, and flag against
the unit that it wants shielding.
● When a unit dies, if it was flagged as wanting shielding, then remove it from that team’s land
zone table.
● When a shield starts construction, cycle through all units in the land zone flagged as wanting
shielding and update to note they no longer want shielding.
o Also update the shield to record the units that it is covering.
● When a fixed shield dies, cycle through all units recorded against it as being shielded, and if
they are still valid then check if they have another shield that is covering them. If not, then
include them in the table of units wanting shielding.
● (See below re tracking units where we cant find a valid location to build a shield)
49
Deciding whether to build a fixed shield
● If a LZ has units wanting shielding, then assign an action to build shields.
● Require T3 engineers if the enemy has T3 arti/novax, or the value is at least 14k mass.
● When processing the action to build a shield:
o Cycle through each unit and look for somewhere nearby to build where the shield
will provide coverage.
o If there are no valid build locations close enough, then flag that the shield range for
which the unit can’t be shielded (in case in the future I want to introduce logic to
allow a teammate’s seraphim shields to be prioritised)
2.21.2) Shield assist logic
This is largely a copy of the M27 logic with slight adjustments to work with the land zone approach:
●
●
●
Every 10 seconds, the list of priority shields will be checked for the land zone by checking any
fixed T3 shields in the land zone.
Where there are any such shields, those covering a value of at least 25k (with this value
made higher for SMD and nukes) will be treated as a priority shield (with an adjustment to
reduce the risk that two shields covering the same unit both are considered priority units).
Engineers then get assigned to the priority shield in the land zone with the least build power
assisting it.
Related changes
● Added a new engineer builder of higher priority than combat units for adjacent land zones,
for where we are defending against T3 arti, or have some mass stored, and have multiple
land factories, for which <25% are building engineers.
2.21.3) SAM creep
●
●
●
If the enemy has an air to ground threat in the LZ, then SAMs/structure AA should be built up
to the value of the enemy air to ground threat.
AA should also be built pre-emptively based on the overall enemy air to ground threat.
High value non-core zones should use similar logic, but only once we have T3 (compared to
core LZs)
2.22) Island logic and engineer build location adjustments
2.22.1) Record islands
●
●
●
Once all land zones have been recorded, cycle through each plateau and then each land
zone;
Check the island reference of the land zone based on the amphibious pathing reference for
the land zone midpoint
Record the following information for each island and land zone:
o Against each land zone:
▪
The island number of that land zone
▪
The pathing information to each other island in the same plateau, similar to
the details of pathing to other land zone, i.e. a table, ordered from the
closest island to the furthest island, which records the nearest land zone in
the island to the current land zone, along with the pathing of an amphibious
50
●
unit to get to the nearest land zone in the target island, and the travel
distance
o Against the plateau-island table: Each land zone in the island
For performance reasons I’ll only record this information for islands with mexes in them, and
also record the number of mexes per island in case I want the information in the future.
2.22.2) Initial logic for islands
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
Engineers – if have nearby (within 60% of the distance to the enemy base) islands that want
engineers, and don’t have an enemy threat at that island, then send engineer there.
Cap the number of engineers to send, to lower of amount wanted by the closest LZ, and BP
of 20 – BP of all engineers traveling to the island
If an island has at least 3 mexes, then build a land factory.
Build a land factory for every 2 mexes above 3, to a max of 4 land factories, in an
expansionbase.
As part of the logic for getting the BP wanted to move to another land zone – if have flagged
that we are using island pathing, then:
o Check if the closest friendly base is on the same island
o if it isn’t then cycle through every land zone on the current island, to see if any are
flagged as a core base, and to get the total BP wanted, based on the individual LZ BP
wanted, less any excess BP assigned to a land zone
o when deciding the BP wanted for a LZ for the engineer logic, if have flagged to use
island pathing then check this other variable to decide whether to send an engineer.
To help with decision making, I’ll now have both a ‘core base’ flag for land zones, and a a
‘core expansion’ flag – the idea being the core expansion will use similar logic to the minor
land zones, but will also build land factories, while those land factories will also have a
slightly different to normal decision making for what to build.
Land factories should no longer build skirmishers or DF combat units to keep up
numbers/proportions if they cant path to the enemy base by land.
Adjusted the MAA limit for land factores on a core expansion (so there’s still a limit but it’s
slightly higher amount than for a core base land factory).
Amphibious combat unit management – if no LZs (in the same island) that want DF support,
check if any islands that want support and if so send amphibious and hover units there.
Slightly reduced the amount of engineers to be built at a non-core base land zone.
Factory builder (normal) – check for any islands with a travel distance within the same
threshold as for combat units that have enemies on them, and if so then get amphibious
combat units.
When sending units to a nearby islan,d it should get flagged as a beachhead – units arriving
there should then check for nearby units and lower the threat requirements to attack, to
make it less likely they arrive then turn around and retreat immediately (due to no friendly
land zone units).
Land zones will no longer throw up an error at being unable to path to an ‘adjacent’ land
zone if it’s on a different plateau.
Rally points should be checked to see if they are on the same island, if there are none, then
the current land zone should be treated as a rally point (i.e. units don’t retreat from an
island).
Core expansion land zones should be considered rally points.
51
●
When recording the midpoint of a land zone, I realised the flaw with my current approach
was it could result in a midpoint that wasn’t pathable using the base ‘NavUtils’ functionality
(i.e. I was relying on backup logic to adjust it), which would then cause issues when trying to
decide if a nearby land zone was pathable on map creation. I’ll therefore move the midpoint
of a land zone to somewhere that will return as pathable, in the same island, using NavUtils,
which should produce more consistent results. This will be done first by starting with the
midpoint and moving outwards up to 50 segments to find somewhere pathable, or trying the
first pathable segment in the land zone (if this fails), or reverting to the previous default of
the nearest mex otherwise.
Other misc changes made
●
●
Lowered the net mass requirement where we have at least 5k mass stored to be based on a
percentage of the total mass stored if lower (as there were cases where M28 had 20k+ mass
stored, and thought it didn’t have the eco to upgrade a mex as its income was -200 mass per
second).
Engineers shouldn’t try and move in range of construction if there’s a building blocking that
location (but should just be given the construction order)
2.22.3) Build location blacklist
I encountered an issue where my engineers wanted to build a T3 pgen, but the location they chose
while valid was blocked by MAA who remained at that position (as there were no enemy air units),
meaning the T3 pgen never got built.
While I could change where the MAA move to (and intend to at some point), I first want to have logic
to cope with this scenario as I expect there will be other scenarios in the future where a similar issue
arises.
For now I’ll just consider adding blacklisted locations for those near the midpoint of a land zone
(given the midpoint is often used by units as a rally point), but at least if the functionality is in place I
can expand this in the future.
I’ll track blacklisted locations via a table attached to the land zone, listing each location and the
radius, along with the ‘type’ of blacklist, as I also want to build in functionality to reserve a location
to build on for potential future functionality (e.g. either for T3 arti, or just to have a location I can
constantly build experimentals at).
●
●
●
●
When an order is given to build at a location, and the engineer is the primary engineer, start
a thread to monitor the location and the primary engineer assigned to it.
Every 10s, check if the primary engineer is still trying to build at that location (i.e. its last
order), and its unit state isnt building (otherwise abort); if so, then check how far away the
engineer is, and wait until the engineer is within 30 of the build location
After 45s of the engineer being within 30 of the build location, check if the build location is
still valid, and the primary engineer is still trying to build the building at that location (i.e. its
last order) without a unit state of building/repair.
If the engineer fails this test, then check if there are any mobile units in the build area (either
non-engineer mobile units, or engineer units that aren’t on our M28team or aren’t owned by
M28AI); if there aren’t, then continue with this check every 15s for another 45s before going
to the next step. If there are mobile units then move to the next step.
52
●
Need to factor in units being paused impacting things – e.g. if unit is flagged as having been
paused, then wait 10s.
Recording a blacklisted location
● Clear the engineer that is trying to build at a blacklisted location, and any assisting engineers.
● Add the blacklisted location to the table of locations for that land zone.
● Cycle through every recorded ‘valid’ location for the land zone and check their distance to
the blacklisted location, and if they are too close then remove them as no longer being valid.
Adjusting build locations for blacklisted locations
●
When getting locations that are valid to be built on for a land zone, check for blacklisted
locations (for any reason) and don’t include if would be building partially on them.
2.22.4) Expanding the core land zone
Although it’s not yet been an issue except for when a bug prevented M28 from identifying further
buildable locations, I need logic for when I have run out of places to build in a core land zone, to
reduce the risk very late game that M28 stops building anything (other than units from factories).
For now the idea is simple:
●
●
●
When searching for valid locations to build a unit, if none can be found, and we are at a core
land zone, then flag the closest adjacent land zone (if we have any on the same island) as
being a core land zone via an override.
Check for this override when setting somewhere as a core land zone.
To test this, I temporarily disabled a bugfix that was preventing M28 from searching for more
land zones, and it resulted in M28 building some units outside its land zone.
2.23) Water zones
2.23.1) Pond creation
Similarly to M27 I want to divide water zones into ponds, and record key information about each
pond such as the number of mexes that can be hit from the pond, as well as determining the
preferred place to build the naval factory.
This will mostly be a copy of M27’s approach with some minor tweaks – I want to do this ahead of
water zone creation, as I want water zones to be based around naval factories in addition to dividing
the map into squares.
At the same time, I’ll record every segment that is in a water location, and will do this as part of the
land zone final logic step (which would check if a segment was in a land zone) to avoid taxing
performance too much.
2.23.2) Water zone creation
Once I have recorded all of the ‘ponds’ on a map, which segments are part of a pond, and where I
want any M28 brains to consider locating a naval factory, I want to divide the map into water zones.
●
●
Water zones are recorded against the pond table (a bit like land zones are recorded against
the plateau table). Water zones use a unique numerical value across all ponds (so that in the
future land zones could record adjacent waterzones as a single variable allowing the water
zone to be identified).
Will want a table to return the pond of a waterzoneref to help with this.
53
●
●
●
●
●
●
●
At first I was intending to have naval factory build locations constitute a water zone, and
setup the code in a slightly convolueted manner to achieve this, but when drafting I changed
my mind and decided to just jump to dividing the map into grids, i.e.:
o Cycle through every 200 x and z position on the map; if it is in a pond, then record it
as a water zone.
o If it’s not in a pond, then check +/- 25, 50 and 75 X and Z to see if any of these values
are water, and if so record the first such value as a water zone instead.
When recording somewhere as a water zone, ignore it if it is within 75 of an existing naval
factory desired location. Also treat the location recorded as the midpoint of the waterzone.
Once have done this/all water zones have been identified, cycle through all of them
simultaneously and assign segments to that water zone, stopping once every segment in the
pond being considered has been assigned.
When dividing the map into water zones, for small maps (5km) I’ll use smaller water zones so
there are e.g. 4 water zone on a map like sludge (almost entirely naval) instead of 1 water
zone.
Record the midpoint, min and max segment X and Z values of each waterzone. Also record
the min and max segment X and Z values of each ladn zone.
Once every watersegment has been recorded and assigned a waterzone, go through each
waterzone and record adjacent land segments.
o Option 1 – Calculate hover path from the water zone to each land zone, and
determine if it goes through any other water zone or land zone, and if not then
record as being adjacent. Main problems are it means land zones that are adjacent
but with a cliff don’t get recorded, and it will take a long time to do the calculations.
o Option 2 – Do a line in 8 (or even more) directions that keeps going until it comes
across another water zone or land zone. Might be faster (not sure) than option 1,
but will fail to pickup on land zones inbetween 2 of the lines. Potential solution is to
do 32 lines, but this might be even slower
o Option 3 – as per 2 above, but with 8 lines and then if a land zone is encountered
then any adjacent land zones to it are checked; if any of these are also adjacent to
the water zone then land zones adjacent to them are checked.
o Option 4 – Look in a square around the water zone, with the size of the square based
on the water zone square root of recorded segments.
▪ Look at square root + 10 for the radius, if are still in the starting water zone
(if going north, east, south, or west) then increase to +30
▪ Cycle through every segment on the perimeter of this square, if it’s a land
zone that hasn’t already been considered then move towards the water zone
and see if it comes to the water zone first or encounters another land or
water zone. Record a table of LZs considered this way.
o Option 5 – Only consider land zones whose min+max X and Z are within 20 /
segment size (to max of 3 segments) of the water zone’s min and max X; do a line
from the land zone midpoint to the water zone midpoint, and check if have any other
valid land zone or water zone that we reach before the target.
Then record adjacent water zones/pathing between water zones
o Option 1 – only including adjacent water zones - Cycle through each water zone in
the same pond, if the min and max Z values come within 1 of overlapping, then
check the pathing between them (navy pathing), and add to a table in order of navy
pathfinding distance between them.
54
o
Option 2 – include all water zones (think will want this so can use similar logic
approach to with land zones) – cycle through every water zone, and calculate the
navy travel distance to each other water zone (midpoint to midpoint), and then
record against each one in order of which is the closest.
▪
●
●
The approach land zones take to reinforcement of combat units is to cycle
through each land zone in order of distance up to a certain threshold, so I
need to be able to cycle through each water zone in order of distance to the
base water zone, which this should allow.
Increase the water zone size on 20km+ size maps – have part of it based on map size with the
current size the desired level for a 10km map (but don’t increase in proportion to map size,
so e.g. an 80km might only be twice the size of a 10km map).
Also record data on mexes in range of a water zone by dist and dd/if type similar to m27
pond logic? [TBC if will do this – left open to revisit once got some unit management logic in
place]
The results of the above water zone creation on Pelagial is shown below:
55
●
Require 5 segments of another water zone before treating a water zone as not being
adjacent, to make sure that corner positions still get included as adjacent to another.
2.23.3) Managing units in a water zone
Overview
I want units to be assigned to either a land zone or to a naval zone. To the extent this causes issues, I
can then address on a case by case basis (e.g. I expect one issue is a land zone sends units to attack
another island, and they enter the water zone and get different orders).
M27 would differentiate between surface combat units (e.g. frigates, hover tanks), anti-air units
(cruisers), submarines, and ‘support’ units (stealth boats and shield boats) for most of its logic, along
with a ‘bombardment’ mode where if it thinks it has won naval control it switches to trying to take
out enemy land based threats.
For M28 I expect I will want a similar approach, but more localised. I’ll want an additional category
though, to cover amphibious units that are good on land but not in water (e.g. Bricks and percivals).
56
M28 handles logic for each unit type separately, so I’ll try that approach for now, splitting between
the following categories:
●
●
●
●
●
Anti-air
o This will need logic where if there are no air threats, and it is a UEF or seraphim
cruiser, then this unit is given orders to attack enemy structures.
Hover and surface naval combat – this will include ‘bombardment’ type logic
Underwater combat (excluding amphibious units with a weak anti-naval attack)
Support units (shields, stealth)
Engineers
General management
For surface and underwater combat the approach will be similar to M27 – attack if we outrange the
enemy, or (if we don’t outrange them) attack if we have notably more threat than them or are in a
water zone containing a naval factory.
Similarly to M28’s land zone logic, enemy threat should be based on ‘adjacent’ water zones, and
handled on a team-wide basis, with a team subtable for each water zone recording relevant values:
●
●
●
●
●
When assigning units to a land zone or pond, have them assigned to a water zone and then
the water zone units refreshed the same way land zone units are.
Cycle through every water zone every second – refer to the total number of water zones in
the game, and come up with the number of water zones to assess each tick.
Record enemy threat values similarly to land zones, but just in aggregate (don’t worry about
threat by range), for just the land zone specifically.
o Combine structure threat with mobile threat
o Keep air threat tracking the same
o For combat threat will have anti-surface threat, and range; anti-navy threat, and
range; and submersible threat (some units getting included in multiple categories)
Filter friendly units into the above 5 categories.
Prepare subfunctions for each of the 5 categories above.
Units moving between water and land zones
Initially I want the water zone setup to work well for land units without building any naval units (and
then check for naval units).
Testing on maps, currently the issue is that e.g. on four-corners as UEF M27 will build percivals, send
them to the enemy island, and as soon as they step foot on the island they realise they have too little
threat and retreat.
I therefore need the percivals to group in the water until they reach critical mass and attack, rather
than retreat all the way back to the base island.
Land zone – summary of current logic approach
Reviewing how my land zones handle this (i.e. getting units to retreat and then build up in strength):
●
If the unit is to be managed by a land zone, the following are recorded:
oUnit[refiCurrentAssignmentValue] = iCurLZValue
oUnit[refiCurrentAssignmentPlateauAndLZ] = {iPlateau, iLandZone}
oUnit[refiTimeOfLastAssignment] = GetGameTimeSeconds()
57
●
●
●
The unit is only considered for assignment (and the above recorded) if the current land zone
value is greater than it’s assigned value, or it was previously assigned to this land zone, or it
has been 5s since it was last assigned.
Combat units in adjacent land zones are also considered if that land zone’s value is less than
the current land zone’s value, and has a negligible enemy threat in it
If the enemy has units in the current LZ or an adjacent LZ, then a check of enemy ranges and
threat is done; if we are outranged and the enemy has a greater threat, then units should
retreat.
o When retreating units, only units from the current land zone will be retreated; i.e.
adjacent land zone units (to have been used if we weren’t retreating) shouldn’t be
told to retreat.
Linking with water zone logic
● Ideally, I want a similar approach to my water zones. The difficulty is a water zone doesn’t
have much value since it’s unlikely to have mexes or bases to defend, so wouldn’t be
expected to override the order from a land zone unit.
● I can think of a couple of options that might resolve this:
o 1) If a unit is to retreat due to lack of threat, set a flag on it indicating it is retreating,
and/or record the plateau and land zone that it is retreating to, and/or the plateau
and land zone that gave the retreat order. This way water zones can take control of
any retreating units from another land zone and give them new orders based on if
they want to consolidate, or retreat.
▪
o
This has the downside of remembering to always record if a unit is
retreating, which is prone to error (as well as needing to update every unit
every time an order is given, to indicate if it is retreating or not).
2) If the unit has been given a move order, and was last given orders by a
plateau-land zone, get the island reference of the move order, and compare to the
island reference of the plateau and land zone giving the order. Where they are in a
different island, then this suggests the unit is either advancing or retreating. Either
way, the unit could become assigned to the water zone’s logic, which can then better
decide what to do with the unit.
▪
Although not perfect, this feels a better approach since I only need to be
considering it when a unit is in a water zone (reducing the CPU impact), and I
don’t need to record extra data for each unit. The main flaw is if I was to try
and issue orders other than move orders for a unit to travel somewhere, but
then it could always be expanded to cover these (e.g. an attack-move order
could be factored in to the logic if needed).
I’ll therefore look to do the following for my water zone logic when cycling through each unit:
●
In addition to the check based on water zone value vs current assigned value, if the unit has
a last order that is a move order, the unit has an assigned plateau and land zone, then check
the island of its assigned plateau+land zone, and see if it is different to the island of the unit’s
move order destination. If it is different, then treat the unit as available to the water zone.
o Consider sending the following units to a land zone from a water zone as part of the
main water zone combat unit manager:
58
▪
Amphibious units (I don’t want fatboys or even wagners trying to fight as
anti-sub units)
▪
Hover combat units where the enemy has no combat units in the current or
adjacent water zone
▪
o
Only consider units above where they are in the current water zone (not an
adjacent water zone)
Once a table is generated of all such units, look for the nearest land zone with enemy
units in it that is in the same island, and consider sending the units there
To generate the list of nearest land zone needing support:
●
●
●
●
●
●
One option is to pre-record the land zone distances like what do with land zones where each
water zone records each land zone in the same plateau based on hover pathing distance
(rather than land pathing distance), so can then cycle through from closest to furthest.
However, this could take ages on an 80km map with lots of water and land zones.
Another is to calculate ‘on the fly’ – e.g. have a list of land zones wanting support, and see
which is closest, but could end up with lots of LZs wanting support.
A third is to cycle through water zones in order of distance (which his already
calculated/recorded), and check if they have any adjacent land zones that need support.
Then, as a backup, if there are no land zones needing support, units should get sent to the
land zone adjacent to a water zone that is on the same island as the closest enemy base to
the original water zone.
o This requires recording the closest enemy base, similar to what is done for land
zones. Land zones do this by checking the closest friendly brain start position to a
position, and getting the primary enemy base for that brain. The land zone logic
uses the function RecordClosestAllyAndEnemyBaseForEachLandZone to do this,
which is called as part of team initialisation for now; it turns out I’d already copied
this over though: RecordClosestAllyAndEnemyBaseForEachWaterZone
o Ill therefore do this third approach both since it requires far less work, and Im
worried M28 already takes far too long to start a game and this could drastically
increase that time (while it should be relatively rare that land units are traveling
through water – i.e. it should hopefully only affect a handful of zones at any point in
time).
Once the land zone target is decided on:
o If the land zone is a core base, adjacent to a core base, or has friendly structures,
then send our units to help.
o If we have hover units that outrange the enemy forces at that island, then send our
units to the island.
o For all other units, only send them if they have x% more threat (based on the land
zone retreat %), i.e. based on below:
(iOurCombatThreat > iEnemyCombatThreat * 1.4 or (iOurCombatThreat >
iEnemyCombatThreat and ((iFirebaseThreatAdjust > 0 and
bHaveSignificantCombatCloserToFirebase) or
tLZTeamData[M28Map.subrefLZTValue] > iOurCombatThreat * 0.5)) or
(tLZTeamData[M28Map.subrefLZbCoreBase] and iOurCombatThreat >
iEnemyCombatThreat * 0.8))
59
I’ll use a custom function for this for the land zone, so I can make use of same function for this check
(so if I change it, it will affect both places)
●
If we don’t have enough threat, then choose whether to consolidate the units at the
waterzone midpoint, or to retreat:
o If we have an amphibious unit, then consolidate at waterzone midpoint
o If we have any hover units, then:
▪
Get the nearest enemy unit in the target land zone
▪
Get the best enemy range in the target land zone
▪
Comapre the distance between the enemy unit and the waterzone
midpoint (straightline)
▪
●
If the distance is more than 20 over that unit’s range, then consolidate at
the waterzone midpoint.
● If we don’t want to consolidate at the waterzone midpoint, then retreat the unit to the
water zone rally point.
When a unit is given a land zone order, clear any water zone assignment – use similar
approach to water zone function RecordUnitAsReceivingWaterZoneAssignment
Engineer assignment from water zones to other water zones and land zones
● Add logic for water zones to send engineers in a water zone to an adjacent land zone:
o Cycle through each water zone
o If wZ needs engineers, and has no enemies in it, send here
o If the WZ is adjacent to a LZ that wants BP, send to the WZ (where it should then be
told to go to the LZ if that LZ still needs the engineers)
Have land units kite naval units
● A new flag will be set to indicate if a land zone has adjacent water zones with enemy combat
units in them (so it can consider if any of them need to be avoided/kited).
● Updated various logic which makes use of the flag for if a land zone has enemies in the land
zone or an adjacent land zone, to also consider checking the adjacent water zone.
● When looking for enemies to decide whwat to do at a land zone, enemy direct fire units in
adjacent water zones should be taken into account as the ‘nearest enemy’ (which impacts
where units try to move to or run from).
Other changes
● When looking for a rally point for a water zone, if there is no ‘core’ water zone, then search
through water zones that are adjacent to land zones and pick the one closest to a land zone
rally point for the same plateau.
● When looking for a land zone override, if no land zone can be found but the unit is
amphibious/hover and the location is pathable by amphibious/hover, then no override
should be recorded.
● Land zone unit management will now consider the nearest amphibious rally point in addition
to the nearest land baesd rally point. Amphibious/hover units will then retreat to the
amphibious rally point if no land based rally points are available (i.e. this means hover tanks
that reach an enemy island and decide they want to retreat should now retreat from the
island instead of running to the middle of the land zone on that island).
60
●
●
●
●
●
●
●
●
●
A land zoneshould have its ‘expansion’ flag cleared if there are no longer any units there (so
e.g. if engineers try building a land factory and are killed, units wont rally to the midpoint of
the island).
If the ACU has no land zones to go to (on the same island) it should consider nearby islands
with enemies or unclaimed mexes.
Limit number of factories to be built if we are <T3 and the nearest enemy base is on a
different island – I’ll now massively increase the mass per factory ratios where the island is
different and less than 35% mass is stored (to help ensure enough mass for ecoing).
If enemy has a sub that is detected, then disable the move to other island logic, and also
have the ACU retreat if it is in a differnet island to the nearest base or is underwater.
o I later add an exception to this so if the ACU has 3 upgrades (so likely a
shield/cloak/nano) then it will still consider going to places closer to its base than the
enemy base.
If a unit has no antinavy attack, consider attacking the nearest surface enemy unit if there is
one; if there isn’t, then consider assigning the unit to a different water zone (the same as if
there is no enemy).
If a unit has no DF attack but has an antinavy attack, consider attacking the nearest
non-hover enemy unit. If there isn’t one, then consider assigning the unit to a different
water zone (the same as if there is no enemy).
When looking for a land zone to support from a water zone, a check is done if the units
contain amphibious units, to make sure they can path there, given amphibious units have
more restrictive pathing than hover units.
ACU should now get the amphibious rally point when looking to kite, so it doesn’t e.g. charge
into enemy T1 PD on an island.
When considering the nearest enemy naval unit to focus on, units with no antinavy attack
should only consider units that aren’t underwater.
Land scouts
● Patrol zones for water zones use largely the same logic/approach as land zones, but slightly
increased range/distance.
● Water zone midpoints should be moved further into the water zone if the initial midpoint
isn’t on land (so it’s not on the beach/shore).
● Land zones:
o After checking if adjacent land zones need scouts, adjacent waterzones should be
checked
o If a scout is assigned to a water zone, and is being considered by a land zone, it
should be sent to the WZ it is traveling to.
MAA
Since land zones only record adjacent water zones, I want any land zone with an initial surplus of
MAA to check if any of them are hover MAA, and if so consider assigning the hover MAA to an
adjacent water zone.
Rather than use a separate function I’ll adapt the existing land zone logic to handle water zones,
although after rewriting it it ended up largely being a completely separate function (so probably
would have been easier to just do via a copy).
2.23.4) Mexes
61
Similar to land zones, I want each water zone to have recorded any (underwater) mexes in it, along
with if these are available, so I can decide whether to send engineers to claim them.
The first step is adding mexes to water zone – since I’m not using mex locations for water zone
midpoint logic/positioning or assigning segments to water zones (in contrast to land zones) this is a
bit simpler so I’ll just loop through every mex already assigned to a pond (from the copy of M27 logic
for ponds), and assign each mex to a water zone, and use a similar approach to the land logic for
deciding if a mex is available to build on.
I also adjust the logic for deciding if a mex is safe to upgrade to factor in if it is underwater and there
are enemies in the current or adjacent water zone.
Mex events (death and construction)
Previously logic tracking if a mex was built or destroyed assumed the mexes would be on land, so the
code needed adjusting to also handle if the mex was on water.
Engineer orders
● =-Water zones should request engineers if they have mexes on them that are unbuilt.
● Engineers should be treated as available for a water zone if they are moving to that water
zone and are now in the water zone, or they were running away but their current water zone
has enough allied threat to deal with enemy units (or has no enemy naval threat).
● I also want engineers to actively try and reclaim enemy underwater mexes (since not many
units can easily target mexes) where the enemy doesn’t have a combat threat in the water
zone.
● Similarly to land zones, I also make sure part-built mees are tracked so they should continue
construction if they are abandoned part-way through.
2.23.5) Unrelated changes
●
●
While testing the water zone changes, I noticed MML fighting for an island wouldn’t fire at a
scouted enemy PD, and was able to reproduce this error as a human (i.e. if the MML was
moving or attack-moving it wouldn’t fire, despite the PD being revealed and in-range, until it
got really close).
o I’ve therefore adjusted the indirect fire land attack logic so if the closest enemy
structure to the midpoint is within range of the unit then it will issue an attack on it.
o Note this relates to a bug with FAF that Jip will be fixing in a later update.
In one game an ACU started an upgrade in range of an enemy ravage, so I added very
simplistic ‘cancel upgrade’ logic as a placeholder (I’ll likely incorporate M27’s logic at a later
point), which compares the ACU’s health and upgrade progress to decide whether to cancel.
2.24) Air management
2.24.1) Planning/general approach
●
●
M27 takes an aiBrain focused approach to air which works much better than I’d have
expected. However, I want to have M28 factor in information from land and water zones in
order to choose whether to attack somewhere with air or to hold off and let land forces
attack, something which M27 can’t do very well.
I also want the AirAA logic to better factor in if it is safe to support an area by targeting air
units – for example, M27 will sometimes send anti-air units to attack enemy units by the
enemy base, and/or will fly fighters over enemy MAA or cruisers that have dropped out of
radar range, suffering heavy losses.
62
●
●
●
I also want to primarily use air defensively, since after watching M27 it’s most effective use of
air, outside of the first T1 or first T3 bomber, is overwhelming an attacking enemy
experimental or ground force with large numbers. The idea will be for M28 to have logic
specific to the following scenarios:
o T1 bomber engi hunter (similar to M27) – for the first 1-3 T1 bombers (potential
future todo action)
o T3 strat mex hunter (similar to M27) – only intended for the first 2 strats, with strats
not built after this (potential future todo action).
o Gunship groups as the primary air to ground solution, which are also used offensively
where there’s sufficient AA support (whether by ground or by air) to target land or
water zones with insufficient AA defence.
o T2 bombers (plus any other T1-T3 bombers) used defensively where enemy has a
large AA threat, in suicide missions (as a last resort) – i.e. this is M27’s main focus,
whereas I want it to be much less of a focus for M28 with the main focus being
gunships (potential future todo action).
o T4 bomber – Custom logic specific to the bomber to target enemy units, and to make
use of SAM creep to support it where lacking AA support (potential future todo
action).
A key issue with all of this will be how to manage air units/split them into groups:
o For AirAA units, these should ideally be kept together to avoid a smaller enemy air
force being able to pick them apart one by one.
o However, on some maps if the M28 players are far apart the air force could risk being
caught in the middle by enemy ground AA (e.g. I saw one game with M27 where the
enemy had cruisers inbetween 2 M27 base, and the air force would respond to
threats for both bases and get wiped out).
o Therefore I’ll want to manage AirAA units on an ‘Air subteam’ basis (where at the
start of the map M28 players are grouped together based on proximity, and air is
handled as though it belongs to a separate team for each subteam).
o For air to ground units, bombers for the T1 and T3 engi/mex logic can be used on a
‘per team’ rather than subteam basis, since I’m only planning on building a couple of
them.
o For other bombers, these are best managed on an air subteam basis, so all of the
M28 players nearby can assist in an emergency defence with bombers.
o For gunships, I probably want these managed on an air-wide basis to avoid 2 gunship
groups targeting the same location. Instead, gunships will be managed by size, so
once a gunship group reaches say >20 units, a new group is formed to manage the
spare ones
This means I’ll need to be able to determine air rally points on both a team and subteam
basis, so some units (gunships, T3 bombers) can go to the nearest team-wide air rally point,
while others (e.g. ASFs) can go to the nearest subteam rally point.
Targeting enemy units
● M27 makes use of strike damage for air to ground forces, and mass value for ASFs/airaa
which works fairly well.
● Meanwhile gunships wouldn’t work on strike damage but instead would just target
vulnerable enemy locations.
63
●
●
●
Air experimentals should consider attacking ground forces based on zone assessment, and
naval forces based on distance to any air subteam’s naval fac (i.e. could be on the same air
subteam but different naval subteam)
It’s possible I could make the AirAA logic work based on strike damage as well, but I’m not
sure whether or not it would be better, and it would require some sandbox testing, so for
now I’ll just go with mass values. However, I’m hopeful that I can improve the AirAA logic so
that a greater mass value is assigned where there aren’t other AirAA targets, so that the air
force moves as a ‘ball’ rather than a ‘stream’ of units, with the ball then adjusting on the fly.
Ideally want to come up with a system where airAA units are managed on a per tick basis and
spread out over each tick, to spread the load, but not sure this will be possible. Also a
question over whether want to go with the M27 appraoch of assigning a target and then not
changing that target, or always reassessing which fighter should target which unit. A hybrid
option would be to only assess air units that are ‘surplus’ to targeting requirements – e.g.
default is to sign 3xmass value of airaa against a target; could then go up to 10x if have idle
air units, and reassign the 4th-10thx value if new targets become visible?
o A variation could be to separate logic for the different types into different ticks, so
AirAA has 1 tick, air to ground has 1, torpedo bombers has 1, transports has 1, or
even to split airaa into 2 ticks, 1 for attack logic, 1 for ‘move to rally point’ logic, but I
doubt this would make much difference since the main issue will be a large air to air
fight.
o Another alternative could be to spread the reissuing of orders to asf over a second,
i.e. the target gets assigned in the initial tick, and then the move order gets
refreshed throughout the tick. However this risks a unit having its orders cleared
and restated 1 tick, and then 1 tick later having the order given again, with this
potentially reducing the amount that the air units fire.
o A third alternative is to do things like target reassessment on a tick by tick basis,
which would spread the load for some parts of the calculation – I think this
approach would be best.
Sandbox testing of air fight
●
●
●
Trying to micro an asf to ‘slow down’ so it can fire more often produced very poor results in a
1v1 scenario, with the micro’d asf ending up hardly firing at all while it was killed by the
other asf, therefore I’m not sure spending a long time on this approach would be worthwhile
(although the results would probably be better against large air groups).
However, one issue with the ‘attack order’ approach is that asfs don’t fire at other units they
pass near when on their way to the attack order target, so move orders instead of attack
orders would be better, except they would have the downside of needing refreshing
constantly which would create a greater CPU load.
To properly determine which method is best, I’d probably want to do a 30 M28 asf vs 30
M27 asf battle a number of times to see which works, and even that wouldn’t be reliable
since some approaches work best against specific other approaches, and human players
will be using a ‘move ball’ approach rather than M27’s spread attack type approach.
2.24.2) AirAA logic in more detail
Idle airAA units
● Manage AirAA on an air subteam basis
64
●
Have airforce wait at either the nearest rally point, or (if have high priority targets to protect)
the priority target nearest the enemy base that is nearest the air subteam rally point, when
idle.
o When escorting a high priority target, get the nearest air rally point to that land
target, and then travel in a straight line from that air rally point to the target, until
reach a land zone or water zone that has significant enemy ground to air threat, and
pick the previous land or water zone as the staging point.
Targeting enemy units and treating air units as available
● If an airAA unit has no targets:
o If it is low health or fuel, send to nearest air staging (or to the air rally point
otherwise)
o Otherwise, make available for targeting, and send any remaining units without
targets to the air rally point
● If an airAA unit has a target:
o Check if still want it as a target, if not then abort – unlike M27, I want to experiment
with doing this regardless of how close the unit is (my concern with M27 was this
would allow human players to bait its air force, but the downside is M27 often
proceeds with air fights that it would lose, so I want to see how well or badly it works
if the air units flee when they’ve lost)
▪
Means need some way of tracking if a unit is a current target or not. One
option is to record the time that it was last a target, and where this is more
than 2s to treat it as not being a target.
▪
May also want to use a distance based measure, e.g. if prioritising targets
based on distance to a location, allow to remain targeting a unit that gets up
to say 20 further from this distance (to avoid considering a large group of
enemy asf, targeting one of them that then moves further into the ball,
leading us to think it’s not a target as the logic tries targeting the asfs on the
outer edge of the ball)
▪
o
threat, but do the reassessment on a tick by tick basis to spread the load.
Test sending a move order to the target (and refreshing this) vs sending an attack
order to the target
▪
●
Alternatively could just manually assess each target for if it has significant air
In the future could consider whether could do microing to make air units fire
for a greater % of the chance? E.g. once in range of the enemy and facing it,
calculate based on our facing direction, enemy facing direction, and our and
enemy speed, whether we want to slow down in order to fire more. Would
only want to do if handling <75 airAA units in a single cycle – see above
sandbox notes where I’m not sure this would work.
Ultimately the approach I want to try and take with target selection is that each asf ends up
targeting the nearest enemy air unit to it, until a certain threshold is reached, at which point
the next nearest enemy air unit gets targeted, etc., with the possibility of a tierd threshold
system with say x3 mass value assigned to every enemy air unit, and then spare air units go
for the nearest units again up to x6 mass value.
65
Target selection
● I need to decide how targets will be selected/prioritised. The best approach is probably
going to be a distance based metric, but this raises the question on what I use as the base
point for the distance – the nearest air rally point (more accurate, but risks strange results as
and when the air rally point changes); or an ‘average start point’ for the air subteam which
has the benefit of not moving, but the downside of treating 2 air units that are far apart from
each other on opposite sides of the map as similar priority if they’re similar distance to this
average start point. A third option is to base it on the ‘air support location’ (which will
sometimes be the rally point, and sometimes be near to a unit that we are trying to protect.
● A fourth option is to order all land and water zones based on proximity to a friendly base,
and then cycle through these in that order, meaning the recording only needs to be done
once.
● A variation of all of these would be to first check priority targets to defend and units near to
the support point or the air rally point, and then only consider other targets after dealing
with these.
● I think the 3rd or 4th option would be best – the 3rd option gives the best likelihood of my air
force remaining concentrated, while the 4th option is much easier to achieve (as it avoids
needing to reorder the land and water zones every second) and should also therefore
provide a slightly more stable targeting result. The 4th option would be combined with the
variation for looking at priority targets.
● For performance reasons I want to avoid searching too far out for targets if I don’t have the
AirAA units to consider the targets. One option would be to have a distance threshold for
land and water zone midpoints from the midpoint of the support point land/water zone,
based on the higher of the following:
o Furthest M28 airaa unit from the support point + 110
o If we had idle airAA units last cycle, then 110 + the previous distance threshold
o 110 + Furthest target for AirAA that has assigned units to it
● An alternative would be to instead assign AirAA to enemy targets as and when they are
encountered, and stop the cycle once all AirAA units have been assigned.
o This alternative requires some logic that will for any given support location work out
the land and air zones near it, based on a straight line distance.
o One option for this is to prepare a table of such logic ‘on the fly’, since it will mean
using straight line distances for potentially hundreds of land zones on large maps
which would be time consuming to record at the start of the game for every possible
land and water zone.
▪
E.g. the start of the game could just record this for each start position, so
there’s a startpoing recorded point.
▪
Then, if the support position changes, the calculations could be done over
the next second (i.e. spread out each land zone and water zone over 10
ticks), and once the calculations have been completed, this new location
could be used as the new support location; however this wouldn’t work
when escorting a fast unit like a strategic bomber which might move
land/water zones by the time the calcualtions are done.
66
▪
However it might still be possible to fit the calculations inside 1 tick, with the
advantage that performance should improve as the game goes on and more
locations have the support logic calculated.
▪
As part of these calculations I want to record the angle of a location to the
support point, so that I can potentially factor in angle and distance when
determining if a land/water zone’s AA should be taken into account for
determining a safe path to a target.
AirAA logic summary from above:
● Each cycle determine the air rally point:
o Go through start positions on the air subteam, and factor in friendly ground AA and
enemy ground AA to pick the ‘best’ location
● Each cycle, get the ‘support location’:
o If have any priority units, then get the closest priority unit to an enemy base (by
getting the land/water zone of each priority unit, and checking the distance to the
nearest enemy base from this midpoint)
▪
o
determine the nearest land or water zone to the particular position, and
record this in a table (similar to the plateau and land zone override)
Starting from the closest friendly base, and then going through each land/water zone
on a straight line basis, check if there is an adjacent enemy groundAA threat, and
keep going until reach a land/water zone with an adjacent threat, at which point will
move ‘back’ one.
▪
●
●
●
If the unit to support isn’t on a land or water zone, then run logic to
If the base that start from has an adjacent enemy groundAA threat, then just
use the air rally point.
Get the AirAA units that are available (i.e. not attached/already refueling, and not going for
refuelling) for the air subteam.
o Bombers near a target they are attacking, and airaa units near a target that has
enemy air in it, should not be treated as available for refuelling.
First check for any enemy air to ground threats around a priority unit (i.e. in the current or
adjacent land/water zones)
Once done this, if still have available AirAA units, cycle through each land and water zone
recorded in order of distance per the above, and check if there are any enemy air units
recorded against the land/water zone.
o If there are, then assess if the air units should be targeted based on the enemy
ground to air threat from there to the support location, along with the nearby enemy
airaa threat (based on how far behind we are on air control and if we have friendly
ground AA in the location)
o Assign available AirAA units to these targets based on the closest airAA unit, with the
AirAA unit being given a move order to the target
While writing the above initially I held off doing any pre-recording of air paths to understand for sure
the use case. I’ve included notes on the thought process for targeting enemies, determining ‘air
paths’, and if an enemy is safe to target below.
2.24.3) Enemy targeting and air path threats
67
The following section contain rough notes I made when trying to figure out how to have AirAA units
target enemy air without venturing near enemy SAMs/flak, and also having a small AirAA force not
suicide into a large air force.
Choice of enemy units to target – only include if:
●
●
Is in the zone itself that are trying to protect; or
Has no groundAA
o i.e. have a flag for whether to check for groundAA when considering somewhere
o will need to reconsider the true flags used for if we have considered somewhere,
might need extra series of flags, i.e. ‘consideredifnoAA’ and
‘consideredregardlessofAA’
However, first need to add in logic that works out every land and water zone including adjacent ones
that will pass by if going from the support zone point to the target, so can then quickly reference this
when calculating if somewhere is safe? That way can do these calculations in a different tick to help
spread the load.
●
Considering potential memory requirements, if there are say up to 1k land and water zones
combined, with an average path of 10 entries to get to the support zone, this would mean
storing 10k data entries; assuming 10 pieces of information are stored for each entry, this
would mean 100k data every time. Realistically would then struggle recording if the support
zone moved around a lot in large team games.
o One solution to this could be to limit the range on any support path calculation?
And/or to ignore plateaus or land zones with a small number of entries?
▪
o
o
o
6 in size?
An alternative solution is to only store information to the next nearest land or water
zone to get to the support zone, and then use that information – that means
information only gets stored once, and saves a lot on the calculations.
i.e. each land or water zone would store a single table containing the plateau, land
zone and water zone (with land being nil or water being nil depending on which it is)
of the land/water zone that it will pass through on the way to the support zone.
Would then need to calculate ‘on the fly’ the total list of land and water zones that
will pass by (including adjacency) if going along that route – so less calculation
overall, but a bit more of it happening in the airaa tick. However also a lot less data
is stored.
▪
●
For example, ignore any plateau whose min and max XZ values are less than
Could also probably refine further so we store the enemy AirAA and
groundAA cumulative values along the path to the support rally point.
One option – round the start x and z, and end x and z, which are based on the land/water
zone midpoint of the support point and the location the target is in, and have a table with
these 4 as keys, which returns a table of intermediary entries (i.e. excluding start and end)
for the plateau, land zone, and water zone references along with a flag each time if it’s a
water zone or land zone, for both the path, and any adjacent references, with this only
including each location once (not multiple times)? That way get around the issue of land
zones and water zones needing separate referencing.
68
Could have table for each land and water zone of the adjacent land/water zone to reach a particular
land/water zone; would sort by having x = (1 = land zone 0 = water zone); y = plateau, z = land or
water zone red of the support location or rally point
Then cycle through every land amd water zone and if ot doesnt have the adjacent value set, record it
Then think of a way to record cumulative value of enemy ground aa; and enemy non-ground AA;
-if do as part of land and water update issue is it is calculating for everything and isnt in order of
distance from support/rally
-if do as part of airaa logic then have to redo for gunship/bomber and harder to do cumulatively;
although gunship logic will probably want to be done based on front gunship position
●
●
Have temporary table that reset each cycle which records the ground AA; and separately one
for AirAA (reduced for friendly groundAA) along a path from zone start point to zone end
point
When recording the zones that come across in a line from start zone to end zone, only record
adjacent zones if we would be in that zone if traveling towards the midpoint of that zone by
70 from the line that we draw from start zone to end zone, to avoid including too much of
the water – could even just test vs the min and max X and Z values of the zone to see if we
might enter into the min/max (i.e. if do a square around the zone, if we will enter into that
square, which would presumably be even quicker to calculate, and would avoid the issue of
landing on a cliff that isnt in the zone, although would also be less accurate)
If take this new approach, then not sure can do the ‘only record adjacent zones on thew ay to target,
and then use their recorded adjacent zones’ approach, unless such information is recorded for a
given start and end zone.
I was going to (based on the above) have a temporary table of air ground threat, done for each airaa
subteam, which works similar to the above in that you input the start zone and end zone, and it
returns the enemy groundAA; and a separate temporary table for air air threat. When deciding
whether a zone has too much AA to target, this value will be calculated.
●
●
AirAA threat should be reduced by 2 x any friendly groundAA threat
Would then apply a factor to enemy AirAA threat vs our AirAA threat when deciding whether
to engage or not, with the factor depending on if we are far behind on air.
However, I’m not sure how much efficiency I’ll actually gain from such a temporary variable (since my
AirAA will assume every air unit is traveling from the support zone) so for now I’ll just calculate on
the fly and revisit when I come to adding in bomber logic if I think it would be more efficient to
record the AA result.
Summary of approach to use
● Global table of air pathing, which has variables for the start zone, and the end zone, and
returns a table of all of the zones that will path through to get there, along with any zones
that will get within 65 of the edge of the zone; this is calculated as and when it is needed
● Enemy air units will only be considered if they have no groundAA threat in them or in a path
to them from the support zone
2.24.4) Refuel units
69
●
●
●
●
●
●
Per the above I should have a table of units that I want to send for refuelling, which will
include units that already have been given a refuel order, and units that haven’t.
With M28’s approach to orders, I want to create a new tracked Refuel order, so I can avoid
telling a unit to refuel if it has already been given the order.
o If a unit’s order is cleared, and the last order was to refuel, then need to make sure it
isn’t registered as refuelling? Or could handle as part of checking for available
airstaging platforms
o Adjust logic so strat bomber takes up 4 space? M27 logic doesn’t work so well as if
air staging has 2 units in it, it will treat a strat bomber as being able to make use of it
I think
I also want to take a similar approach to M27 regarding calculating available air staging
platforms to send units to refuel at, but fixing some of the issues M27’s simplifications
caused, in particular:
o Create a list of all air staging platforms owned by the air subteam
o Calculate for each of them how much spare capacity they have
o Cycle through each unit wanting refuelling, and based on its size find the closest air
staging platform with enough space to fit it
o Send any remaining untis (for whom there are no available air staging platforms) to
the air rally point.
Have idle air units refuel if their health is <85% or their fuel is <55%
If an air staging has units in it that have full health and fuel then all such units should be
released.
If a unit’s last order was to refuel but the target is dead then it is treated as available.
Meanwhile if a unit was recorded as being told to refuel but it is dead or its air staging target
is different to the air staging unit being considered, then it is removed from that air staging’s
list of units trying to refuel (so the air staging is treated as being available).
2.24.5) Attacking with land vs air – gunship logic
Whereas M27 relied heavily on bombers for defence and gunships were also a defensive tool that
would try and avoid going onto the enemy side of the map, for M28 I want to experiment more with
focusing more on gunships in preference to bombers, and using gunships aggressively where the
enemy lacks anti-air.
My initial thoughts on logic to use are as follows:
●
●
●
●
●
●
Get a list of available gunships (vs those wanting refuelling etc.) similar to AirAA units.
Send gunships for refuelling similarly to for AirAA units.
Check for any enemy units in a core base, and consider attacking units if we have available
gunships (i.e. as a ‘last resort’), regardless of AA threat
o To help with adding enemy units, I want to expand the previous function created for
airaa units to add in a threshold for groundAA
If still have no enemy targets, then get the gunship nearest to an enemy base, and use this
front gunhip’s nearest land/water zone as the start point.
Check if any enemy airaa threat in current or adjacent land or water zones, and if so then
return to rally point
If still have no enemy targets, then check the current and adjacent land zones to the front
gunship – for each one, consider if any enemy units, and (if so) calculate the enemy
groundAA threat and AirAA threat.
70
o
●
●
If any airaa threat, then retreat to rally point (i.e. in a zone adjacent to an adjacent
zone)
Then start from the front gunship point and move outwards across land and air zones by
order of distance.
o Will want to consider cumulative enemy ground threat and airaa threat from the
gunships to the current target.
o If any AirAA threat, then ignore the target.
o Otherwise, decide if the enemy MAA threat is insufficient to defend against the
gunships:
▪
Want an 8:1 threat ratio
▪
I may want to refine this in the future, e.g. one option is that if the enemy
has 3200+ groundAA threat, then I want it to have a 6:1 land to ground AA
threat ratio (e.g. experimental supported by MAA). However for now I’ll see
how it performs without this restriction as it may be in large numbers
gunships can still overwhelm SAMs/T3 MAA with the AI’s gunship spreading
logic.
Gunship positioning – This is mostly an exact copy of M27’s logic, except for the final section
for ordering gunships which makes use of the new M28 orders logic to do a similar thing.
2.24.6) Torpedo bombers
●
●
●
Only to be used defensively (as will be relying on navy to beat the enemy more generally)
At the start of the game, create a table of water zones that want to consider for torpedo
bombing for each air subteam – Get the naval factory build location for each pond, and then
consider this and any adjacent water zones.
Each cycle order the water zones by distance to the air rally point. Cycle through these one
by one.
o Consider if there is any enemy airaa in a path from the water zone to our rally point;
if there is, then don’t attack.
o Otherwise, attack provided either have 125% as much torpedo bomber threat as the
enemy groundAA threat, or we have at least 80% as much threat and have torpedo
bombers in the water zone (to avoid sending bombers in and then aborting as
they’re about toget in range). If we have at least 8k torpedo bomber threat then
attack anyway.
▪
Prioritise enemy units containing the antiair category and shield boats in a
group of enemies
▪
Then frigates
▪
Then other units
o
●
Assign torp bombers based on strike damage (using a copy of M27 logic for
determining strike damage for all units, but with additions for torpedo bombers
based on theblueprints to be more accurate – i.e. M27 would work based on threat
and manual adjustments, whereas now I will work based on actual strike damage); in
the case of cruisers and carriers assign 50% more in strike damage than is expected
to be needed.
Factor in unavailable torp bomber threat in decisions
71
●
●
●
Treat torp bomber as unavailable if it is within 90 of its target and its target is valid and on
water; similarly (for future use) treat bombers as unavailable if their target is on land
On death – remove assigned strike damage
When targeting torpedo bombers, the closest enemy units in a water zone (of the priority
category) should be targeted first (by the closest torpedo bombers).
2.24.7) Air scouts
Recording when have visual of a location
● Record omni coverage similar to how radar coverage is recorded
● If a land or water zone has omni coverage of at least 30 (40 for water zone) then treat as
having visual as part of the land/water zone cycling
● Cycle through every air scout and record its current land/water zone as having visual
o Similarly, if have land scouts for a land zone, or naval non-sub combat units for a
water zone, then treat as if have visual of that land/water zone.
Air scout management
● Scouting – although done by subteam, will largely reference team varaibles (main reason for
doing on subteam basis is so can ensure the logic takes place in a different tick to other air
unit management)
● At start of game record each enemy base start position as a high priority, zones with mexes
with medium priority, and other zones as low priority.
o want to scout high priority (i.e. enemy base) every 60s if have t3 air and at least 12
mass per tick, 120s otherwise
o locations with a mex: Every 120s if have T3 air/high mass, 200s otherwise
o any other location 3 times the ‘with a mex’ location value
● Increase value by the cube of the number of failed scout attempts (i.e. 1s for 1 failed
attempt, 8s for 2, 27s for 3, etc.)
● If have radar coverage of a location then x4 the time until want to scout it
● Create a shortlist of locations that are overdue being scouted
● If have available scouts:
o cycle through each air scout one by one, by order of distance to the scout owner’s air
subteam’s rally point
o find the nearest ‘overdue for scouting’ location to the air scout, and send it here
o record the target land/water zone against the scout; clear this value if scout is sent to
rally point
● On scout death - if it had a target land/water zone, then record against tram data of that
land/water zone the failed scout attempts, as +1, and reduce by 1 after 5m, and clear the
assigned land/water zone (so dont increase by more than 1)
2.24.8) Transports
The main differences to M27’s approach for transports are that I want to do on an island basis
(instead of plateau basis), incorporate land zone logic information (so less likely to drop dangerous
plateaus/islands), and make use of M28’s order tracking approach to hopefully end up with simpler
logic (although I suspect I may run into similar issues that M27 did with transports and have to add
complexities).
In contrast to other air logic, I also want to do have transports rely on air team wide information
rather than air subteam information, since ultimately I’ll be considering orders for each transport
72
individually (getting the nearest location) and they’ll consider dropping anywhere on the map, so I
want to avoid air subteams doubling up on transports. However I’ll still include it as part of my air
subteam logic so it’s easier to see at a glance what air logic is running in each tick.
Main dids are doing on island instead of plateau basis, incorporating land zone logic, and order
tracking approach:
Generate shortlist of locations that want to try to drop to: Exclude islands with any of the following:
●
●
●
●
●
enemy start position in it
Enemy combat threat of at least 175 in a land zone (indicating an enemy acu or a number of
tanks in the island)
Engineers already travelling there
Have had a failed transport attempt in the last 5 minutes (i.e. where a transport has died
while travelling there)
dont consider islands in the same plateau as the nearest friendly base that are within 190
travel distance of the nearest friendly base
Calc available and in use transports (in use if special micro active)
For available transports, cycle through each one and get preferred island to drop to – I’ll first sort the
transports by distance to the rally point, to try and reduce the extent to which the order transports
are considered changes causing their targets to change (with this resulting in a constant loop where
they end up never reaching a destination).
I also want logic to get the closest plateau and land zone/water zone to a unit position, and to revise
the previous approach taken to air units with no zone so they will instead be assigned to the nearest
zone.
●
●
●
●
●
If no plateau at the position (factoring in override), then get the segmentX+Z of the current
position
If not in override table of nearest valid segment, then start moving in a circle around this
until find a segmentX+Z that has a valid plateau and land or water zone.
Record this segment X/Z in an override table based on the base segment (i.e. this way we
avoid a really massive table size)
Then return the plateau and land/water zone of this override segment (return 0 for water
zone)
Previously had logic for enemy air units with no zone – this should become redundant after
updating for this
If no islands to drop to, then go to nearest rally point
For each transport:
●
●
●
●
If have engineers attached then get preferred island to drop to; then decide if want to wait
for more engineers or if have enough to drop
Preferred island to drop to – go through each island on the shortlist, and calculate the closest
land zone to the transport that has a safe path (i.e. no enemy airAA or groundAA)
If have enough engis for preferred island, then give order to drop (so are doing this every
cycle) if have enough engis, clearing any engis who have orders to load into the transport
If want more engis then move to the closest friendly base if we aren’t in a core land zone
73
●
If are in a core land zone, then record against that land zone that the transport is waiting for
engineers.
Then for engi logic if have any transports wanting engis, load as a high priority unless have enough
already assigned (will need to load one at a time with max of 5bp to assign)
●
●
Engi logic will need to refresh the table each time to check for dead units or transports that
no longer have this as their closest plateau/LZ
As part of this I have a new order for loading onto transport (really this is the same as
refuelling a unit but in case I want to apply different tracking later I want them separated
out)
When transport is told to unload, record its island target, otherwise clear this; then when a transport
dies record against the team data for this island to indicate it has made a failed transport attempt
If transport has no target plateaus and has engis attached then unload.
If a target island has engineeres or factories on it then don’t target it for a drop.
2.24.9) Factory production and upgrades
Building an air factory
Have a function that will decide what type of factory we want to build for a land zone, which takes
into account the following:
●
●
If in core base LZ, or in same island as nearest allied base, then:
o If cant path to enemy base by land or are on a 20km size map, then min tech 1, min
land factories 1, energy wanted 22 per tick
o If have less than 80% energy stored, then build a land factory
o Otherwise calculate the number of land and air factories based on the distance to
the nearest enemy base (closer distance = more land factories).
Otherwise, only want land factories (i.e. land facs to secure a plateau dropped by engineers)
Building units from an air factory
● If low power then only consider building engineers, and then only if have lots of mass
● Emergency builder – if enemy non-air scout air unit in current land zone, or air to ground
threat in adjacent land zone, then build AirAA.
● Otherwise, if enemy ground in current or adjacent land zone, then build gunship
● Otherwise, try to maintain the following in order of priority:
o AirAA until have at least 3 units
o Torpedo bombers if enemy has nearby naval threat to target (limit of 150% of the
nearby enemy naval threat)
o Gunship ifhave fewer than 3
o Air scout if don’t have any
o If have a need for torp bombers (i.e. for further away targets):
o
o
▪
If not far behind on air, build torp bombers
▪
If far behind on air, then only build torp bombers if our Torp bomber threat is
less than our AirAA threat.
If far behind on air, then want 3 times the mass in AirAA as gunship air to ground
If not far behind, but don’t have air control, then want 2:1 ratio
74
o
o
Otherwise, want 0.75 mass in AirAA vs gunship threat
Get more gunships if have enough AirAA based on the above ratios and either don’t
have low mass, or have fewer than 5 gunships, subject to a cap of 22 gunships.
Upgrading air factory
I wanted the following to be done as a high priority:
●
●
Enemy has nearby naval threat and we have no T2 air
Enemy has T3 air and we don’t
However, it looks like I’d already drafted code for this when I did my normal upgrade logic so I only
needed a minor tweak to link in with the new variable flag for if I have naval targets but no torpedo
bombers for anyone on the air subteam.
2.24.10)
●
●
●
●
●
●
●
Will need to update the varaibles refbFarBehindOnAir and refbHaveAirControl based on
AirAA threat – for now, if I have <75% of the enemy AirAA threat and it is more than a certain
level, then it should flag as being far behind on air control. If I have at least 20% more AirAA
threat than the enemy then it should flag as having air control.
Air staging – if there are units needing refuelling and no air staging, then air staging should
be built (to a max of 3 per core zone).
Added the front gunship as a priority unit for AirAA to protect and for the support air zone
logic.
Gunships should prioritise cruisers over other anti-air
Reduced the threat ratio required for gunships to attack when the enemy is closer (to reduce
the risk of them advancing, getting hit by AA, and retreating, without doing anything).
Gunships should ignore enemy AirAA if we have air control and the enemy AirAA threat isn’t
that high a proportion of the gunship threat, depending on how close the gunships are to the
support rally point.
When getting the air support location if we have air control enemy air should be ignored.
2.24.11)
●
Other air logic related points
Other misc changes
Added further redundancy for the default navigational pathing failures on larger maps, so
segments with no land or water zone will search for nearby valid pathing locations (in a 3x3
distance of the segment midpoint) based on if the position is land or water.
2.25) Naval logic
2.25.1) Engineer adjustments
My engineer logic was setup focused solely on land zones, and when I first added water zones I only
included basic logic required in order to build on underwater mexes and get reclaim in water zones. I
therefore need to replicate similar functionality for water zones for identifying locations to build. As
part of this I create a separate list of template blueprints for each building size that can be built on
water, and adjust the existing functions relating to this to handle water zones as well (rather than
doing copies of the functions).
As part of this I then reworked various parts of the engineer logic to in theory work with water zones
as well as land zones (e.g. tracking of queued buildings).
75
I also add in logic to track hydro locations by water zone, to help for logner term logic where I try and
get M28 functioning when it is given a water start point.
Naval factory assistance
● Have any idle engineers assist the naval factory
● If don’t have low mass, have more engineers assist the naval factory (+5 BP each time/cycle
over what is already assigned)
● Ensure a base level of BP assigned to assist the naval factory based on team’s mass income.
2.25.2) Factory build logic
Choice of blueprint to build:
●
Cycle through each water zone, and decide what to build based on each zone:
o If have units wanting shielding, build shield boat
o If have units wanting stealthing, build stealth boat
o If enemy antinavy threat, build antinavy unit
o If enemy surface combat threat, build surface combat (frigate/destroyer/battleship)
o If need AA, build AA
o If go through all WZs and have no units that want to build, then get a bombardment
unit
o If have low mass or low power then cap number of bombardment units to get
●
Have a naval factory upgrade if don’t have low mass and we have at least 70 average mass
income on the team (T1 to T2) or 140 (T2 to T3), separate to the centralised logic, and
providing there are no active upgrades in the waterzone, and only if there are no units to
build to attack an enemy WZ.
2.25.3) Naval unit management – bombardment mode
Although I’d setup water zones with details of adjacent land zones with this in mind (as an alternative
means of checking for nearby enemy units), in the end I was losing motivation to try and do
something fancy here so just copyed the M27 logic for bombardments.
2.25.4) Other naval changes
●
●
●
●
●
If a naval unit is thought to be in a land zone, and has no orders, then it should be told to
move to the nearest water zone, or move randomly otherwise.
Reduced the distance to stay away when shielding or stelathing a unit to 66% of the previous
distance for larger shields, to hopefully help make shield boats more likely to keep their
target under shield – most of the time they manage it now, although there are some cases
where the unit can outpace the shield.
Limited shield boat production to factor in power levels (both if have low power and gross
levels), and also added a cap on the number of shield boats (and stealth boats) to be owned
by a particular AI.
Made naval factories more likely to upgrade with very high mass income.
Added ground firing for units with no anti-naval attack but a good aoe attack, where there
are no surface targets, and the enemy unit is less than 1.3 underwater or is a submarine (i.e.
most battleships should ground-fire submarines).
76
●
●
●
●
●
●
If units with an antinavy attack can’t see the nearest enemy unit and that unit is submerged,
they should try to get closer.
Amphibious units that are underwater should have 35% of their mass cost treated as their
base threat when calculating the submersible threat of enemy units.
Shield boats shouldn’t be built if we have run out of high value targets for them
Adjusted logic for amphibious pathing in water to check they can path amphibiously from the
unit’s actual position (previously it used the water zone midpoint, which caused issues for
water zones where the midpoint couldn’t be reached by amphibious units).
Increased the priority on assisting the naval factory water zone to be equivalent to that of a
water zone with unclaimed mexes.
Adjusted the early game build order on naval maps to build fewer factories, and make it a
higher priority for engineers to expand to islands, as well as adding an eco condition to
building the initial naval factory.
Sonar
● Added logic to track sonar coverage of water zones, similar to how radar coverage of land
and water zones is tracked.
● Build sonar to increase sonar coverage: if no low mass or power
o If average team mass income is 6 per tick, we have at least 125 E per tick, our highest
tech level is T2, and there is no enemy threat in the core WZ or adjacent to it, then
build T2 sonar in the core WZ
o If we have 12 per tick average mass, and at least 250 E per tick, then for each
non-core water zone, check if it is ‘safe’ (no enemies in WZ or adjacent), and if so if
its sonar coverage is less than 115 build T2 sonar
o Don’t build if AiX intel
o Added logic to build t1 sonar where there are enemies in an adjacent WZ and we
don’t have really high mass income.
Frigate scouting:
● If have T3 navy fac, T3 naval units, mass income of at least 25 per tick, and no enemies
adjacent to a core WZ, then set a flag that frigates are to be treated as mobile land scouts
instead of combat units, and use them accordingly
● If this flag is set, then have factory build frigates if we have flagged as needing scouts in a
location.
● When implementing this I realised there was a flaw in my land based scout logic (and hence
frigate based logic) – it was tracking scouts assigned to travel to a location, but wasn’t
updating these lists if the unit died, meaning some locations would never be scouted.
2.25.5) Other non-naval related changes
●
●
●
If we have spare engineers for a land or water zone and are upgrading a unit in that zone,
then all the spare engineers should assist the upgrade.
Added new high priority engineer builder for air factories so we have a minimum number of
engineers before getting other units
There was a bug in the logic for calculating air travel paths, due to a bug with recording the
min and max segment ranges for a land zone – the variables for the max segment X and Z
used the same definition, causing strange behaviour where ASFs would suicide into enemy
SAMs (thinking they weren’t nearby).
77
●
●
●
●
●
●
I also change the AirAA logic to assign 3x the threat to AirAA targets, and if there are any
spare AirAA units to intercept then I increase this to 6x the threat.
There was an issue with air logic where if an enemy intie came into a land zone (e.g. the
enemy flies an intie over M28’s base) and the unit survives, then M28 would spend the
whole game thinking there was an air unit there, messing up it’s logic. I’ve therefore tried to
approximate the ability of a human to assess where the air is, by updating the positions of air
units in a zone every 30s, or more often if allied units are in that zone. The difference this
made was dramatic – I was prompted to do this by an M27 vs M28 replay on an island based
map where M28 was behind the entire game initially about 2:1 and increasing to 3:1 until
M28 lost navy and its main base. After the change M28 was able to take the lead in eco for a
bit via use of its gunships, and able to repel M27’s initial naval attack with its torp bombers
and gunships. However, M27’s navy production was superior to M28’s, and a large amount
of shield boats coupled with cruisers was able to repel large amounts of torpedo bombers
without loss.
Air units should move near an air staging followed by a queued order to dock at the air
staging if far away due to an issue I came across once where gunships were given a refuel
order and it was somehow cancelled leading to them remaining idle far from base. This way
if the issue happens (and there is no air staging available) the gunships should at least be in a
safer location.
Increased resources wanted to start on an ACU upgrade in certain scenarios including where
we can’t path to the enemy by land, and where we already have ACU upgrades.
Reduced the number of land factories to get on non-core islands and the likelihood of
upgrading the land factories.
Where the enemy cant be pathed to by land then engineers should be built in greater
numbers initially.
2.26) Novax, Nukes, and TML
Before M28 releases I want it to have slightly more variety than its current late game approach of
spamming land experimentals, especially for UEF since fatboy is really bad on naval maps even if it
makes it to the enemy land base.
Since the SML logic and TML logic in M27 shares some parts, I’ll also add in TML logic at the same
time.
2.26.1) Novax
Production
● UEF – if cant path to enemy base by land, then check if enemy has significant land combat
threat (a least 2k + 15k for each fatboy we already have) in total on our current island; if not,
then only build fatboys up to the number of enemy land experimentals, with a max of 2,
otherwise build a novax.
● If can path to enemy with land, then order of fatboys wanted:
o 1 Fatboy
o 1 Novax
o 1 Fatboy + 1 per every enemy land experimental, to a max of 2 fatboys + 1 per active
M28 team member, up to 4.
o Remaining experimentals: Novax, unless have >=50% mass stored and >=90 team
mass income per tick, in which case build mavor (for now I’ll build a novax though as
I don’t have the logic in place to manage a mavor
78
To facilitate this, I’ll generate a table of the engineers by factions to pass to the function that picks
the experimental category ot build, so I know more reliably if I have a UEF engineer (M27 would
assume the faction based on the aiBrain in question, but M28’s decentralised approach means this
could go wrong, and at some point in the future I want to try and have a system for one faction
requesting the engineer from another faction).
For the novax usage I want to copy thet part of the logic that considers targeting vulnerable enemy
shields and ACUs, and then make use of the land zone logic to search for other targets:
●
●
●
●
●
●
●
Generate a list of novax on the air subteam.
Hook on killed so if novax kills unit it immediately gets a new target
Copy M27 code with the following adjustments:
o When searching for nearby enemy units, search in the current and adjacent land and
water zones, so we can make use of ‘memory’ of a unit to consider targets.
o If the novax is in a zone, refresh the position of all enemy units in that zone given the
novax provides intel.
Search current and adjacent land and water zones for all non underwater enemy units, and
calculate the damage that could deal factoring in distance and if unit is shielded, with
increased value for mexes and volatile units
Weight the values by unit category similar to M27 (e.g. mexes are treated as being worth
more than their mass cost, similarly for volatile units)
Check if units are shielded by creating a list of mobile and fixed shields in these zones and
seeing if are within range of any of these units.
o However after initially drafting this I realised there’d be a flaw with this approach –if
a unit was on the edge of an adjacent land zone, and was covered by a shield in a
further away land zone (i.e. not adjacent), that shield wouldn’t be picked up initially,
the novax would think the unit was unshielded and move ot target it, then realise it’s
mistake and change targets once it got into the same land zone, leading to a
potential infinite loop where it did nothing.
o I’ll therefore need to create a shortlist of shields in zones adjacent to adjacent zones,
and make sure I only include each shield once, and also only include the shield if it is
close enough to the edge of its zone that the shield will go over. However after
testing before this change (see later comment) I ended up scrapping the approach of
cycling through adjacent zones due to it’s unreliable nature.
I only want to include such targets if they meet a base value threshold – i.e. I don’t want my
novax wasting time targeting high health low mass units. I’ll do this by treating the novax as
already having ‘found’ a target with a certain value (iBestTargetValue), so future entries will
only be considered if their value exceeds this.
o In terms of what this value should be, the novax DPS I think is about 243 (ignoring
that it’s much higher while firing and then 0 while reloading). It costs 36k mass,
while in comparison getting a T3 mass fab and some power costs about 5k mass for
16 income per second.
o Therefore, the opportunity cost of a novax is approximately 115 mass per second.
▪
I therefore ideally don’t want to go much below this when picking a target.
▪
Practically, I would expect the novax targeting a monkeylord to be ok, but
targeting a ythotha to be inefficient
79
▪
Checking a few units (mainly using UEF values since they typically have
higher health), targeting a frigate or pillar constantly would deal 32 mass/s,
68 for a destroyer, 88 for T3 MAA, 96 for a Ythotha, 108 for a monkeylord,
194 for a cruiser. I’ll therefore use a value of 105.
▪
If I have at least 4 T3 gunship equivalent of air to ground threat on my air
subteam and aren’t far behind on air then I want to increase the value of
focusing down AA by 50% (so T3 MAA gets included) to give gunships more
of a chance to attack, otherwise to leave it at default.
▪
Engineers only provide a value of 79 (T2 engineer) to 95 (T3 engineer).
However, killing them potentially denies my opponent valuable reclaim and
the ability to build shields, so I want to still consider targeting them. I’ll
therefore increase the value of an engineer by 35% so they should also be
targeted.
▪
If there are no suitable long range targets, then I want to instead use the
best value target near the novax subject to half the cap as before.
However after testing I realised this approach was too inconsistent, due to the differences in sizes of
land and water zones, so I switched to using getunitsaroundpoint.
2.26.2) Nuke launcher
Initially I wondered if I could make use of M28’s zone logic to keep track of blocking SMD, e.g.:
●
●
●
●
●
●
Record against each land and water zone any potentially blocking SMD for each M28 owned
nuke, so can then decide whether or not to target it
Whenever an SMD is built or killed, this table would be updated.
Would also want to be based on land zones, so could make good decisions about whether to
build a nuke in the first place
Check approach re TML and TMD as may be something similar wanted.
Then for targeting would consider the value of a land zone coupled with combat and AA
threat when deciding whether to target it (for WZ would just be combat and AA threat)
And then would do a more precise calculation at both the midpoint of the zone and any T3
units in the zone to estimate the best target for that zone.
An alternative approach to this could have been:
●
●
●
●
tTeamData: Generate table of core bases, whenever core base is set to true, and it is a land
zone, add to this table
Whenever an SMD is sent to the pond/zone function for recording, add to table of
enemySMD (may already do this), and run a function that cycles through every core base and
then cycles through every land and water zone and considers if the SMD can block it; have it
as a function that is sent the SMD unit in question (as will want to use same function below
when a new core base is added and smd already exist)
Whenever add a zone to this table for a team, check the team-wide table of enemy SMD; if
it’s not empty, cycle through each entry, and then cycle through every land and water zone
For every land and water zone, check if a missile fired from the midpoint of the core zone to
the midpoint of the land/water zone in question would be blocked by the SMD. If it is, then
80
●
●
●
●
record the SMD in the core base’s land zone of blocking SMD for that zone (i.e. order things
by plateau (0 for WZ) and zone ref; and then return a table of any blocking SMD units
Whenever recording the blocking SMD in this way, against the SMD record in a similar table
(i.e. plateau or 0; and WZ/LZ ref as the initial keys) the plateau or zero and core wz/lz ref that
the SMD likely blocks a nuke from
Whenever an SMD is destroyed, go through any zones where it was recorded as a blocking
SMD, and remove it
When a nuke is built by an M28AI, then redo the zone check (unless there’s already a nuke in
the zone), but instead of using the zone midpoint will be using the nuke launcher position.
Will also record against the teamdata of that zone the nuke that the nuke check is based on.
When a nuke is destroyed, and it was owned by an M28AI, then re-do the zone check based
on the midpoint (unless there’s another still living nuke in the zone in which case revert to
that nuke’s position), and remove the nuke from the nukes in the zone.
However, after sketching out the work involved for this, I decided I’d just copy M27’s approach, as the
main benefit of the above is just when it comes to the decision of what nuke to build, and in practice
I don’t think it would be as good as restricting targets based on if the midpoint of the zone they’re in
can be hit will lead to a less accurate nuke. Some of the most entertaining parts of M27 replays have
been when it manages to edge-nuke a human, which the zone based approach would significantly
reduce the likelihood of (even if it would be far quicker to run).
I’ll therefore consider doing something along the following lines:
●
●
Keep a table of enemy SMD (so if they fire we know where they are even if they’re not
revealed to avoid repeatedly nuking somewhere covered by SMD); when nuke is loaded then
cycle through enemy units, considering 50 units each tick, and fire on the best target after
considering 500 units, or finding a really good target, or running out of units to consider
i.e. probably just a copy of M27’s logic. However, the main differences to M27’s approach
will be:
o Not cancelling construction of SML or reclaiming it
o Checking for new targets every 10s if it failed to find a previous target
o Increasing the threshold for firing a nuke to 14k (from 12k)
SML construction
I want to track on a team basis for each core land zone the other land zones it could hit with a nuke,
and update this list whenever an enemy SMD is built or destroyed, a bit like what is done with TML
but for zones instead of TML units, so I can build a nuke if there are any high value enemy zones
considered vulnerable to a nuke.
●
●
●
●
When deciding on the experimental category to build, engieners should call a function that
gets the highest land zone value that they are expected to be able to nuke from there
If I haven’t calculated this before, or an smd has been built or died since last calculated, then
I’ll do a refresh of this logic for the core lz teamdata that are dealing with, with a flag for if
smd have been added or died; if smd have died or have no entries then I’ll do a full refresh,
otherwise I’ll only consider removing existing entries
Ensure enemy smd is tracked when added to the pond/zone assignment function for the first
time
When cycling through enemy land and water zones record enemy structure mass value
(ignoring health)
81
●
●
●
●
●
●
Cycle through every other land and water zone and for each midpoint check if it is in range of
any of the smds known about for that team; if it isnt then record as an unprotected zone
Once this function has run: in the bp function cycle through each recorded entry and get rhe
highest value calcd as 100% of enemy structure mass value + 25% of enemy ground threat
value
If highest entry exceeds 20k then build a nuke launcher
However a land experimental will be built as a higher priority if we have none and can path
to the enemy base with land.
Only one nuke launcher should be built per zone.
Seraphim – added logic to build yolona – e.g. any map where cant path to enemy base by
land or already have 3 land experiemntals, and have at least 80 mass income per tick.
2.26.3) TML
TML targeting and usage
● I expanded the previous tracking approach so I can use it to identify targets as well – i.e. I’ll
use the same trackers that highlight units that need TMD (for purposes of building TMD) in
order to select targets for the TML where M28 owns the TML.
● I also copied the M27 logic for finding TML targets with minimal changes.
However, I ran into issues getting this to work and reviewing the code realised there were flaws with
it due to the complexity of scenarios it would need to capture to reliably track potential targets.
After a number of aborted rework attempts, I decided to take a shortcut for my sanity and have all AI
use a single shared tracking system, meaning M28 TML may get a slight unfair advantage re target
selection but it should hopefully avoid performance concerns with recalculating everything on the fly
whenever deciding to fire a missile, and makes things easier to follow.
In summary, the code should do the following:
●
Whenever a TML, TMD, or Category that is vulnerable to a TML is built, check for all nearby
TML/TMD/Vulnerable category units, and update the TML related tracking variables on all of
them, to record the following:
o Units the TMD is protecting from enemy TML
o Units in range of TML that have no TMD protection
o Units in range of TML that have TMD protection
These are tracked against individual units.
When a TML or TMD dies, then the tracking is updated.
However, I wont update when a ‘vulnerable category’ unit dies, and instead just have a check for if
units are valid as part of the logic.
Decision on when to build TML
Ideally I want the engineer builders to have a low CPU way of accurately identifying potential TML
targets if they were to build a TML in the land zone. However after the pain of getting the MTL logic
to work (which I was expecting to not be that difficult) I’ll go with a simpler to code approach. The
approach I’ll use is as follows:
●
For now, only consider building TML in core LZs, although in theory it should be possible to
expand to other zones if I decide to later on.
82
●
●
●
Only activate if we are at T2+, the enemy has at least T2 units, and we have no TML in the
land zone
Whenever a ‘vulnerable to TML’ category unit is sent to be recorded for a team, and it is an
enemy to the team, and the team has M28AI in it, record it in a table for that land zone if it is
a structure against the M28AI team data
o Similarly record whenever a TMD is built and recorded against a LZ
o Refresh both of these lists when updating the engineer logic (rather than whenever a
unit dies) to remove dead units
Cycle through every land and water zone, and consider any with vulnerable to TML enemy
units recorded, but stop as soon as reach a zone that contains TMD (i.e. won’t bother
considering direction, will just stop searching for all targets once reach a zone with TMD).
2.27) T2 PD, Ahwassa and T3 arti
The following reflects relatively ‘core’ features I want M28 to be capable of before it releases.
Although I originally planned to not release with T3 arti logic, on reflection Aeon and Cybran need
something they can build for maps where land units cant reach the enemy base so they are capable
of winning a late-game map (in theory).
2.27.1) Emergency PD to stop enemy Guncom
●
●
●
For now I’ll hold off on M27’s complex logic on PD placement and firebases, and instead
focus on just getting T2 land/air and T2 PD if the enemy’s ACU gets near.
Core bases will call a function that cycles through a list of identified enemy ACUs, and totals
up the threat of all ACUs who are close to the zone.
Based on this threat, either a priority upgrade should be called (if the zone lacks T2), or point
defence should be built.
2.27.2) Ahwassa
Usage
For now I’ll use a very simplified approach that I expect will need improving later on
●
●
●
●
If lack air control then run from enemy AirAA threat of more than 400, otherwise only run
from AirAA threat of more than 4k
Determine pathing to all other zones from the zone the Ahwassa is in (similar to what is done
for gunships
Treat as unavailable if current target is within 100 of its position or special micro is active.
Cycle through each other zone from the ahwassa position, and consider enemy structure and
combat threat, considering any zones where the threat is at least 1.5k
o Consider each T2+ unit in the zone plus T1 navy, and calculate the total mass value of
enemy surface units.
o Target the closest unit to the bomber
o Calculate the best AOE target for targeting this unit if it is a structure
o I’ll copy over M27 logic relating ot hover-bombing, but instead of hover-bombing I’ll
have the Ahwassa fire a shot and then immediately hover-turn and retreat, before
turning back again – this reduces its damage output, but hopefully increases its
survivability (e.g. if the enemy attacks it with asfs it's more l’kely to be retreating to a
friendly location).
83
o
●
I also decide after testing to target the unit where a shot deals the most damage,
instead of the unti closest to the bomber (improving its damage output at the cost of
survivability).
Make sure available bomber logic factors in ground attack orders similar to unit attack
orders.
Construction
If have air control, or have as many land experimentals as the enemy and aren’t far behind on air, or
cant path to the enemy base by amphibious, then build an ahwassa.
2.27.3) T3 and experimental arti
I wasn’t originally planning on having T3 arti for the release version of m28 but on reflection it felt
too big a gap to have UEF having a late game option (novax), Sera getting the experimental bomber,
but Aeon and Cybran stuck with land only.
Usage
For now I’ll go with the following simplified approach to calculating T3 arti targets:
●
●
●
●
●
Calculate pathing from the zone using the function used for air pathing
If the arti doesn’t have potential zone targets recorded against it, then cycle through the
zone pathing options, excluding any zones whose distance is inside the arti’s minimum range,
or outside the maximum range.
Then cycle through each of these zones, and identify the zone with the most ‘value’ to
attack, and the second best zone (as when go through the detail the best zone may not
actually be the best)
Adjust the zone value based on a ‘T3 arti likely failure count’ value – whenever a unit dies in
the zone that is a fixed shield whose fractioncomplete is 1, or a structure unit with a mass
cost of at least 1k factoring in % complete, then reset this count for the zone. Whenever a
shot is fired then get the zone of the target and increase the count by 1.
o The value of a zone is 100% if the shot count is less than 10, and then changes to be
reduced by 20% + 2% for every failed shot, to a maximum of a 96% reduction.
Select the zone with the greatest threat value applying the following calculation:
o Shot value % (per above) * (structure mass cost + mobile unit cost * 0.2)
▪
Add a further +80% of mobile unit cost if the groundAA is at least 4k, and the
zone is within 300 straight line of the nearest friendly base, and the enemy
has at least as many land expermentals as our team
▪
Alternatively, apply a further +80% of mobile unit cost if the enemy mobile
threat with a range of 80+ exceeds 4k and the zone is within 300 straight line
of the nearest friendly base.
▪
Apply a +100% uplift to the structure mass cost if it is within 200 of a friendly
base and includes indirect fire units with a range exceeding 100 and a threat
exceeding 6k (i.e. T2 arti)
▪
●
The value will be reduced slightly if the target is in a far away angle (up to 40
degrees if it’s in the opposite direction to the direction the Arti is facing)
In the end I decided to select the best 2 zones (since the calculation used above isnt that
precise), and then select the best target within these zones (focusing just on T3+ units if
84
there are any), before then getting the best point on the ground to attack out of these
targets.
Construction
● Build if we can’t path to the enemy base (or for UEF if we would build a novax but already
have 3 novax and have more than 1 more novax than T3 arti (i.e. want 1 T3 arti with 3 novax,
2 T3 arti with 4 novax, etc.).
o If team mass income exceeds 80 per tick then instead build a mavor or scathis or
rapid fire arti
o Also build these experimental arti if the nearest enemy base is more than 740 from
the zone.
2.27.4) Late game eco and other adjustments
Engineer reassignment
● When identifying available engineers, if none are available of the required tech level, check
all assigned engineers with a different action to see if they are available, and if so then free
up the engineer with the highest numerical priority (i.e. the lowest priority action, since
1=most urgent).
● When an engineer is given an action, record its priority (so the above can be checked).
● Ignore engineers who are actively reclaiming or have a priority 1 action.
● Engineers who have nearby enemies should be treated as having a priority 1 action.
Other misc changes
● Added a relatively high priority second power builder to help with late game high mass
situations. As part of this I’ll record when an air factory didn’t build an air unit due to lack of
power, to ensure that more than this power is produced.
● Also added a similarly high priority 3rd power builder for very high energy and mass
scenarios.
● Core land zones should consider a pre-emptive SMD once the total value of units in the LZ
reaches 20k (or 30k if have low mass)
● Improved the emergency AA engineer builder to increase the amount of AA built
● T3 pgens shouldn’t be built adjacent to T1 air factories
● Added logic to ctrl-K T1 air factories and T1-2 land factories when low on mass and at T3.
● Capped number of land factories to 2 if we cant path to the enemy with amphibious units.
● Added a spare engineer action to reclaim in the area if there’s almost any mass.
● Engineers shouldn’t try reclaiming another wreck on completing their current reclaim order
if the AI has lots of mass stored.
● If we reach 90% mass stored, then all reclaiming area and friendly unit engineers should have
their actions cleared (with this check being spread out with 30 engineers considered per tick
to avoid a significant performance issue)
● Cybran ASF (and other stealth units) should default to having stealth enabled.
● Improved TML targeting so if multiple TMLs are considering the same target and it dies to 1
missile, they should choose different targets; similarly a TML shouldn’t firem ore than once
every 20s at a target that can be 1-shot.
● The mass and energy stall logic should no longer unpause missile launchers with 2+ missiles
loaded.
85
●
●
●
●
Fixed an issue with units being paused when not complete which would lead to them being
permanently paused when construction completed.
T2+ Engineers dropped on a plateau should build a T1 land factory instead of a T2/T3 factory
as their first factory.
The first units built by a land factory on a plateau should be a combat unit and then a land
scout, before the normal logic resumes.
Transports with <25% fuel and no engineers should be ctrl-K’d.
2.28) Compatibility changes and performance check
2.28.1) Performance comparison to M27
All core logic other than compatibility type logic (for more niche game and map settings) are now in
place, so I want to revisit how M28 performs on Africa compared to M27.
The detailed profiling (which lists out every function) gave the following results for tick 6001:
info: ProfilerOutput: Total time taken to get to 6001= 67.482643127441; Total time of any freezes =
0.39152234792709; Longest tick time=0.24288177490234; tick ref = 3921 to 3922
By contrast, M27’s results (from v67) were:
info: ProfilerOutput: Total time taken to get to 6001= 167.24580383301; Total time of any freezes =
20.756183624268; Longest tick time=0.45658874511719; tick ref = 4690 to 4691
Overall this is far beyond what I was hoping for – not only is M28 about 2.5 times faster than M27,
but more importantly it hardly ever freezes with a total time of freezes of just 0.39s to M27’s 20.8
(and I’m not sure the extent to which freezes could be caused by the logging I’ve added).
2.28.2) Unit cap
This was a rare case where I was able to copy the M27 logic with minimal changes needed for it to
work to some extent. The main refinements I then made to refine M27’s approach were:
●
●
●
●
●
●
●
Recording the categories destroyed to get below the unit cap, and not building more units of
this category
Adding an exception to prevent building of T2 units so T2 engineers could be built if there
was a bit of leeway in the unit cap and there were too few T3 engineers
Only calling the function to update when a unit is created or constructed, and only when that
unit’s creation means we may be close to the unit cap.
Having more units be destroyed at low unit caps to give a margin of error
Engineers shouldn’t build anything if their current action is a low tech unit and we are about
to run out of units.
After running, the same function should be run again in 30s
Don’t build more factories if we already have 2 and are close to reaching the unit cap.
Testing on Setons in the rear slot with a 125 unit cap, this resulted in the AI mostly having T3 mexes,
T3 pgens, and building a couple of GC. It struggled to have the GC survive against enemy T2
bombers, but since it’s an unrealistic scenario anyway I’m happy with its general approach.
2.28.3) Maps with smaller playable areas
To check M28 works ok, I tried running games on Adaptive Moon and DualGap Adaptive, as well as
winter dual, to confirm there were no issues.
86
One issue that playing on dual gap highlighted is that the FAF navmesh generator will include areas
outside the playable area when determining pathability – I’ve flagged to Jip in case this can be
resolved, but it’d be too much effort to fix for M28 for what I expect to be a niche scenario (in short,
if playing dual gap with M28 on only the top island, not the bottom, then it wont send transports to
try and claim the bottom island and also wont send engineers to try and claim it amphibiously as a
high priority. A fix if it’s not possible for Jip to change the way the navmesh generates paths would
be for the transport drop logic to check the path generated to get from A to B and see if it passes
outside the playable area, but this would only be fixing one symptom of a wider problem, and I
expect most times dual gap is played there will be players in both the top and bottom start areas on
the same team.
2.28.4) Water starts – Dreadnought Chasm (all water)
This is a map (possibly the only one) that is entirely water. As far as I know no AI supports such
maps, so I want to make M28 support it since that should make it more effective on maps where it
starts in a ‘navy’ slot (even if there is nearby land).
The aim isn’t for the AI to be good (e.g. long term there doesn’t seem to be any answer to nuke subs
beyond killing the sub before it gets into range), but to at least provide a basic level of challenge (that
would be greater with higher AiX modifiers).
Initial plan
● Fix any error messages that arise when running the map
● If the start position is in water and there are no adjacent land zones, then set the start
position as the core base, and set the flag that we have a naval start.
● Prioritise building any unbuilt hydro if we have low power
● Prioritise building mexes
● Include a (basic) anti-air engineer builder if the enemy has an air to ground threat in the WZ
o I expand this to work for other (non-water start based) positions, including a lower
priority AA builder if we mass available and are at T3 – i.e. effectively a SAM creep
logic for water zones
● Update the naval factory builder to build engineers if it is in a core base naval zone, with a
high priority builder (if we have fewer than 3 engineers of the factory brain’s highest tech
level), and also a high priority builder if we have high mass and aren’t power stalling and
want more BP for the water zone.
● When doing this I realised the ACU logic all assumed the ACU was always on land, so I
updated it to make it easier to give water orders in the future (if e.g. I decide to go with
cybran torpedo upgrade combat logic).
● Get Cybran ACU to get torpedo upgrade if its current WZ is an underwater start position
One issue this highlighted was that the water zone creation approach (which was deliberately simple)
doesn’t work if it results in the starting mexes and hydros being treated as being in a different water
zone to the ACU (which his what happened on this map).
My solution is to first create water zones for any start positions that are underwater, and treat a large
area around these as being part of the same water zone.
I also make some improvements to the underlying logic – using a ‘hollow box’ approach to assigning
segments to a water zone in increasing box sizes (previously I was using filled boxes, meaning there
was a lot of duplication of logic being run for the ‘filled’ part of the box), and fixing a bug where I was
using non-rounded segment references.
87
Another highlighted issue is that the naval factory build location isn’t taking into account if we have a
water zone start point, resulting in the ACU walking ages before it can build a naval factory. For this
I’ll just use the start position as the naval factory desired build location, and then have the logic for
generating build locations run loads of times for ‘core base’ water zones at the start of the game, to
increase the likelihood there is a build location identified near the ACU when it tries to build.
After fixing another bug highlighted (where mexes were having a land zone assigned even when
underwater) this revealed an unexpected infinite loop where the logic for valuing land zones was
only waiting between individual land zones, but if no land zones existed it would never wait, meaning
the while loop would freeze the game.
Further adjustments made include:
● Lower priority hydro builder
● Building more engineers initially before building navy.
● Build more naval factories
● Increased gross power requirement to build shield boats if in an underwater start position
● Adjusted the water zone creation so instead of setting the area around individual start
positions to a water zone, all individual water start positions will be cycled through
simultaneously with the area around them set (i.e. meaning 2 adjacent start positions should
be similar in size)
● Include RAS upgrade for ACU
● Lowered power eco threshold for getting RAS upgrade on ACU if ACU is in an underwater
start position WZ
● Allowed multiple naval factories to upgrade simultaneously
● A strange issue this highlighted was a flaw in my approach of predefined buildable locations
– somewhere can show as buildable for a UEF naval factory, but not be buildable for a
seraphim naval factory of the same size. I suspect the reasin is the SkirtOffset values in the
blueprints are different. As a basic workaround for just this case I’ll have a table of blueprints
for a size where I need to consider other blueprints of the same size, but a better solution
might be to figure out if the offsetXZ values mean the build location should be adjusted to
get a more generic location that is buildable. To help reduce the likelihood of this issue
occurring for other blueprints, I’ll also add an extra check when picking a preferred location
to exclude those which aren’t buildable
● Another issue highlighted was that my upgrade logic for ACUs needed to factor in upgrades
that the ACU has which have pre-requisites so it doesn’t try and move from the better
version to the earlier version (e.g. advanced RAS to RAS).
2.28.5) Water starts – Italy
Italy provides a more common alternative to the extreme ‘water only’ scenario of Dreadnought
Chasm, where some start positions have water, and some have land.
My plan for now is to have water start positions work the same as on Dreadnought Chasm, but
non-water zone start positions to act the same as on a conventional map.
Only one change was required for this map, which was fixing a bug caused by the FAF navmesh not
being 100% accurate, where somewhere could be pathable by land but not by hover/amphibious –
now if the amphibious/hover pathing isn’t valid it should try a backup option to reduce the risk of
this occurring.
2.28.6) Survival maps and unit restrictions
88
In theory M28’s approach should make it work on survival maps but there’s a high likelihood some
logic won’t work as expected since it was all written on the assumption of a nearby enemy.
Therefore the main aim will be for it to function to a basic extent on survival type maps.
Fortunately both cases of error messages that arose I’d already included some basic
redundancy/backup logic – in the case of there being no enemy brain, it would use the furthest away
alive brain regardless of if it was an enemy or not, and in the case of unit restrictions it would just
give a warning to alert me that it couldn’t build an air factory (which it looks like the map
auto-enables as a unit restriction) and then try and proceed with other engineer logic. The downside
of this is that land factories wouldn’t get built in as large numbers since it will be trying to build air
factories.
I figured it’d be a good time to add some basic logic for unit restrictions at this time.
Since disabling air might be a moderately common unit restriction, I want to add a basic check so
that land factories still get built:
●
●
●
At the start of the game, flag if unit restrictions are present
If unit restrictions are present and we haven’t specified if air factories can be built, then set
against each team a flag for if air factories are buildable.
If air factories aren’t buildable, then in the function to flag if an air factory should be built
instead of a land factory, always return false.
Trying on a Thermopylae survival map with all random M28 AiX 1.5 ACUs, they lasted until 14m30
before changing the land factory approach. For comparison, M27 AiX 1.5 lasted until 16m30.
However, M28 had fewer error messages than M27 which was something.
AI Wave survival mod
The following changes were made to improve compatibility with this mod:
●
●
●
●
Adding placeholder logic for bombers so they will attack the nearest enemy
Adding placeholder logic for mercies so they will attack the nearest T2+ building or ACU
Having Czar be treated the same as a gunship (so it will at least be used, even if poorly) in
case the mod spawns czars at some point
Fixed an issue where if there were dangerous enemy units at the start of the game the ACU
would stop building the land factory and be stuck ‘running’ to the rally point.
2.28.7) Niche mods – flying engineers, infantry factories
These are based on examples I recall where M27 failed:
●
●
Flying engineers – for this I copied M27’s solution (change the category of units treated as
engineers), which mostly works although I also decided to hide one of the error messages
that appears if air units are sent to the land zone unit manager where the unit is an engineer.
Infantry factories – this was a mod which allowed the construction of factories that couldn’t
build engineers. M27’s solution which I planned to copy was to require the first factory built
by an ACU to be capable of building engineers. However, the mod no longer works so
without a way of easily testing this I’ve left it for now and can patch it in later if anyone raises
a similar issue.
2.28.8) NoRush
The main things I want M28 to do with no-rush are:
89
●
●
●
●
●
●
●
Early build order – recognise if the hydro it wants to build is outside the norush radius, and (if
so) build normal PGens instead (M27 for a while suffered an issue where it wouldn’t build
either)
Factory production – ignore production of any non-factory or non-upgrade unit if no-rush has
more than 30s remaining on the timer
Upgrading – treat any location as safe for upgrading if there is at least 60s remaining on the
timer
Land and water zones – if the midpoint is outside the norush radius and it isn’t a core base,
don’t flag as needing any engineers if there is more than 30s remaining on the timer.
If in norush mode then only want 1 land fac before air fac, and don’t want more than 1 of
each
Priority mex upgrades should be sought if norush mode is active
ACU should only build 1 land factory and then build 1 air (instead of building 2 land)
2.28.9) Crazy rush
This is a special map where every time a mex is build, every adjacent slot becomes capable of
supporting a mex.
Including some logs it looks like the SimInit function CreateResourceDeposit triggers when this is
done, which means in theory I can have my AI recognise these new points. However there will be
some complications given it records mex locations at the start of the game and wasn’t intended to
update these during the game.
Fortunately with the way M28 was setup I didn’t need to make a huge amount of changes for
recording land zones and it seems to work (although clearly suboptimally since most logic aims to
cap T1 mexes as soon as possible leading to constantly being short on power).
I also make the following changes to help it in this scenario since I figure they may also be of use
more generally:
●
●
Cap the BP assigned to build mexes if we are overflowing mass and have low power
If team has at least 8 gross mass/tick and 50 gross E per tick and is at T1 then prioritise T2
land factory upgrade even if have low power
2.28.10)
●
●
●
Accidently running the game with M28 enabled
I want to make sure that M28 doesn’t run any code if it’s enabled by mistake.
Most of the code won’t run due to being triggered by the creation of an M28AI. However,
there were some error messages relating to events/callbacks that M28 was triggering which
weren’t checking if any M28AI were in the game first.
Adding in these checks to the remaining callbacks means these error messages no longer
appear if M28 mod is active with no M28AI present in the game.
2.28.11)
Unit mods and other popular mods– Nomads, Blackops, BrewLAN
In theory M28 should work with unit mods, but to make sure I want to run games with M28 vs M28
on a map featuring land air and water to check for error messages.
Nomads
I’d hoped to get successful games working with Nomads, but the game crash. At first I thought it was
an issue with M28 but Hdt80bro highlighted the debug.traceback() function which allowed me to
90
confirm it was the nomads projectiles that were causing the crash, and I was able to replicate the
issue as a player with no AI mods (i.e. ground-firing certain Nomads units causes the game to crash
as it creates projectils on a constant loop).
Fortunately it turns out the issue was fixed by the maintainer and I was just using an older version
though. While there are still errors relating to projectiles with the new version, they appear
unrelated to the AI.
Also of encouragement was that in the test UEF vs Nomads game (at AiX 2.0 to speed things up)
nomads was able to get a significant eco lead on UEF (whereas for M27 generally it plays worse as
nomads than as other factions).
Mods that change ACU upgrades
Testing with a blacktops ACU mod which rewrites all the upgrade options, both UEF and Cybran ACUs
got a gun upgrade as expected.
BrewLAN
Testing a game with this I could see some of the new units (e.g. experimental point defence, other
faction T3 mobile shield generators), and no error messages occurred (that related to the AI).
2.28.12)
Playing as a human with M28 as a teammate
Adding in logic for civilians (see below) highlighted an issue where M28 will take control of all allied
units not just its own. I therefore made sure that if I had M28 as a teammate and build land, air and
naval units, that none of htem would be taen control of (after putting through the fix to stop civilian
units receiving orders).
2.29) Misc changes
Revealing and capturing civilians
● Checking if an engineer of ACU is ‘available’ – I’ve expanded logic to cover if the unit state is
capturing based on similar changes made to M27 (e.g. in case I add in capturing logic to M28
at some point).
● I then decided to copy over M27 logic for temporarily highlighting at the start of the game
civilian structures (to give the AI similar ‘memory’ to a human player).
● Although I started copying over the logic for identifying targets to capture from M27 I
decided I wanted to redo this, to make use of the land zone logic, so will park as a future
todo option since it’s such a niche improvement and took a lot of time to get working for
M27 (although that was largely due to the civilian ‘temporary reveal’ issues).
With thanks to Balthazar for clarifying how to give a unit temporary vision:
https://github.com/The-Balthazar/BrewLAN/blob/master/mods/BrewLAN_RNG/CrateDrop/hook/lua
/sim/ScenarioUtilities.lua#L215
I used this approach as there were issues with the reliability of the approach used by M27/RNG
2.30) Competitiveness
My plan to get M28 to a basic level of competitiveness is to pick a selection of maps that at least one
of RNG or DilliDalli performs well on, along with maps that M27 typically tests itself on, and for each
one have a random faction matchup against both RNG and DilliDalli. If M28 loses then I keep making
changes until it either wins the replay or survives to the end of it in a winnable position, at which
point I do a new replay with random factions and repeat the process until M28 wins.
91
i.e. it’s not about making sure M28 can win a majority of the time on every one of the maps, but just
that it is at least capable of winning a game, with the hope that if it is capable of winning on every
map it means that on the majority of maps it will have well above 50% win rate, and it will only be
some of the harder maps that it is below 50%.
2.30.1) VS RNG on maps suited to RNG
Crash site - performance
I chose this map as I know it’s one RNG has been tested on a lot so should prove a fair test for M28.
M28 has also lost vs RNG on this map relatively recently when I did a quick test (which is good since
it’s easier to learn from a loss than a win).
●
●
●
UEF mirror -The first game was a UEF mirror and despite very different styles and focus there
was little to separate the two AI, and the game could’ve gone either way. It ended up lasting
44m with multiple experimentals built, but M28 just edged it (in no small part due to its
fatboy surviving in a 1on1 fatboy dual with a sliver of health).
What was interesting was that despite M28 maintaining double RNG’s mass kill values in the
early-mid game, and having similar eco, it wasn’t able to increase its eco lead (and actually
fell behind slightly) – e.g. it would end up sacrificing its mexes to protect its mongeese. I
don’t think this is wrong, but it highlights the impact of different playstyles.
Seraphim Mirror: M28 frequently lost during testing, although did sometimes manage wins;
after making all the below changes I did one full game and M28 won a very hard fought
battle after 1 hour 2m; however fixing a minor bug that caused the replay to desync resulted
in an RNG win.
Crash site - Changes made (these were issues I spotted with M28 while watching replays relating to
the above):
1. Increased likelihood ACU will reclaim wrecks that are within its build range.
2. ACU should build on mexes inside its build area even if it’s still on its initial build order.
3. Improved initial build order and fixed bugs with tree reclaim so engineers will now try and
reclaim trees to prevent power-stalling while constructing a hydro.
4. Fixed AirAA run logic so it will now run from groundAA (previously there were bugs with this)
5. Gunships should ignore land scouts (which bypasses the issue of them trying to target a
selen that became cloaked after M28 became aware of it).
6. Added logic to assist air factories once T2 air has been reached.
7. When placing initial factory, if is a hydro in the same zone, then build the factory as close to
the hydro as possible while retaining mex adjacency and being in build area and not being on
reclaim.
8. Added similar approach to M27’s initial logic (before complexities were added) for deciding
on the location of T2 PD.
9. Made sure engineers are only built if the factory’s land zone is requesting more engineers (as
they were getting massively overbuilt if for a split second it looked like we had lots of mass,
but early-game mass storage %s can fluctuate quickly)
10. Most buildings should try to build close to the midpoint of a zone
11. Fixed shields should have a maximum search range based on the highest radius of the shields
available to the building engineer.
12. Increased the initial tanks to be built as a high priority once a certain number of engineers
have been built.
92
13. Made sniperbots (particularly seraphim bots) much more cautious when they outrange the
enemy so they’re less likely to die when kiting an enemy land experimental (although they
still can since theyre slower).
Overall I’d say M28 probably has a c.50% win chance against RNG on this map which is good enough
for an initial release.
Inquisition - performance
● Initially in an Aeon vs Aeon mirror M28 narrowly lost (it was at a 1:3 eco disadvantage for the
entire game but managed to get amazing value from its units, only dying because the enemy
built a GC that it only whittled down to half health before it reached M28’s ACU).
● Trying with random factions (Sera M28 vs Cybran RNG), throughout the below changes M28
managed to win a couple of times but lost 90% of the time.
● However, changing factions to random again (this time UEF mirror) M28 got a fairly
convincing win – it was ahead early, fell back slightly mid-game, then slowly obtained a
dominant position.
● More generally as with other replays it highlights the difference in design philosophy – M28
was able to have a 2:1 to 4:1 kill ratio to RNG consistently while not being far off RNG on eco,
yet couldn’t translate that into an eco lead as RNG’s aggression and M28’s conservativism
with its units would lead to RNG having map control and threatening m28’s mexes.
Inquisition - Changes made:
1. Fixed gunship spacing to be more of a square than an x
2. Increased priority of T2 air fac upgrade
3. Added in a new PD builder for when non-ACU ground threats are close to base.
4. Added a high priority engineer builder for air factories when high on mass even if ground
enemies are close (now that engineers can help by building PD)
5. On non-5km maps M28 should aim to only have 4 factories until it reaches T3, and should try
and be upgrading a mex almost at all times.
6. If enemy has T3 air and we lack T3 SAM in a core base then build as a higher priority than
power unless we have air control
7. MAA should no longer be built if the enemy is still at T1 and has no air to ground air threat.
8. Added a build cap on the number of active T1-T2 gunships of 80, and added backup logic to
build strats if we are about to overflow mass (with lots of mass).
9. AirAA units should ignore enemy groundAA when deciding whether to protect a friendly
experimental or ACU if the enemy has an air to ground threat nearby.
10. Engineers that are the primary builders can’t be reprioritised.
11. If an engineer is assisting an under construction building and there are no existing engineers
assisting it, that engineer should be treated as the primary builder.
12. If the ACU is told to retreat to base, but has no nearby enemy experimental, it should
consider attacking very nearby units and/or getting upgrades. If there is a nearby
experimental then the ACU should run to the nearest fixed shield.
Osiris
This map is one of the few I’ve come across where RNG can beat M27 at 1.0 a significant proportion
of the time, so I want to test M28 on it to see how it performs.
1. I came across an issue where 2 battleships stopped firing when near the naval factory and
just sat (with move orders that weren’t followed) until they were killed. My hope is that
having them try a specific unit attack order where this happens (and their shot isnt blocked)
93
may help mitigate this to some extent. I.e. battleships that haven’t fired for a while and are
near the rallypoint should try an attack order on the nearest enemy unit.
2. Fixed a bug where frigates were being used as scouts far too early.
3. Changed destroyer target prioritisation so they prioritise shield units
M28 won as UEF vs RNG’s Aeon on this map (prior to changes being made other than the bugfix for
frigates). Retrying with the factions reversed, M28 won even more convincingly.
2.30.2) High AIX modifier games
Island Zero
For a long time I was very confident that M27 could crush the default and Sorian AI on any map with
any settings (absent settings that completely broke the AI/caused it to not function). Then I saw a
video with an AiX 10.0 mod where M27 was nomads and died to Sorian AiX 10.0 AI.
While this is an extreme scenario and essentially means ‘bad’ decisions (like trying to build 4
experimentals at once, ignoring reclaim, using the ACU aggressively early on) could end up being
good, I suspect it should also reward effective mass management and spending at high eco levels
(and so simulate scenarios on large maps with lots of mexes).
I’d hoped to test vs DilliDalli or Sorian rush on the map where M27 failed, but both had errors so I’ll
do M28 vs M27.
Initially M28 lost to M27 (M28 UEF vs M27 Cybran), but after making the below changes it was able
to win (and had a stronger position throughout, with better eco and land forces).
Changes made:
1. One valuable thing this highlighted was that I’d forgotten to go back and add AiX modifiers
when calculating the ‘income excluding reclaim’ values that M28 uses for most of its logic
(leading to it thinking it was power stalling despite massively overflowing in energy).
2. Once engineers finish constructing something all engineers building that should be cleared
(to avoid an issue on high AiX modifiers where engineers far away don’t reach the building
before construction completes).
3. Allowed second power builder to trigger for T1 and T2 at very high gross mass levels
4. Second experimental builder if have at least 1.2k gross mass income, at least 5k stored mass,
net mass of at least 8 per tick, and mass stored % of at least 50% (provided don’t have low
power). Normally these will be built separately to the main experimental but will assist an
existing construction if they end up trying to build T3 arti or game-enders.
5. When construction is started, any queued engineers with a different action whose
construction will be blocked should have their orders cleared.
6. T3 and experimental arti and SMLs should be built adjacent to T3 pgens.
7. If we have at least 40 T3 engineers in a land zone then start ctrl-king T1 engineers in that
land zone that aren’t primary builders to reduce pathfinding issues – To consider this, I want
the aiBrain to have at least 100 engineers in total, and more than 10 T1/T2 engineers in a
zone, and it will then consider ctrl-King 3 engineers each time a T3 engineer is built in that
zone.
8. Units trying to retreat when in the core land zone should run away from the nearest enemy
unit.
9. There’s an issue with FAF where a unit htat is partially underwater (e.g. a megalith) but still
able to fire at surface units wont be fired at by units on move and attackmove orders.
However, they will if given a direct attack order. Therefore, if I have units that outrange the
94
enemy, they should use a direct attack order if the nearest enemy is underwater. This helps
in the scenario (which I saw in this replay) where a fatboy is faced with an enemy megalith
that is partially underwater.
o Testing in sandbox it looks like the fatboy is capable of hitting T1 subs (I didn’t bother
testing for others but from memory they all travel along the same layer), but I held
off implementing logic to ground fire these due to the risk it ends up ground firing
subs that it no longer has intel on (which would feel unfair for a human player on the
receiving end).
2.30.3) M27 pre release maps
I want to check how well M28 does against RNG and DilliDalli playing on maps that I normally test
M27 on prior to a release as a basic check of its competitiveness.
Polar Depression
● Vs RNG (M28 UEF vs RNG Seraphim) – a surprisingly close game, I thought M28 was set for a
comfortable win when it got to the early-mid stage with marginally more eco than RNG, but
RNG managed to scale up its eco with M28 falling behind, to a more than 2:1 eco lead to
RNG towards the late game. M28 then managed to slowly wear RNG down and maintain a
greater than 2:1 kill ratio. After more than an hour, with numerous experimentals built on
both sides, M28 won.
● Vs DilliDalli (M28 Sera vs DD Aeon) – Initially DD crushed M28. After many changes it looked
like M28 likely would have won the replay (but it ended). Re-running resulted in an M28 UEF
vs DD Aeon that DD won, but again after a change M28 looked like it would win. M28 finally
won with the next replay (with no changes needed), being M28UEF vs DD Sera. Essentially if
M28 survies against DD until it has T3 air and gains air control (which basically requires 2 Asfs
since DD will send its intie swap over M28’s base to die to flak/sams) then M28 is favoured to
win as DD has no answer to the ever growing gunship ball.
Changes made:
1. I realised (after seeing M28 build a crazy number of TMD) I’d not got any code preventing
TMD being overbuilt or even trying to ensure TMD gets built near a unit needing TMD
coverage, so added this in (i.e. adding a cap of 10 TMD per zone, having TMD get built near
the unit closest to the enemy base that wants TMD coverage, and not building TMD if it will
be further away from the unit than the TMD range).
2. When a building completes construction, don’t keep repairing it.
3. Improved the PD build logic to make sure the PD gets built in the same land zone, avoiding
issues where it would get overbuilt (due to being built in a nearby zone and not registering in
the zone that wants the PD)
4. If have at least 5 land scouts, then cap the number of land scouts at an amount equal to the
number of engineers we have
5. When looking for zones to send engineers to, those with unbuilt mex locations should be
prioritised.
6. Increased the thresholds for the ACU to get reclaim not in its build radius, particularly
early-game.
7. Increased the thresholds for starting upgrades, as at one point we ended up getting 4 T3
mexes at once at the core base (which only has 4 mexes).
8. Also adjusted the order of backup upgrades to be more likely to upgrade support factories
instead of HQs.
95
9. Changed air factory so instead of being idle it is more likely to build engineers when there
aren’t many engineers in the current zone (if it has the highest tech level).
10. Added new AA builder for if we have T2+ air factory and no T2+ fixed AA in base (even if the
enemy has no air to ground threat).
11. Non-engineer units just built from a land factory should be told to move away from the
factory for a second to reduce the likelihood they block the factory from building more units.
12. Given the ACU a 30s memory of zones it has run from, and zones it was trying to move to
when it decided to run, so it avoids those locations for 30s (reducing the risk the ACU
appears to constantly move towards somewhere then run a second later)
Pelagial
● Vs RNG – An interesting and closely fought game as M28 Aeon vs RNG UEF, RNG won the
island closest to M28 but M28 contested RNG’s island for some time before losing it.
However M28 gained naval control and air control despite worse eco, and was able to use
restorers to wipe out one of the islands. However RNG fortified the other with shielded flak
(that eventually took a nuke to dislodge it) and rebuilt navy at the rear of its base (leading to
a trap for any M28 navla units trying to assault it), managing to hold out with comparable
eco for a surprising length of time despite M28 navy being on its doorstep.
● Vs DilliDalli (M28 UEF vs DilliDalli UEF) – A much easier battle for M28, winning both islands
and then killing DilliDalli with T2 gunships. To be expected given DilliDalli doesn’t build navy
or T2+ air.
Twin Rivers (2vs2)
● Vs RNG – Although close at times (when M28 started upgrading in its base and RNG was able
to push and kill one of the ACUs) for most of the game M28 was on top. However after fixing
a bug M28 died to RNG Guncoms leading to a change to make them more scared if they are
outgunned by the enemy ACU.
● Vs DilliDalli – A very close game – forst almost the entire game DilliDalli was in one of M28’s
bases (for the first half the game the bottom-right player’s, with most core mexes dead, then
the other M28 ACU died and had its main base mostly destroyed). It wasn’t until M28 got a
handful of T3 gunships that it was likely to win.
Changes made (despite winning):
1. Relaxed the MAA builder so even if the enemy has no air to ground threat MAA should still
be considered once factories are at T2+.
2. ACUs should flee to base if they see that the enemy is getting an upgrade or has an upgrade,
and we aren’t in the core base.
3. Adjusted gunship formation to remove the corners of the last part of the square to reduce
the risk gunships on the edge cant fire at the target.
4. Units on a plateau or island should now move more randomly in the zone they end up in if
there are no enemies and they cant reach the enemy base.
5. UEF and Seraphim Cruiser weapon firing priorities should prioritise enemy TMD and shields
ahead of other units
6. Seraphim destroyers should surface if they start the game underwater
Eye of the Storm
● Vs RNG (UEF vs Aeon) – comfortable win, with M28 winning land and navy (with neither
player winning air).
96
●
Vs DilliDalli (UEF vs Aeon) – I missed most of the replay but it looked like a fairly comfortable
M28 win for the part I saw (to my surprise as DilliDalli gave M27 grief for a long time on this
map).
Changes made:
1. Naval combat units should retreat if no friendly AA and enemy has nearby torps.
2. Anti-air submarines should surface or submerge based on nearby enemy threat.
3. Battleships should prioritise enemy structures and high value naval targets.
Forbidden Pass
● Vs DD – DD proved a tough opponent winning numerous times initially – the base starts are
just far enough away that M28 thinks it is safe to eco/tech, and then DD sends a stream of T1
tank spam into M28’s base before M28 has time to build enough T2 units. However this did
expose some fatal flaws in M28’s logic where it would get multiple T3 upgrades despite
having no T1 or T2 presence. Even after improvements, with M28 gaining air control and a
gunship ball it looked like it could fail, as DilliDalli’s relentless stream of units from two sides
meant the gunships at some points couldn’t keep up and tanks would seep into M28’s core
base. M28 eventually won as Aeon vs DD Sera.
● Vs RNG (M28 UEF RNG Sera): RNG maintained a 2:1 eco lead for much of the game, but M28
managed an impressive 8:1 mass kill lead that increased to more than 10:1. When M28
eventually reached eco parity the game was effectively over.
Changes made:
1.
2.
3.
4.
Added high priority early game T1 air fac upgrade logic
Tweaked low power flag so we are less likely to overbuild T1 PGens initially
Made M28 more likely to eco early on 10km maps
Added a minimum number of units for T2 land and air facs to have built before being
considered for upgrading.
5. Fixed an issue causing M28 to think it had more factories than it actually had when a factory
upgraded.
6. Increased energy requirements to get a 2nd HQ upgrade to reduce the risk that we think we
have enough energy due to the 1st upgrade not yet having been started (e.g. if a land factory
queues up an upgrade, while building a unit, then M28 will think it has more energy spare to
start an air fac upgrade than it actually has).
7. Slightly increased requirements to build a naval factory.
Floralis
● Vs RNG – It took a long time to get M28 to the position where it would win in a Cybran
mirror replay – it basically required abandoning the ‘Eco and rush to T2’ plan in favour of a
‘build land factories and tanks for ages’ plan, coupled with making the ACU semi-suicidal.
Even then the game went to the T3 stage.
● Vs DilliDalli – After how hard the battle against RNG was, and how long it took M27 to win
against DD I was expecting a tough battle but M28 won on the first attempt (Aeon vs Sera)
Changes made:
1. Toned down the emergency T2 upgrade for T2 PD in response to an ACU is the ACU doesn’t
have any upgrades and our mass income is poor.
97
2. On small maps more land factories should be built, and built more aggressively, while slightly
less power should be built.
3. Decreased threat thresholds for units to attack on small maps and if the ACU is in the current
zone.
4. Reduced the amount of fixed AA to be built in the core base at lower tech levels.
5. Significantly increased ACU aggression on small maps.
6. Limited the number of engineers built on smaller maps to be based on the number of
combat units.
Open Palms
● Vs DilliDalli – A couple of very close games where DD won (one was due to a bug that caused
M28 to stop building units at its land factory) before M28 won as Cybran vs Seraphim.
● Vs RNG (UEF vs Aeon) – At first I thought M28 had a comfortable win, but RNG got a GC to
bring M28’s shielded ACU that was under a shield within a second of death before M28’s
gunships took it down.
Changes made:
1. Adjusted the logic for upgrading from T2 air to T3 air slightly more to require T2 air units to
have been built (not just T2 units) before upgrading to T3 air, but to only have this check if
the enemy doesn’t already have T3 air.
2. Allowed more emergency PD to be built against non-ACU threats if we have some mass
stored and/or positive mass income
Astro Craters
● Vs RNG (Cybran vs Cybran) – Comfortable win with M28 ahead on eco the entire game.
● Vs DD (UEF vs Aeon) – Comfortable win (as expected given M27 has managed to beat 2.0 DD
on this map – i.e. it’s one of DD’s weakest maps)
Changes made:
1. Fixed an oversight that meant the first land experirmental built by a Cybran wasn’t a
monkeylord
White_Fire
● Vs DD – Comfortable win
● Vs RNG – Comfortable win
Setons Clutch (4v4)
M28 won both games but they were much closer than expected:
●
●
Vs RNG – A closer game than I was expecting, with M28 building a yolona in the rear air slot
but RNG getting a GC and ythotha up to it and killing the nearby M28 ACU. Fortunatly M28
managed to kill the experimentals shortly after and its yolona proceeded to wipe out RNG.
Vs DD – Also a close game – DD took out 3 of M28’s 4 bases (although only 1 of the ACUs),
and M28 didn’t get its usual gunship deathball due to spending its mass on an experimental.
I didn’t actually see the original replay through to the end as M28 was making incredibly
slow progress with a megalith deathball inching slightly closer to DilliDalli’s rock and beach
bases (M28 had control of both seas, navy, and far outmatched DD’s ground forces, along
with a 3:1 eco advantage so it was only a matter of time before DD died).
Changes made:
98
1. Fixed a few different bugs
2. Realised I’d not completed drafting logic for specifying a particular faction to build a unit
(relevant at the moment for experimentals) so added in
3. Similarly realised I didn’t have any code drafted for allocating stealth boats so added this in.
4. When building antinavy units from a naval factory, ‘Plan B’ missile subs shouldn’t be
considered.
5. Made it more likely for game-enders to be built at high counts of experimentals.
6. Experimental construction should be paused slightly sooner if there are nearby enemies (to
allow other units to be built instead)
7. Added logic for the Engineer part of M28 to get mex upgrades, mainly intended for cases
where we have a T3 mex or as a team have significant mass income, to make sure core base
mexes are upgraded constantly if there are no nearby enemies, to help with faster eco
scaling.
8. ACU orders – if no enemies in LZ, are in core base, and no land factories, then build a land
factory
9. Don’t build more nukes if we already have 2 (or 1 on 10km and smaller maps)
10. Scathis should now be treated like a T3 arti instead of a mobile land unit that tries to move to
the enemy
11. Scathis should now have a delay before being given a target to fire at to reduce the likelihood
it firs a single shot and then aborts.
Burial Mounds (1v1)
● Vs DilliDalli – DD suicided their ACU into the civilian PD
● Vs RNG – M28 won – I had this running in the background so didn’t see the game but it
looked fairly close in that M28 had gunships at RNG’s base while RNG had a GC approaching
M28’s (with M28 having nothing in response).
Badlands (4v4)
● Vs RNG – M28 lost after a close battle (it got a T3 gunship deathball but couldn’t get a
response to RNG’s T2 arti firebases that were able to reach its main base). However, this was
in part due to some of the logic it had working fine for 1v1 but terribly for 4v4, for example
with it seeking to get gun, nano then shield on a UEC ACU, along with upgrading T2 land to
T3 while still having T1 mexes in base (because it was considering the total team’s mass
income). After fixing this, M28 was able to win.
● Vs DD – I was expecting M28 to lose this but it had a comfortable victory, being equal or
ahead on eco at the T2 stage and crushing DD as soon as it got sufficient T2 units (the only
moment of concern being prior to this when DD took the fight up to M28’s bases).
Changes made:
●
●
●
New very high first T2 builder
Updated priority factory upgrade logic to factor in the number of M28 players on the team
for team resource thresholds (so factory upgrades aren’t started too early)
Increased some of the ACU upgrade eco thresholds to factor in the number of teammates,
and increased the thresholds generally for ACUs with 2 upgrades and multiple teammates.
Africa (4v4)
● Vs DilliDalli – Another comfortable victory
99
●
Vs RNG – A closer battle, RNG killed one of M28’s ACUs and had roughly a 3:2 eco advantage
for a long time, but M28 maintained a strong kill:loss ratio. RNG then surprised one of the
M28 with an underwater Ythotha (claiming its second kill)
AiX further test - vs Adaptive, Sorian adaptive, and Uveso Overwhelm
● Vs Uveso Overwhelm on Island Zero – M28 and Uveso were equal for much of the game;
M28 had air and navy control, but Uveso flooded the pond with percies which soaked up
M28’s navy’s attention, while M28’s massive (>100) restorer deathball couldn’t crak Uveso’s
core base. M28 was undone initially by a bug with its SMD that caused it to stop reloading
after 2 missiles. After fixing this and some other changes to make it spend its mass sooner
when overflowing (including building gameenders and tempests) M28 was able to claim a
comfortable win.
● Vs Adaptive and Sorian Adaptive on Finns Revenge – M28 had comfortable wins against both
default AiXs. What was concerning was against Adaptive there was a massive slowdown
every second or so. Enabling detailed logging didn’t reveal any culprit so I’m unsure if it was
down to M28 or the adaptive AI (but am hoping the latter)
Changes made
1. Missile firing buildings (SMD, SML, TML) should be unpaused whenever they fire a missile.
2. Added new builders for tempest, atlantis, and an independent game ender builder (so in very
high mass scenarios in theory 4 experimentals could be built at once, even if most of the
time 1-2 will be built).
3. Increased the number of naval factories in very high mass scenarios.
4. Updated strat placeholder logic to target T2 or T3 structure and make use of the torpedo
bomber logic to factor in strike damage
2.30.4) Pre-release further tweaks
1. Changed the colour of the eyes from M27 so there’s a slight difference in the mod icon
2. Added some initial greeting messages and some on death messages, including a reference to
M27 being better
3. Made mobile shields and stealth a lower priority than other units wanted for a land zone
4. Made MML much higher priority if the enemy has a nearby ACU and T2+ structures
5. Fixed a bug causing massive T1 PD overbuild if the enemy ACU got close before we had T2.
6. Fixed a bug where TML that were paused wouldn’t fire the extra built missiles.
7. Reduced PD wanted in response to enemy Guncom to 1.75 of the threat (so e.g. against a
nano-gun UEF ACU we should get 4 T2 PD instead of 7)
8. ACU more likely to run from enemy guncom.
9. TML shouldn’t be paused if they have no missile loaded
10. More shields should be built if the enemy has a significant air to ground threat.
11. Delayed initial greeting slightly
12. 1 tick delay added for factories if a factory in the same zone has built something in the last
tick (to avoid issue where we have 4 air facs, don’t build anything due to lack of power, then
build asf on all 4 at once once get power, and think each time we have enough power
because the power requirements of those started in the same tick aren’t factored in)
13. T2 arti in response to high range enemy units (>=65), amount based on their threat, and our
threat – e.g. moving outwards for each zone
14. T1/T2 radar should be ctrl-k’d when it becomes obsolete
15. Slightly relaxed the thresholds for not building any more MAA
100
16. Lowered the limit on bombardment units to be built, and added a high priority cruiser
builder if we have 3+ destroyers and no cruisers.
17. Naval factories should no longer be built in low value ponds.
18. Gunships should engage enemy experimentals approaching a core base as a relatively high
priority, even if the experimental is protected by AA.
19. Increased the max enemy AirAA required to make restorers flee.
20. On 10km and smaller maps even if the enemy base takes a long time to travel to M28 won’t
go into full ‘eco/mass engi production’ mode at the start and neglect tanks entirely.
21. M28 should exit energy stall mode early-game if it has built more power from when it last
stalled; it should also be less likely to go into energy stall in the first 2 minutes, requiring
0.1% energy storage instead of 5%.
22. At least 1 gunship should be built even if we have low power if enemies are near to the air
factory.
23. If we have available gunships and no AirAA threat then the gunships should be restricted to
only fighting by ground to air or adjacent to a core base, in case the enemy has an AirAA
threat.
2.31) Pathing optimisation
Polar depression load time improvement – initial setup
FAF servers were down for longer than expected so I figured I’d have another look at the pathfinding
to see if there were any improvements that wouldn’t impact too much on accuracy given how long
some maps like polar depression take to calculate.
Performance with current process:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=133.69367980957
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=132.0138092041
info: ProfilerOutput: No.3=AssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=102.55484008789
info: ProfilerOutput: No.4=AssignMexesALandZone; TimesRun=1; Time=11.381332397461
info: ProfilerOutput: No.5=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=9.1100463867188
info: ProfilerOutput: No.6=RecordTemporaryTravelDistanceForBaseSegment; TimesRun=278;
Time=9.0962219238281
info: ProfilerOutput: No.7=RecordPathingBetweenZones; TimesRun=1; Time=8.9162902832031
info: ProfilerOutput: No.8=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=2450; Time=8.909912109375
So clearly the area for improvement is assigning segments around mexes to land zones.
It also highlights that I do need to do something about this – taking more than 2 minutes on a very
fast CPU to load means worse cpus presumably could take 4+ minutes on a 10km map 1v1.
101
M28’s current pathing approach (summarised)
● Divides the map into 'segments', which are small squares based on map size (e.g. 10km I
think is either 1x1 or 2x2 in size)
● Groups the map by plateaus (i.e. amphibious/hover pathable areas) that contain mexes
● Assigns each mex to a 'zone' - nearby mexes are grouped into the same 'zone'
● Cycles through each mex in the plateau being considered
● For each mex, it then cycles through each of the segments around that mex (up to a
particular search distance, e.g. a distance of 85) and checks how long it takes to path to the
mex by land, vs how long it takes by air
● If the land pathing is within 15 of the search distance (e.g. 100) then it records that mex's
zone as a potential zone of interest, and records how long it would take to travel to the mex
by land
● Once it's finished doing this for every mex, it goes through the values it recorded for land
travel distance by mex for each segment, and assigns each segment to the closest (by land
travel distance) mex
Faster alternative approach
Jip helpfully suggested a far quicker alternative approach to this that avoided the need to calculate
travel distance at all:
●
●
●
●
Start at each mex position
Consider the 4 adjacent ‘segments’ to this, and if they are pathable, simultaneously with all
other mexes, and (if pathable) assign them to the same zone
Then move on to each adjacent segment to each of the preceding pathable segments and
repeat the process
Jip drafted some code to achieve this but I wasn’t able to get it to work for my use case
(either I was using it wrong or it was complicated by how I was handling references to
segments) so I rewrote in a probably slower but easier for me to follow way. The result is
instead of taking 102.5s to assign segments near mexes, it took just 0.12 seconds!
There were a few complications in getting the approach to work though:
●
●
My segment sizes on a 10km were 4x4 compared to the 1x1 of the Navmesh. Therefore it
was possible that two 4x4 squares would both be in the same land pathing label, but they
might be either side of a cliff that is missed because it is less than 4x4 in size. I therefore
added logic to check the level eveyr single point in a straight line between the two 4x4
squares.
FAF’s navmesh would return a land pathing label for locations that weren’t pathable (in case
it was a unit that was close to a cliff). Jip kindly agreed to add a new function that would just
return the expected land pathing label for the position (and not try and search for the
nearest valid pathing label). To avoid needing to delay M28’s release for this, I’ll do a copy of
the code in the meantime and can patch M28 to refer to the FAF definition at a later point.
This is what was generated prior to the new label:
102
i.e. you can see zones in most cases are being created ignoring cliffs (which isn’t wanted).
This is the result after the new label:
103
i.e. the zones are now reflecting cliffs that break the pathing.
Initial performance results
Log after putting in an alternative calculation (‘ThirdAltAssignSegmentsNearMexesToLandZones in
the below log, which replaces the previous ‘AssignSegmentsNearMexesToLandZones’ function):
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=25.735855102539
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=24.262310028076
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=9.7822494506836
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=7.9220657348633
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=2352; Time=7.917064666748
info: ProfilerOutput: No.6=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=6.417652130127
104
info: ProfilerOutput: No.7=RecordTemporaryTravelDistanceForBaseSegment; TimesRun=3291;
Time=6.3633232116699
info: ProfilerOutput: No.8=ThirdAltAssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=0.12160491943359
Applying similar approach to the rest of the land zone creation logic
Given these massive performance improvements, I want to try the following:
●
Use a similar approach for ‘AssignRemainingSegmentsToLandZones’ (i.e. this currently takes
6.4s per the above)
o When doing this, to avoid tiny zones I’ll copy the zone of a nearby segment that
already has a zone for up to half the segment search distance (so effectively
extending existing land zones by half the search distance, or creating new zones at
the full segment search distance).
After reflecting this, the results for polar depression are:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=18.589544296265
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=17.096019744873
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=10.234748840332
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=6.6568431854248
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=5174; Time=6.6492786407471
info: ProfilerOutput: No.6=ThirdAltAssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=0.17839050292969
info: ProfilerOutput: No.7=GetMapWaterHeight; TimesRun=1; Time=0.042026519775391
info: ProfilerOutput: No.8=GetCombatThreatRating; TimesRun=8890; Time=0.041606903076172
info: ProfilerOutput: No.9=GetAirThreatLevel; TimesRun=16510; Time=0.026054382324219
info: ProfilerOutput: No.10=AltAssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.016372680664063
I.e. ‘AltAssignRemainingSegmentsToLandZone was used instead of
AssignRemainingSegmentsToLandZone, with the new function taking 0.016s vs the old function
taking 6.417s.
Water zones
Previously my water zones used a simple approach of requiring the segment to be pathable, but not
worrying about how long it would take to travel there.
If I was to use a similar approach to the land zones then it should allow blocking terrain to be taken
into account.
Currently Pelagial loads very quickly, with the following results:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=6.1898040771484
info: ProfilerOutput: No.2=RecordIslands; TimesRun=1; Time=3.330696105957
105
info: ProfilerOutput: No.3=RecordClosestAllyAndEnemyBaseForEachWaterZone; TimesRun=2;
Time=2.3944473266602
info: ProfilerOutput: No.4=SetupWaterZones; TimesRun=2; Time=1.1941375732422
info: ProfilerOutput: No.5=RecordWaterZonePathingToOtherWaterZones; TimesRun=1;
Time=1.1399459838867
info: ProfilerOutput: No.6=SetupLandZones; TimesRun=3; Time=0.21800231933594
info: ProfilerOutput: No.7=AltAssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.11338043212891
info: ProfilerOutput: No.8=ThirdAltAssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=0.061996459960938
I replace it with the following logic:
●
●
●
As before, create water zones for any underwater start positions, and assign the nearby
segments to this water zone
Then divide the map up into intervals, finding the nearest water position to every interval.
The main difference, is instead of assigning any nearby entry that is in the same pond to the
water zone, I’ll instead check the NavUtils water result when considering all starting interval
points simultaneously (i.e. considering the 1st adjacent segments; then 2nd adjacent
segments, etc.)
The log after these changes is:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=6.8587608337402
info: ProfilerOutput: No.2=RecordIslands; TimesRun=1; Time=3.5790138244629
info: ProfilerOutput: No.3=RecordClosestAllyAndEnemyBaseForEachWaterZone; TimesRun=2;
Time=3.1335601806641
info: ProfilerOutput: No.4=SetupWaterZones; TimesRun=2; Time=1.5646324157715
info: ProfilerOutput: No.5=RecordWaterZonePathingToOtherWaterZones; TimesRun=1;
Time=1.1829566955566
info: ProfilerOutput: No.6=CreateWaterZones; TimesRun=1; Time=0.37642288208008
info: ProfilerOutput: No.7=SetupLandZones; TimesRun=3; Time=0.22396850585938
info: ProfilerOutput: No.8=AltAssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.11511611938477
info: ProfilerOutput: No.9=ThirdAltAssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=0.066295623779297
I.e. the time to setup water zones has increased slightly, but not massively, so I think the benefits to
accuracy warrant this change.
106
Increased resolution
The next change I want to consider given the time savings is increasing the segment ‘resolution’ size
– currently each segment is a 4x4 box on a 10km map, and ideally I’d like it to be 2x2. On a 10km this
would mean 65.5k entries, vs 16k from 4x4. Testing with 2x2 size:
Testing on polar depression gives the following results with this change:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=18.795766830444
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=17.228979110718
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=10.103311538696
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=6.5982322692871
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=4302; Time=6.5912971496582
info: ProfilerOutput: No.6=ThirdAltAssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=0.47310447692871
info: ProfilerOutput: No.7=GetMapWaterHeight; TimesRun=1; Time=0.042203903198242
info: ProfilerOutput: No.8=GetCombatThreatRating; TimesRun=8890; Time=0.041923522949219
info: ProfilerOutput: No.9=AltAssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.040422439575195
i.e. before it took 18.6 to setup the map, now it takes 18.8, so there’s been barely any change.
Trying again this time with a cap of 120k entries (almost 5 times the current 25k entries), i.e. 1x1 on a
10km, or 3x3 on a 20km:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=18.685972213745
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=17.150274276733
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=10.107316970825
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=6.5250091552734
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=4302; Time=6.5181579589844
info: ProfilerOutput: No.6=ThirdAltAssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=0.46537780761719
info: ProfilerOutput: No.7=GetMapWaterHeight; TimesRun=1; Time=0.041728973388672
info: ProfilerOutput: No.8=GetCombatThreatRating; TimesRun=8904; Time=0.041591644287109
info: ProfilerOutput: No.9=AltAssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.039133071899414
So again barely any increase (although there is a margin for error with these estimates – it took 20s
on 2x2 sizing on a separate replay on the same map, polar depression).
I’ll therefore stick with this size (i.e. 1x1 for 5km and 10km, 3x3 for 20km, 12x12 for 80km
107
Setons Clutch – time taken
Testing with these settings on Setons Clutch gives the following results:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=16.968250274658
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=14.395324707031
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=10.605827331543
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=2.8388862609863
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=8126; Time=2.8275909423828
info: ProfilerOutput: No.6=RecordClosestAllyAndEnemyBaseForEachWaterZone; TimesRun=2;
Time=1.8834114074707
info: ProfilerOutput: No.7=SetupWaterZones; TimesRun=2; Time=0.93829345703125
info: ProfilerOutput: No.8=CreateWaterZones; TimesRun=1; Time=0.86266326904297
info: ProfilerOutput: No.9=AltAssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.55030059814453
info: ProfilerOutput: No.10=ThirdAltAssignSegmentsNearMexesToLandZones; TimesRun=1;
Time=0.37235832214355
I.e. it’s within the acceptable time limit for generation.
2.31.1) Check of performance on other maps
As a final sense check of how M28 compares to RNG and DD, I wanted to pick a selection of maps in
the current ladder pool and see what it’s win rate is (i.e. this is in contrast to before where I would
re-run a replay again and again until M28 got a win in some cases).
Unfortunately with FAF down at the time of doing this I couldn’t use the latest map pool; however
when testing M27 for v41 I’d noted its performance against RNG and DD on a selection of maps
which I think were mostly in the pool at the time, so I’ll do the same for M28, although in the end I
got bored and just did M28 vs RNG matchups. The maps I ended up doing were:
M28 won 15 of the matches, RNG won 4, with a mix of 5km 10km and 20km maps although the
majority were 5km. The maps where RNG managed a win were Loki, Serenity Desert Small, Saltrock
Colony, and Forbidden Pass (although M28 also managed wins on all of these except Saltrock Colony
where I only did the one match).
As a general rule, M28 had comfortable wins on larger (20km) maps and maps with navy. On small
maps RNG would usually win the T1 stage and end up with a 2:1+ eco advantage. M28 then would
usually claw back that advantage slowly with gunships and T2+ units, or RNG would have such an
advantage that it would overwhelm. Sometimes RNG would win just as M28 looked to have
equalised on eco, by getting a land experimental or early strat before M28 was able to finish
recovering and build up enough gunships or AA to deal with the treat.
The full results are as follows:
108
Row Labels
2v2 - Serenity Desert Small – FAF version
Adaptive Hardshield Oasis
Auburn Canyon
Crossfire Canal - FAF version
Forbidden Pass
Loki - FAF version
Paradise 2v2
Point of Reach
Saltrock Colony
Theta Passage
M28 won
2
3
2
1
1
1
1
1
RN
G
wo
n
1
1
1
1
3
2.32) V2-v3 - M28 in the FAF Campaign
2.32.1) V2 Hotfix
Thanks to Relent0r who mentioned M28 wasn’t building tanks unless it had intel of the enemy –
there was a bug where it thought it had built 0 of every unit leading to it always building engineers.
2.32.2) V3 - Misc changes and bugfixes
●
●
●
●
●
Fixed a bug with PD placement logic – previously it would start near the base and move
towards the enemy when looking for a valid location; now it will start near the base and
move closer to the base. I.e. this means engineers should no longer try and build PD
miles away from the existing land zone.
M28 wouldn’t work with AiX modifiers in some cases where the AiX modifier was nil, e.g.
on a campaign game.
Strategic bombers should have an increased search range on very large maps or in large
numbers, and idle bombers should return to the rally point.
ACU should only get a build location once – previously it would get every cycle, and then
not process if the order was for the same location. However, it would also exclude
queued buildings when picking a location, leading to it alternating between building in
one location then another location on repeat (if the location wasn’t in its build range).
If the ACU can’t find a location that it can build immediately for a land factory (with mex
adjacency) it should try and find a location without any adjacency.
2.32.3) Black Day
Identifying if a map is campaign
Doing logging of the ScenarioInfo.Options table on a campaign map it provides the following values
which looked like they might work as proxies for figuring out if it is a campaign map (this was with
myself in the first slot and M28 in the second slot on the first FAF campaign map)
:
Difficulty: 1
info: iEntry=save; vValue="/maps/x1ca_coop_001.v0028/X1CA_Coop_001_save.lua"
109
ScenarioFile: /maps/x1ca_coop_001.v0028/x1ca_coop_001_scenario.lua
Other values of potential use:
info: iEntry=HumanPlayers; vValue={ table: 2EE1B870 Player1=1, Player2=6 }
info: iEntry=size; vValue={ table: 2E6D5D98 1024, 1024 }
By contrast, on a non-scenario map the same values were:
info: iEntry=save; vValue="/maps/astro_crater_battles/astro_crater_battles_save.lua"
debug: - Difficulty: 1 [I think this is a value for one of the various mods I’ve got downloaded]
info: - ScenarioFile: /maps/astro_crater_battles/astro_crater_battles_scenario.lua
There was no value for HumanPlayers
info: iEntry=size; vValue={ table: 1C1C0BB8 512, 512 }
However Hdt80bro helpfully provided a more reliable method – checking the ScenarioInfo.type
value, which is “skirmish” for skirmish maps (including matchmaker), and "campaign_coop" for
campaign maps done via co-op.
I also added in a 0.5s delay at the start of the game before running M28 logic since Hdt80bro
highlighted how the brain creation happens before some of the campaign/map information is
loaded.
●
However, this caused an issue as M28 would call the default brain create logic if a non-M28
brain was created, and the delay meant the flag for if a non-M28 brain was created wasn’t
being set, leading to both M28 and non-M28 logic running.
Changes made
● Refined how M28 views civilians so on a campaign map it will only treat brains with “civilian”
or “Civilian” in their name or nickname as civilians (in addition to the other checks M28
does).
● Changed the initial player greeting to reflect it’s a campaign, and delayed it until M28 has an
ACU
● M28 wouldn’t appear to ‘see’ enemies when the playable area expands – i.e. it is based on
the original playable area. I therefore need an approach that will be compatible with
changes in the playable area size.
o Searching the FAF database I’m assuming the following function (which changes the
playable area) will be called by campaign maps when the map expands:
o fa/lua/ScenarioFramework.lua function SetPlayableArea(rect, voFlag)
o I therefore want to hook this function so after it has run I can update M28’s record of
the playable area (which, at a basic level, should at least allow it to send units to the
enlarged playable area).
o For now I’ll try with the approach of having M28 start off generating land zones as
though the entire map is playable, and then switch to only allow units to move
110
●
between the currently playable area; when the playable area changes I’ll then
update the recorded playable area.
At the start of the first mission the first player gets given all of the remnants of the base,
including T3 PGens and T3 mexes. A human player is forced to be in the first slot, and the
ability to gift units to a teammate is disabled. Therefore M28 is prevented from gaining
resources to build up (beyond reclaim and what it builds itself), causing its progress to be
heavily stalled. I therefore want to allocate units to M28 at the start of the game using the
following approach:
o When an ACU is identified, if it’s a campaign map start a forked scrip to check for
friendly buildings.
o Every tick, check for allied units within 250 of the ACU position. And abort the code
after 5 seconds
o If units are found, start a new script to divide up friendly units:
▪
Calculate the number of human players, and M28AI players, and work out
the nth unit to give to M28AI (e.g. if 50% of players are M28, n will be 2; if
75% of players are M28 n will be 4)
▪
Group all units by blueprint in a table, only grouping those non-ACU units
owned by a human brain
▪
●
●
●
Cycle through each blueprint table entry. If there is more than 1 entry in the
table, then cycle through each entry, and allocate the nth entry to M28AI
● If there’s more than one M28AI, allocate it to a random M28AI.
During testing one issue that arose was enemy brains that had a start position outside of the
playable area. M28 would try and get the plateauland zone of that location and come across
an error. This highlighted a flaw with M28’s logic, as it divides the map into ‘segments’, but it
bases these segments on the playable area rather than the overall map size. I therefore want
to review all use of my PlayableArea logic and have a separate variable for the
potentialplayablearea – on campaign maps this will be the entire map size, on other maps it
will be the main playable area.
One issue this exposed on non-campaign maps is that delaying when some of the m28 initial
code runs until later means it thinks M28 AI aren’t in the game
Campaign doesn’t allow use of AiX modifiers, meaning if an AiX AI is selected it causes an
error as M28 tries to interpret a nil value. I therefore added a lobby option for AiX up to 1.0
for the ally, along with backup logic where if it’s nil then it will manually set the buff to 1.5.
Given the bugs encountered during this (the main one being an error in certain water map zone
creation which could break the entire AI) I’ll release with only these changes for now rather than
improve its campaign performance to a decent level.
2.33) V4 – Further campaign improvements
2.33.1) Bug fixes
1. Added in some redundancies to code to check for when there are no longer any active M28
brains.
2. Updated some of the team handling logic to make sure it is only running for teams
containing an M28 brain.
111
3. Fixed an error in the code that could have sometimes occurred when trying to build shields
4. If the enemy base was in water then a T2 land factory trying to build something when a T3
factory existed could cause an error.
5. Expanded the check on unit restrictions being present to reference a separate setting (as
campaigns don’t make use of the scenario.options setting for unit restrictions, which was
what I was checking previously).
6. There’s an issue where a separate AI is giving my factories assist orders causing them to not
build anything. As a partial preventative measure, my factories will be treated as idle if their
unit state is guarding (more for redundancy).
7. In addition, I’ve added various hooks to try and prevent base AI taking control, requiring 3
different sets of code to cover the current FAF, FAF develop, and then soon to be released
FAF develop changes all of which impact on where the code running base AI logic appears.
8. M28 brains should update the primary enemy brain base location when a player dies if the
nearest enemy base brain is dead.
9. When there were no enemy units in the pond the navy was meant to try and move
elsewhere to support but there was a bug cuasing it to do nothing.
10. Fixed a bug where a flag for whether to avoid groundAA when protecting ACUs and
experimentals wouldn’t reset (which in theory could have caused an issue with multiple
experimentals in different scenarios).
11. When searching for buildable locations in a water zone, one of the lines failed to flag it was a
water zone (resulting in errors).
12. One of the unit cap checks that would reset it was inverted, meaning it would immediately
reset the flag that M28 was close to the unit cap limit (instead of requiring a bit of headroom
before changing the flag).
13. Mass and energy stalls should now factor in the highest build rate cheat modifier on the M28
team if the brain has cheats enabled (i.e. its an AiX brain) – previously build rate modifiers
were ignored.
14. It looks like if engineers are told to assist a T2 factory, and that factory upgrades to T3, the
engineers will stay assisting it. This causes an issue for my tracking, as I track what units are
assisting a factory against the factory itself, but if a factory upgrades, the T2 factory unit is
destroyed and replaced with a T3 factory unit (that doesn’t contain this information). I’ll
therefore have M28 treat engineers as available if their last order was to guard a unit that no
longer exists.
15. Power and mass stall managers were failing to calculate the mass/power saving achieved on
upgrading mexes.
16. Power and mass stall managers should no longer incorrectly calculate the mass/power
savings as being 10% of the actual saving for certain units (engineers and upgrading units)
17. Anti-air submaraines (e.g. seraphim submarines) should more reliably surface when faced
with enemy torpedo threats.
18. If there are submarines in a zone but no non-submarine units, the submarines should still get
orders.
19. Seraphim submarines and atlantis should no longer be treated as AA units but instead as
submersible combat units.
20. There was an error in one of the eco tests if an experimental factory was built (e.g. as a result
of mods).
21. Yolona Oss should no longer be paused even if it has no targets.
22. Water zone generation logic contained an error that conflated segments with positions.
112
23. When air rally points were considering locations underwater they used the wrong reference
for water zones which could lead to no valid rally point being identified and various resulting
errors.
24. Fixed several tracking errors that resulted in my profiler providing incorrect results for how
long a function was taking.
2.33.2) Misc changes
1. Retreat low health shielded units (titans, obsidians, etc.) – previously only units like fatboy
would be retreated. However such combat units will retreat when their shield is much closer
to being depleted compared with a fatboy.
2. Added a higher priority AA builder for minor zones that contain T2+ mexes that are under
enemy air attack.
3. Added a cap to the number of experimental naval units to be built.
4. If unit restrictions are present and we failed to build an experimental level unit, then a
random experimental level unit should be built instead if we have at least 35% mass stored.
5. Further factories shouldn’t be built if we have recently failed to find anything to build from
the factory.
6. Add builder to build land experimentals in water if we have very high mass and income, and
already have at least 4 T3 navy/experimental units.
7. If we tried building an experimental and failed, and unit restrictions are present, then a
random experimental should be built.
8. Added in Quantum gateway builder if we are AiX 1.2+ or in campaign, and all mexes in the
zone are T3, and we don’t have low mass and have no quantum gateway in the zone.
9. Added simple logic for RAS SACUs, so they help with fighting if units are in the same zone as
them, otherwise they assist a unit (e.g. the quantum gateway; an air factory; shields; or an
active upgrade)
10. Adjusted AirAA thresholds to be more likely to engage when there are low levels of enemy
ground AA threats.
11. AI Teammates on campaign maps would often have start positions that weren’t pathable or
were outside the initial playable area (e.g. the Order on mission 2). I’ll therefore only include
such locations in the ‘nearest friendly base’ that is recorded at the start of the game if it is
pathable, and is inside the playable area.
12. Sometimes land zones could be close enough for AA in them to hit a zone that wasn’t
adjacent to it, so I’ve added a backup to look for a single point at 90 degree angles when
searching along a path for zones that could be in range of air travel paths.
13. Increased the enemy AA threshold required to make gunships run if their last order was to
attack.
14. Adjusted the flag that means no MAA get built to be based on MAA threat rather than total
ground threat to see if it resolves issues where M28 would fail to get MAA to support
experimentals.
15. Some unit cap mods change the army unit cap rather than the scenario.options unit cap, so
I’ll check the unit cap for a particular army rather than the scenario settings to work out the
unit cap for an M28AI.
16. Engineers should now try and build the same building at the same location instead of
assisting the building engineer so the building sometimes gets started on slightly sooner.
17. Factories with nothing to build should have any assisting engineers clear their orders.
18. Fixed a mod compatibility issue where the indirect fire range on units with no range category
wouldn’t be recognised.
113
19. Increased SMD pause threshold based on total enemy nuke launchers – i.e. 2 + 1 for each
enemy nuke launcher
Optimisations
Fearghal provided a replay where M28 would suffer major slowdowns late game. I therefore made
the following optimisations in an attempt to reduce this:
●
●
●
●
●
●
●
Blacklist build locations are only done for a specific location rather than an area. Theyre also
recorded by location, so instead of needing to check every blacklist location I only need to
check for 1 particular entry in the table. I’ll also no longer track the type of blacklist (i.e. if I
want to ‘reserve’ a location then I’ll have to come up with other logic/tracking table, e.g.
having it return a value to indicate the type of reservation – currently it just returns true)
Engineers with the same order – I removed the distance check done as part of this and will
rely on the ‘queue reference’ that is used.
Slight reduction in the number of checks idle factories will do (I highly doubt this will have a
noticeable impact but thought there little harm)
Engineer logic should be significantly curtailed once there are no more locations to build.
Added spare engi logic to account for scenarios where the first engineer builds on the last
available build space leading to no further engineers being assigned – if not got low power or
mass then assign every engineer to the nearest under construction building.
Queued buildings – In addition to current tracking, I’ll now track the queued location using
tables for each location against the relevant land zone, to hopefully provide a faster lookup
option than doing a distance check when there are multiple queued buildings in a zone. A
very rough (and somewhat unreliable) comparison with the replay I was dealing with at the
time suggested this may have reduced the time required by 19% overall for this function
(although this will be somewhat offset by a slightly increased time on the tracking functions).
Reduced the extent to which building locations get refreshed.
When checking the (now deynced) replay I noticed a massive pause at one point, and for once the
function count was really high:
info: ProfilerOutput: iTick=20307: No.1=GetCurrentAndMaximumShield; TimesRun=38022; Total
Time=38022
info: ProfilerOutput: iTick=20307: No.2=IsTargetUnderShield; TimesRun=5396; Total Time=5396
info: ProfilerOutput: iTick=20307: No.3=GetDamageFromBomb; TimesRun=25; Total Time=25
i.e. despite only trying to get the damage from 25 bombs, we were checking the shielding on 38k
units!
Trying to debug every instance would be impractical so I decided to log where every 100th call to
GetCurrentAndMaximumShield was taking place, and only once the count in a tick exceeded many
thousands of entries.
Summarising this in Excel resulted in the following line counts:
mods\m28ai\lua\ai\m28building.lua(1375): in function <...er forged alliance\mods\m28ai\lua\ai\m28bui
mods\m28ai\lua\ai\m28logic.lua(508): in function `GetDamageFromBomb'
mods\m28ai\lua\ai\m28unitinfo.lua(979): in function `GetCurrentAndMaximumShield'
114
mods\m28ai\lua\ai\m28utilities.lua(54): in function `ErrorHandler'
mods\m28ai\lua\ai\m28logic.lua(291): in function `IsTargetUnderShield'
mods\m28ai\lua\ai\m28logic.lua(505): in function `GetDamageFromBomb'
An example audit trail is:
warning:
...r forged alliance\mods\m28ai\lua\ai\m28utilities.lua(54): in function `ErrorHandler'
warning:
...er forged alliance\mods\m28ai\lua\ai\m28unitinfo.lua(979): in function
`GetCurrentAndMaximumShield'
warning:
...ander forged alliance\mods\m28ai\lua\ai\m28logic.lua(291): in function
`IsTargetUnderShield'
warning:
...ander forged alliance\mods\m28ai\lua\ai\m28logic.lua(505): in function
`GetDamageFromBomb'
warning:
...er forged alliance\mods\m28ai\lua\ai\m28building.lua(1375): in function <...er
forged alliance\mods\m28ai\lua\ai\m28building.lua:1088>
i.e. this is part of the ‘ConsiderLaunchingMissile’ logic.
This logic was one of the few parts taken from M27, so it looks like I need to come up with a more
efficient way of calculating things even if it is less precise.
In particular, the advantage of the current approach used for nukes is that it is far more likely to find
locations that aren’t protected by an SMD and target them. The main downside is that as unit
countrs scale it results in an exponential number of targets. E.g. if there are 100 units bunched
together of a particular category of interest, then the nuke considers firing at the location of each
one, and factors in the damage dealt (which means considering the other 100 nearby units), i.e. it
ends up doing 10k calculations. Now if there are 1k units (in the slowdown replay it was on astro
craters so it was possible to have very large quantities of units in one location) it ends up doing 1
million calculations.
This is despite having a 1 tick delay every 25 units being considered (so presumably the sheer
number of units in the aoe was causing the slowdown).
I therefore thought of introducint the following changes:
●
●
At the start of the game setup ‘alternative nuke points’ for land zones:
o If the min/max position in a land zone is more than 40 from the midpoint, then
divide the zone up into 30x30 points, i.e. each point is 30 from the midpoint/other
points.
Cycle through every zone and sort through them by likely value (based on 100% of enemy
building value and 25% of ground combat threat). Consider launching a nuke at the zone
midpoint and any alternative nuke points and getting its value.
However after thinking some more, the following approach should provide a similar result but
requires less change to the code base:
115
●
●
●
●
●
●
When deciding whether to consider a target for a nuke, check if we have already considered
somewhere within 15 of this:
o After considering a target, record the location divided by 15 (and rounded) in a table
for the X and Z values
o Only consider new locations whose value for this isnt already in the table.
Consider a max of 10 targets per tick (previously 25)
After more than 2s of calculations, reduce the threshold required to stop searching by 5%
each tick. After 10s of calculations, reduce further, and after 15s abort.
Don’t recalculate the best aoe target if we havent changed the target from the start position
(where we already do this upfront).
Another issue this highlights is that I’m checking units’ shielding despite a nuke dealing so
much damage that I don’t need to worry about shielding. I therefore want to avoid checking
for shields at all when the base damage is at least 25k.
Have a flag as part of the unit cap logic, this time for all units – if the number of units in the
game reaches 1k+, then have nuke launchers use refined targeting logic where they ignore T1
units (all kinds) and ignore T2 mobile land.
o High value aoe (such as nuke) will also now ignore T1 mobile land anyway.
I’m still not sure that this has solved the problem, but as a basic check after all these changes I
compared how long M28 would take on a 4v4 on astro rich (the replay that had the major slowdown)
with v3 M28, and v4 M28 (post these changes).
The results are below for 30m into the game (some of the ‘time= values are incorrect due to errors)
With changes
UEF 4v4:
info: ProfilerOutput: No.1=SearchForBuildableLocationsForLandOrWaterZone; TimesRun=141614;
Time=7773.6118164063
info: ProfilerOutput: No.2=DecideAndBuildUnitForFactory; TimesRun=62436; Time=6075.38671875
info: ProfilerOutput: No.3=CanBuildAtLocation; TimesRun=1196651; Time=97.941482543945
info: ProfilerOutput: No.4=ManageAllLandZones; TimesRun=27662; Time=92.679611206055
info: ProfilerOutput: No.5=ManageSpecificLandZone; TimesRun=43090; Time=75.623260498047
info: ProfilerOutput: No.6=OnConstructed; TimesRun=3627; Time=63.012977600098
info: ProfilerOutput: No.7=UpdateGrossIncomeForUnit; TimesRun=17366; Time=62.493110656738
info: ProfilerOutput: No.8=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=100634;
Time=39.733673095703
info: ProfilerOutput: No.9=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=14365;
Time=35.5087890625
info: ProfilerOutput: No.10=FilterToAvailableEngineersByTech; TimesRun=100634;
Time=29.474563598633
info: ProfilerOutput: Total time taken to get to 18001= 572.85369873047; Total time of any freezes =
49.67162322998; Longest tick time=0.38421630859375; tick ref = 15882 to 15883
116
Aeon 4v4
info: ProfilerOutput: No.1=SearchForBuildableLocationsForLandOrWaterZone; TimesRun=180185;
Time=1152122.75
info: ProfilerOutput: No.2=CanBuildAtLocation; TimesRun=1267780; Time=13714.370117188
info: ProfilerOutput: No.3=DecideAndBuildUnitForFactory; TimesRun=63457; Time=8481.966796875
info: ProfilerOutput: No.4=ManageAllLandZones; TimesRun=27304; Time=188.31784057617
info: ProfilerOutput: No.5=ManageSpecificLandZone; TimesRun=43090; Time=167.60601806641
info: ProfilerOutput: No.6=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=100633;
Time=114.56607055664
info: ProfilerOutput: No.7=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=14365;
Time=109.92969512939
info: ProfilerOutput: No.8=OnConstructed; TimesRun=4257; Time=106.64226531982
info: ProfilerOutput: No.9=UpdateGrossIncomeForUnit; TimesRun=17687; Time=106.05750274658
info: ProfilerOutput: No.10=FilterToAvailableEngineersByTech; TimesRun=100633;
Time=101.03744506836
info: ProfilerOutput: Total time taken to get to 18001= 849.73559570313; Total time of any freezes =
126.95933532715; Longest tick time=0.79705810546875; tick ref = 12341 to 12342
info: ProfilerOutput: No.18=SearchForBuildableLocationsForLandOrWaterZone; TimesRun=51735;
Time=3.0337448120117
Without changes
UEF 4v4
info: ProfilerOutput: No.1=MonitorEngineerForBlacklistLocation; TimesRun=9270;
Time=46497.78515625
info: ProfilerOutput: No.2=DecideAndBuildUnitForFactory; TimesRun=53011; Time=10219.234375
info: ProfilerOutput: No.3=CanBuildAtLocation; TimesRun=939375; Time=5361.7529296875
info: ProfilerOutput: No.4=OnConstructed; TimesRun=3686; Time=425.10430908203
info: ProfilerOutput: No.5=UpdateGrossIncomeForUnit; TimesRun=18650; Time=398.64923095703
info: ProfilerOutput: No.6=ConsiderReclaimingPower; TimesRun=116; Time=397.72705078125
info: ProfilerOutput: No.7=ManageAllLandZones; TimesRun=27275; Time=105.44129943848
info: ProfilerOutput: No.8=ManageSpecificLandZone; TimesRun=43090; Time=86.590408325195
info: ProfilerOutput: No.9=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=100634;
Time=50.129913330078
info: ProfilerOutput: No.10=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=14365;
Time=46.643600463867
117
info: ProfilerOutput: Total time taken to get to 18001= 609.38195800781; Total time of any freezes =
33.399173736572; Longest tick time=0.4432373046875; tick ref = 17671 to 17672
Aeon 4v4
info: ProfilerOutput: No.1=MonitorEngineerForBlacklistLocation; TimesRun=7235;
Time=22651.900390625
info: ProfilerOutput: No.2=CanBuildAtLocation; TimesRun=932320; Time=4223.7084960938
info: ProfilerOutput: No.3=DecideAndBuildUnitForFactory; TimesRun=34536;
Time=2551.9614257813
info: ProfilerOutput: No.4=OnConstructed; TimesRun=2979; Time=477.38296508789
info: ProfilerOutput: No.5=UpdateGrossIncomeForUnit; TimesRun=18157; Time=440.66409301758
info: ProfilerOutput: No.6=ConsiderReclaimingPower; TimesRun=67; Time=197.17919921875
info: ProfilerOutput: No.7=ManageAllLandZones; TimesRun=27076; Time=49.765731811523
info: ProfilerOutput: No.8=ManageSpecificLandZone; TimesRun=43092; Time=40.011734008789
info: ProfilerOutput: No.9=CheckIfBuildableLocationsNearPositionStillValid; TimesRun=2979;
Time=32.785472869873
info: ProfilerOutput: No.10=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=100636;
Time=22.65845489502
info: ProfilerOutput: Total time taken to get to 18001= 433.2658996582; Total time of any freezes =
23.438877105713; Longest tick time=0.32365417480469; tick ref = 9619 to 9620
So overall the changes are far from conclusive. That said, there was a bugfix in one of the functions
for searching for buildable locations so Im assuming fixing this resulted in the large increase in times
this was called. From a rough skim of function calls by tick though the calls are spread out over each
tick (e.g. it might be called 8-12 times a tick), which is good since while the times it is called in the
game is massive, the load from this should be spread out.
I therefore do a check of Africa 4v4 UEF to compare to the v1 results.
V4 gives:
info: ProfilerOutput: Total time taken to get to 6001= 52.16051864624; Total time of any freezes =
0.81795173883438; Longest tick time=0.13208770751953; tick ref = 5040 to 5041
In contrast, v1 was 67.5s, which suggests (although again the measure is unreliable) that these
changes may have helped improve performance.
2.33.3) Completing capture and repair objectives
Looking through a couple of the FAF campaign scripts, it looks like objectives to give an order are
stored in a local variable AssignedObjectives
However, they also appear to follow a consistent naming convention when stored in ScenarioInfo,
e.g.:
ScenarioInfo.M1P1
118
For the first primary objective for the first map size
ScenarioInfo.M2P1 for the second map size’s first primary objective
Etc.
This records a table containing information on the objective. For example, a capture objective for M2
(to capture the prison) is:
ScenarioInfo.M2P1 = Objectives.Capture(
'primary',
-- type
'incomplete',
-- status
OpStrings.X02_M02_OBJ_010_010, -- title
OpStrings.X02_M02_OBJ_010_020, -- description
{
FlashVisible = true,
NumRequired = 1,
Units = {ScenarioInfo.Prison},
}
)
I therefore want to check for this information rather than hardcoding map names (which might
change), which hopefully means that if other maps are setup in a similar way (they probably wont
be) then my AI would automatically help with capture missions.
The idea is that I will check for such objectives periodically (every 60s) on a campaign map, since I
don’t think Im able to do a callback for this and the overhead should be minimal.
The log for the first objective of the 2nd FAF campaign map’s 2nd mission (i.e. 2nd map expansion) to
capture a prison is:
info: - Active: true
info: - AddAreaTarget: function: 5C424F50
info: - AddProgressCallback: function: 5DBD39BC
info: - AddResultCallback: function: 5D2B6B28
info: - AddUnitTarget: function: 5C4248C0
info: - Complete: false
info: - Decal: false
info: - Decals: table: 5D355D98
info: - Description: <LOC X02_M02_OBJ_010_020>Capture the holding facility to free the Loyalist
captives.
info: - Fail: function: 2F426508
info: - IconOverrides: table: 57949960
info: - 1: table: 573C87D0 (Unit of type: xac1101, with entity id: 1048773)
info: - ManualResult: function: 273EC780
info: - NextTargetTag: 1
119
info: - OnProgress: function: 5C4A09F4
info: - OnResult: function: 2F3415E8
info: - OnUnitCaptured: function: 5DE912D0
info: - OnUnitGiven: function: 5C72B168
info: - OnUnitKilled: function: 5DE91AB0
info: - PositionUpdateThreads: table: 57949348
info: - 1: thread: 572EEC30
info: - ProgressCallbacks: table: 57949BE0
info: - ResultCallbacks: table: 57949910
info: - 1: function: 5D355398
info: - SimStartTime: 2584.1000976563
info: - Tag: Objective3
info: - Title: <LOC X02_M02_OBJ_010_010>Rescue the Loyalist POWs
info: - UnitMarkers: table: 5D355DC0
info: - VizMarkers: table: 57949550
I can see a table containing the unit xac1101 but it looks like this might be linked to iconoverrides so
I’m concerned it isn’t reliable.
The code that recordes the objective is based in:
import('/lua/ScenarioFramework.lua').Objectives
which in turn takes it from:
import("/lua/simobjectives.lua")
This actually highlights that I could have done a callback for the objective instead of my 60s cycle
approach, so I’ll look to implement callbacks instead since it feels a bit neater.
The above code file includes the function:
function Capture(Type, Complete, Title, Description, Target)
This makes use of the line:
AddObjective(Type, Complete, Title, Description, image, Target)
Where Target will be a table of units to be captured
Going through the code some more, it looks like it records Target.captured = 0 for a capture mission,
so I’ll use this to decide whether to record a capture target.
120
I’ll then update the minor land zone logic to capture any buildings in that table, so it should request
engineers to undertake the capture, but to only do this if there are no dangerous enemies in the
zone.
Civilian capture logic
Since I’m implementing capture logic it feels like a good time to similarly have M28 try and capture
civilian combat units of interest (e.g. titans on a map like big bang dry).
2.33.4) M28 vs RNG performance check at AiX 10.0
Supreme Battles 2020 mentioned RNG beat M28 15 out of 16 games (presumably at 10.0 AiX). I
therefore want to figure out why M28 is performing worse than expected (since M28 typically beats
M27 at this level).
In the games played M28 did fairly well, often being ahead much of the game and if it died it was
usually due to failing to fully shield its base before RNG’s t3 arti started firing. However the following
changes have been made to try and make M28 play better generally as it was still frequently
overflowing lots of mass.
1. Added in logic for minor land zones to build land factories (up to 2 per zone) when about to
overflow mass and at high mass income levels.
2. If there are already 2 land factories then they’ll build an experimental level unit (e.g. a land
factory).
3. Added several new high priority engineer builders for high mass scenarios.
4. Reduced BP assigned for high multipliers to reduce the scenarios where 40 engineers all try
and build something that would built faster with 20 due to less time spent for the engineers
to move out of the way.
5. Land factories shouldn’t be paused in a power stall if we have a base level of energy and
significant mass stored.
6. If the enemy base isn’t revealed yet then T3 arti should fire at the enemy start position.
7. Added in a ‘second shield’ builder if we have unshielded T3 arti or game-enders, that will
focus just on trying to shield those units.
8. Increased the accuracy of experimental builder for minor zones so they will check if the zone
itself can path to the enemy base instead of relying on if an aiBrain can.
9. ACU should now factor in AiX build rate multiplier into its decisions on whether it can afford
to get an upgrade.
10. Playing a game on Finns revenge highlighted a major flaw with my ACU initial build order, as
many of the conditions were based on gross energy/mass income, e.g. it assumed that it
would gain 2 mass per sec for each mex and not build more mexes with the ACU once this
was reached. At 10 AiX this meant it failed to build mexes, and also failed to build power,
resulting in a major power stall.
11. The upgrade logic would only consider upgrading after 2.5 minutes – I’ve reduced this to the
higher of 1m and 2.5 minutes / the AiX modifier, along with a further check where if our
team mass is at least 60/tick then the time threshold will be ignored entirely.
12. One hilarious to see but unintended consequence of some logic M28 has was to see M28
spend a while building an assault transport, only to ctrl-K it the moment it was built. I
presume this was logic it has to ctrl-K transports that aren’t of use to get the reclaim value.
Therefore, the backup ‘build any experimental if the initial experimental choice can’t be
built’ logic won’t consider building experimental transports, while experimental transports
121
will be treated the same as gunships instead of transports (to avoid the ctrl-K logic
triggering).
13. There’s a bug with FAF where RAS SACUs don’t benefit from AiX resource modifiers, so I’ve
added a basic fix in M28’s logic (for all AIs when there’s an M28AI in the game)
14. More than 1 quantum gateway should be built at high (AiX 1.5+ modifiers).
Running a few games vs RNG on different maps after all the above changes:
Finns revenge
● Sera mirror – M28 won, but very close
● Aeon Mirror – M28 won, another close game, M28 almost won early on with a nuke but RNG
compelted the SMD missile just as the nuke was about to land, before M28 slowly won with
navy despite slightly worse eco throughout
● Cybran mirror – M28 lost (although I suspect this was in part due to a bug where RAS SACUs
don’t benefit from the AiX resource boost, as M28 built a lot of them but they had little
impact on its eco).
High Noon
● UEF mirror – M28 loss – fairly close game for much of it, but late-game RNG’s T3 arti proved
superior to M28’s land experimental+novax mix.
● Aeon mirror – M28 win
● Cybran mirror – M28 win
● Seraphim mirror – M28 win (just – RNG’s Ythotha was about to kill M28’s ACU as M28’s
yolona broke through RNG’s SMD)
Setons Clutch
● UEF mirror – M28 win
Badlands
● M28 Seraphim vs RNG Cybran – M28 win
2.33.5) Another reworking of build locations
While testing the above I noticed M28 was failing to identify places as being buildable. I therefore
want to revisit the idea of tracking build locations by location so I can update properly when a unit is
built or dies rather than the current approach of just cycling through a certain number of segments in
the zone to refresh build locations (which may be having performance implications).
Currently M28 will for each zone record a list of all buildable locations by building size. I still want to
be able to easily refer to something like this so I can just specify the size of building and instantly
have a shortlist of buildable locations, but I also want to have a separate table which tracks which
locations are buildable in the first place.
One option is to have a new table, with keys that are segment X and Z values, and for each of these I
can specify for each segment the maximum building size that it permits (and not record anything if
the location isn’t buildable, to try and slightly reduce the memory).
Then when a unit construction is started (or the unit is created and hasn’t had an on construction
event run) or the unit is or destroyed, I can search 50% of the maximum build distance (which will be
that of a Czar) + the unit’s size radius, and update every segment to see if it is still buildable for the
largest size – when a unit is built, I only need to consider the segment properly if its largest size
122
brings it into conflict with the unit just built. When a unit is destroyed, then I need to consider the all
segments, and check from the size above from the previously recorded highest value onwards.
To then have this link into the table of buildable locations by size, I could change the buildable
locations by size to also use [x] and [z] segment keys, and then update this based on if locations have
been added/removed for a particular size.
I therefore rewrite (as a separate branch in case it ends up not working or far worse) to do this,
involving a compelte rewrite of some of the functions to make use of the new referencing system,
and a lot of time debugging to figure out why parts of the logic wouldn’t work.
As part of this, I needed to add a table recording destroyed buildings, since I’m unable to do a forked
thread from the onunitdeath event without it causing an error. Therefore when a unit dies I’ll record
the blueprint and time, and every tick I check the table and update for any entries more than 3.9s old
(since I need to wait until the building destruction animation has completed).
Experimentals under construction
The testing also highlighted another issue – the built in function CanBuildStructureAt doesn’t take
into account mobile experimentals under construction – e.g. it still thinks a PGen can be built in the
location a GC is being constructed.
I therefore see the following as a potential solution:
●
●
●
●
●
●
When a mobile experimental construction is started for the first time, Cycle through every
segment covered by the experimental footprint, and if it doesn’t have a blacklist location
recorded for it, record the blacklist
Record the temporary blacklist entry against the mobile experimental whose construction
was started.
Once the blacklist is recorded, run the normal check for if the nearby buildable locations are
still valid, including the blacklist (only include blacklist if it’s a mobile experimental).
When an experimental dies or construction completes, check if it has this entry, and if so
remove from the blacklist and the experimental.
As a backup, whenever recording a blacklist entry for an experimental, clear the entry after 4
minutes if the experimental no longer exists.
Normal blacklist entries should also be cleared after 8 minutes in case it was due to a
temporary issue that has now resolved.
Profiling new construction logic
Checking how long it takes to run on the 4v4 Astro scenario (see earlier for the times with the old
method):
4v4 UEF with changes:
info: ProfilerOutput: No.1=CanBuildAtLocation; TimesRun=10222327; Time=[invalid – error in code]
info: ProfilerOutput: No.2=DecideAndBuildUnitForFactory; TimesRun=88892; Time=4865.056640625
info: ProfilerOutput: No.3=ManageAllLandZones; TimesRun=26943; Time=208.82913208008
info: ProfilerOutput: No.4=ManageSpecificLandZone; TimesRun=43090; Time=184.24096679688
info: ProfilerOutput: No.5=OnConstructed; TimesRun=3760; Time=160.69473266602
info: ProfilerOutput: No.6=UpdateGrossIncomeForUnit; TimesRun=17338; Time=160.17340087891
123
info: ProfilerOutput: No.7=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=100634;
Time=117.87591552734
info: ProfilerOutput: No.8=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=14365;
Time=111.26459503174
info: ProfilerOutput: No.9=FilterToAvailableEngineersByTech; TimesRun=100634;
Time=97.935234069824
info: ProfilerOutput: No.10=CheckIfBuildableLocationsNearPositionStillValid; TimesRun=1540;
Time=26.161964416504
info: ProfilerOutput: Total time taken to get to 18001= 1279.2435302734; Total time of any freezes =
263.53198242188; Longest tick time=0.95118713378906; tick ref = 9 to 10
Unfortunately instead of showing an improvement to the time taken this shows a massive worsening
of the time.
The main reason is the amount of times a location is checked to see if its buildable – previously this
was 1.2m times in 30m. After these changes (which do at least have the benefit of being more
accurate) this check is being done 10.2m times.
Time spent managing land zones has also increased dramatically from 97s to 208s.
The time taken for an engineer’s assignment has also increased dramatically, from 40s to 111s, which
accounts for a significant part of this increase.
I suspect that one factor at play here is that there are more options to choose from when picking the
best build location, which is increasing the calculation time.
One potential option to mitigate this might be to localise engineer build logic/decisions on where to
build a unit, by searching outwards from a centrepoint and including all buildable locations.
However, there’s no guaranatee this would reduce times since late game all places close to the
centre of the land zone would have been built on meaning it would take much longer to reach
buildable locations.
Another option would be to stop after getting 50 valid build locations. This runs the risk though of
building far away from the centre of the land zone if all build locations are grouped in one place,
limiting the effectiveness of the logic of choosing the best location.
Yet another option could be to reduce segment size and limit the buildable options that way.
Another variant could be to get the first 30 build locations, and to then randomly pick a further 20
locations (ignoring any duplicate random numbers), so most of the time there is a reasonable variety
in the build location options. However due to the way Im now tracking build locations I don’t think
this random option would be that easy to do as I’d have to cycle through and discard lots of invalid
zones.
A variation that I think is more feasible is to work out the number of available entries, and if its more
than 50 then only include the nth valid entry in the potential locations.
For now I’ll try restricting the options to choose from to the first 40 by taking the nth entry.
124
Unfortunately this doesn’t have much of an impact – e.g. for the time taken to get to tick 13821 (I
couldn’t be bothered with waiting to the end) it decreased from to 751s to 736s, with the detailed
log being:
info: ProfilerOutput: No.1=CanBuildAtLocation; TimesRun=8002101; Time=[N/A – Bug]
info: ProfilerOutput: No.2=DecideAndBuildUnitForFactory; TimesRun=73448; Time=[N/A – bug]
info: ProfilerOutput: No.3=OnConstructed; TimesRun=3044; Time=160.5387878418
info: ProfilerOutput: No.4=UpdateGrossIncomeForUnit; TimesRun=13217; Time=160.16943359375
info: ProfilerOutput: No.5=ManageAllLandZones; TimesRun=20736; Time=123.68119049072
info: ProfilerOutput: No.6=ManageSpecificLandZone; TimesRun=33059; Time=110.50128936768
info: ProfilerOutput: No.7=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=77227;
Time=68.629089355469
info: ProfilerOutput: No.8=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=11022;
Time=63.691535949707
info: ProfilerOutput: No.9=FilterToAvailableEngineersByTech; TimesRun=77227;
Time=55.483390808105
info: ProfilerOutput: No.10=CheckIfBuildableLocationsNearPositionStillValid; TimesRun=1156;
Time=20.372230529785
One potential way I could reduce the time required is to not reconsider every size for every segment,
but only those sizes I expect may have changed.
For example, suppose a 2x2 PGen gets destroyed. It’s now possible that a 3x3 area centred on that
pgen can support another pgen (so should be reconsidered). Meanwhile it’s possible that any of the
locations in a 25x25 area centred around that pgen location could now support building a czar where
previously they couldn’t.
In addition, if the max size of the pgen (when refreshed) is only say an 8x8 building but not a 10x10
building, then that means only locations in a hollow box with the outer being a 25x25 area and the
inner box being a 10x10 area might be able to support a czar.
However, due to how the logic works trying to add this sort of exclusion approach is non-trivial.
I could also just limit the search to cover 8x8 buildings (e.g. T3 pgens and smaller) instead of czar
sized buildings, and rely on the separate functionality for checking which segments are buildable
over time (and when there are no more build locations) to cover this.
For example previously if a land factory died, it would search an area 4 (land fac radius) + 12 (Czar
radius) = 16x16 = 256
If I changed to cover 8x8 buildings, then it would only search an area 8x8 = 64, i.e. 25% as many
entries.
Changing the segment size to be 2x2 on 10km and larger maps as a minimum would apply another
25% reduction.
125
Another change was not running the logic to check for buildable locations when a mex or hydro is
built or dies.
These still don’t have the desired effect. However after fixing a couple of bugs my profiling logs are a
bit more meaningful, with the following being the position after these changes (at an earlier tick):
info: ProfilerOutput: No.1=ManageAllLandZones; TimesRun=17629; Time=77.556365966797
info: ProfilerOutput: No.2=ManageSpecificLandZone; TimesRun=27851; Time=67.93505859375
info: ProfilerOutput: No.3=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=65075;
Time=36.946632385254
info: ProfilerOutput: No.4=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=9285;
Time=33.541702270508
info: ProfilerOutput: No.5=FilterToAvailableEngineersByTech; TimesRun=65075;
Time=30.062580108643
info: ProfilerOutput: No.6=RecordGroundThreatForLandZone; TimesRun=27851;
Time=11.213144302368
info: ProfilerOutput: No.7=ManageCombatUnitsInLandZone; TimesRun=9644;
Time=10.174573898315
info: ProfilerOutput: No.8=GetCombatThreatRating; TimesRun=1034721; Time=10.168375015259
info: ProfilerOutput: No.9=CheckIfBuildableLocationsNearPositionStillValid; TimesRun=497;
Time=6.7498073577881
info: ProfilerOutput: No.10=CheckIfSegmentsStillBuildable; TimesRun=497; Time=6.6457366943359
info: ProfilerOutput: Total time taken to get to 11651= 544.40576171875; Total time of any freezes =
69.678382873535; Longest tick time=0.95945167541504; tick ref = 9 to 10
This is including the initial map start time.
This highlights that by far the biggest contribution towards slowdown is the
FilterToAvailableEngineersByTech function, since this is run as part of all of functions 1-4 that are
taking the longest time, accounting for 30/33 of the core land zone engineer assignment logic.
I suspect this could be due to large land zones on the map in question (astro craters rich) resulting in
a large group of enemy units being considered. Each friendly engineer will check how far away it is
from each enemy unit when deciding if an enemy unit is range.
One simplification I can make is to check how close the engineer is to the zone midpoint, and how
close the closest enemy unit is to the midpoint. If the distance between these two is more than the
threshold for the engineer acting based on nearby enemies, then it can ignore it.
A second potential simplification is to switch methods to a ‘getunitsaroundpoint’ approach where
there are at least 10 enemies, since I suspect this approach may be quicker as it tells me if there are
enemies within the distance without needing to distance check each of them.
126
Testing the second first (since it’s much easier to do) results in a dramtic improvement in time taken
early game for this function, so I wont bother with the first option.
I also notice that my emergency PD logic gets the nearest enemy unit twice (once for each builder) so
I add an optimisation to not get it the second time if I’ve already got it the first time.
Running a new 4v4 UEF mirror on astro rich gives the following results:
info: ProfilerOutput: No.1=ManageAllLandZones; TimesRun=27046; Time=89.451591491699
info: ProfilerOutput: No.2=ManageSpecificLandZone; TimesRun=43091; Time=70.2021484375
info: ProfilerOutput: No.3=RecordGroundThreatForLandZone; TimesRun=43091;
Time=20.336393356323
info: ProfilerOutput: No.4=GetCombatThreatRating; TimesRun=1405422; Time=17.022764205933
info: ProfilerOutput: No.5=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=100635;
Time=17.020629882813
info: ProfilerOutput: No.6=ManageCombatUnitsInLandZone; TimesRun=14144;
Time=15.054508209229
info: ProfilerOutput: No.7=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=14365;
Time=13.736473083496
info: ProfilerOutput: No.8=CheckIfBuildableLocationsNearPositionStillValid; TimesRun=813;
Time=10.491506576538
info: ProfilerOutput: No.9=CheckIfSegmentsStillBuildable; TimesRun=813; Time=10.340942382813
info: ProfilerOutput: No.10=CanBuildAtLocation; TimesRun=3879070; Time=9.3387413024902
info: ProfilerOutput: Total time taken to get to 18001= 783.64086914063; Total time of any freezes =
41.950523376465; Longest tick time=1.0018310546875; tick ref = 9 to 10
The Aeon 4v4 results are:
info: ProfilerOutput: No.1=ManageAllLandZones; TimesRun=26959; Time=107.51538848877
info: ProfilerOutput: No.2=ManageSpecificLandZone; TimesRun=43090; Time=85.687393188477
info: ProfilerOutput: No.3=RecordGroundThreatForLandZone; TimesRun=43090;
Time=23.963935852051
info: ProfilerOutput: No.4=GetCombatThreatRating; TimesRun=1644976; Time=19.987089157104
info: ProfilerOutput: No.5=ManageCombatUnitsInLandZone; TimesRun=14362;
Time=18.299633026123
info: ProfilerOutput: No.6=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=100634;
Time=17.607286453247
info: ProfilerOutput: No.7=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=14365;
Time=14.353818893433
127
info: ProfilerOutput: No.8=CheckIfBuildableLocationsNearPositionStillValid; TimesRun=825;
Time=10.983692169189
info: ProfilerOutput: No.9=CheckIfSegmentsStillBuildable; TimesRun=825; Time=10.845937728882
info: ProfilerOutput: No.10=CanBuildAtLocation; TimesRun=4071099; Time=9.4974784851074
info: ProfilerOutput: Total time taken to get to 18001= 927.14483642578; Total time of any freezes =
103.59169006348; Longest tick time=0.92333221435547; tick ref = 9 to 10
So unfortunately after lots of work optimising I’m still at a worse off position than before. However
the appeal of more accurate build location logic and the potential for this to help late-game
performance even if early game performance is worse means I’ll stick with this approach rather than
revert back.
However, later on during testing I realised there was an error with the ‘Getunitsaroundpoint’
approach I used where it would never actually trigger (hence why it was so much faster). After fixing
this, checking part-way through the replay, at tick 11301, gave the following results for the function
relating to this.
Before:
info: ProfilerOutput: No.18=FilterToAvailableEngineersByTech; TimesRun=63115;
Time=1.8183212280273
After:
info: ProfilerOutput: No.13=FilterToAvailableEngineersByTech; TimesRun=63116;
Time=2.9245834350586
So while the time has increased significantly, it looks like it is significantly faster than before, as the
log for the same time previously was:
info: ProfilerOutput: No.9=FilterToAvailableEngineersByTech; TimesRun=63116;
Time=32.177177429199
Note that some variance is expected with the results since these are different games/replays, but it
still gives a rough idea as to how long the function is taking.
Running on Africa 4v4, by 10m:
Including initial setup: info: ProfilerOutput: Total time taken to get to 6001= 76.245834350586; Total
time of any freezes = 10.55298614502; Longest tick time=4.8122062683105; tick ref = 9 to 10
I forgot to disable profiling for the map setup (previous logs had excluded map setup time).
However the time to setup the map with the core function was 10.3s, i.e. the approximate total time
would have been less than 66s with 0.2s of freezes.
2.33.6) Acknowledgements
●
●
Hdt80bro – helping me find a better way to confirm if a map is a campaign map, and
clarifying how to hook orders such as IssueGuard
Grandpa Sawyer – helping me debug an issue with the base AI taking control of my units
(including the IssueFactoryAssist() order being the one I needed to hook)
128
●
●
●
Jip – Helping with the same bug as the above, highlighting I was hooking a file that only
existed on FAF develop (hence why it wasn’t doing anything for non-FAF develop)
Fearghal – replay highlighting major late game slowdown for M28 on Astro Craters Rich
Wingflier - replay showing M28 losing at 1.5 AiX
2.34) V5 Changes
2.34.1) Bugfixes
1. There were some issues on campaign maps due to the difference between the map area and
the playable area. I’ve therefore refined the logic for getting a location within map bounds
so for each usage of this function I have specified whether it should use the playable area
currently, or the map playable area if expanded. I.e. for initial recording of zones and
pathability I want to do the latter, for deciding where to send a unit right now I want to do
the former.
2. I’ll also ignore air units targeting zones whose midpoint is outside the current playable area.
3. There was an issue with the new building logic that could result in M28 thinking there was
nowhere to build when there was.
4. If an air factory HQ is destroyed and the team has no HQ left, T1 air factory should be built as
a top priority. Similarly for land factories.
5. Reworded a couple of variable references that used M27 as a prefix instead of M28
6. The playable area wasn’t always being identified on a map expansion. I therefore will use
the string rect (which sends a mission code) as an override for the playable area.
7. There was a bug where some of the air logic used the wrong reference for a zone leading to
the zone never being considered.
8. M28 was meant to only build more power if its net energy income was below a certain
threshold, but a bug meant it would always want more power.
9. When deciding whether a unit is in a land or water zone for assignment, if the unit can path
in water then it will first check if the segment is assigned to a water zone, and if not will
check if it is in a land zone. In addition, only mobile land units will be treated as being
expected to have a pathable nearby land zone (previously all units would, leading to error
messages appearing if a submarine wasn’t in a land zone).
10. When calculating the damage done by an aoe effect (e.g. T3 arti), there was a bug relating to
how under construction units were taken into account (meaning e.g. experimental air units
being constructed wouldn’t be factored in properly).
11. RAS upgrades weren’t being taken into account by M28 when calculating how much mass
and energy it was generating (affecting various thresholds for builders).
12. RAS SACUs aren’t treated as having an upgrade the moment they are built meaning M28 was
failing to calculate its income properly. However, the blurprint lists put the upgrades they are
set to receive, so I was able to refrence this to fix the issue.
13. RAS SACUs were meant to assist under construction experimentals at high mass values but
instead assisted each other.
14. RAS SACU orders should only be cleared (when they are to be idle) if they are
repairing/building/guarding, to avoid scenarios where they are cleared before exiting the
gateway.
15. M28’s calculation of its gross mass income should now include a very rough approximation
for mass storage’s adjacency bonus.
2.34.2) Misc changes
129
1. Added a low priority air staging builder for minor zones with no nearby enemies if we have a
shortage of air staging facilities and the zone has no nearby enemies and has radar coverage.
2. Increased the cap on low priority gunship builder for campaign and high mass non-campaign
scenarios.
3. Added ‘T2 radar creep’ logic for 20km+ maps
4. Air staging should no longer be ctrl-kd when close to the unit cap (due to the risk of ending
up with low fuel units that do nothing)
5. Unit cap thresholds for resetting being close to the unit cap are much higher at the
‘worse’/closer to threshold results.
6. Low fuel units should be ctrl-kd if we are close to the unit cap and the unit is close to the
rally point, and lacks an air staging to go to.
7. If there are no land zones adjacent to water that need support, then a land zone that is
closer to the enemy base than the current zone should be picked.
8. The ‘factory is idle’ warning message should not show if the M28 owner is close to the unit
cap.
9. Reduced the AA threshold at which gunships will consider attacking for campaign maps since
they can probably operate more safely further away from base compared to if fighting a
human player.
10. Made it less likely to want more power in high net energy scenarios where we are close to
the unit cap.
11. Added a cap of 50 on the number of RAS SACUs to be built.
2.34.3) Campaign - Repair damaged building objective
After checking logs for the FAF mission 1 objective to repair the shield there’s little information I can
get other than that there is an objective involving a unit.
I’ll therefore assume there is a repair objective if there is a table which only contains injured allied
units.
I also add in logic to both minor land zones and core zones (previously capture logic was only in
minor land zones), since in this case the shield is part of the initial base starting position.
2.34.4) Protect civilian objectives
●
●
If there is a ‘protect’ objective, then include such units in a table of priority units to protect.
Have this table included when looking for priority targets for gunships to protect.
As a reference, the following were logs for some of the objectives on FAF mission 1, e.g. the objective
to protect civilians:
info: Have a mission, Title=<LOC X01_M02_OBJ_010_010>Defend the Civilians at Seabring;
Description=<LOC X01_M02_OBJ_010_020>At least 50% of the civilian structures must survive.;
Target.captured=nil; reprs of Target=
info: - Image: /textures/ui/common/faction_icon-lg/uef_ico.dds
info: - NumRequired: 12
info: - PercentProgress: true
info: - ShowFaction: UEF
130
info: - Units: table: 65F0A550
info: - 10: table: 51AD4B40 (Unit of type: uec1101, with entity id: 4194313)
info: - 11: table: 651F3D48 (Unit of type: uec1501, with entity id: 4194314)
info: - 12: table: 538D0D20 (Unit of type: uec1201, with entity id: 4194315)
info: - 13: table: 5DB7FD20 (Unit of type: uec1101, with entity id: 4194316)
info: - 14: table: 6593A848 (Unit of type: uec1301, with entity id: 4194317)
info: - 15: table: 57AC2BE0 (Unit of type: uec1501, with entity id: 4194318)
info: - 16: table: 54194780 (Unit of type: uec1101, with entity id: 4194319)
info: - 17: table: 52930168 (Unit of type: uec1101, with entity id: 4194320)
info: - 18: table: 2F932578 (Unit of type: uec1101, with entity id: 4194321)
info: - 19: table: 5BEEC870 (Unit of type: uec1401, with entity id: 4194322)
info: - 1: table: 65F0AC30 (Unit of type: uec1401, with entity id: 4194304)
info: - 20: table: 674DD2D0 (Unit of type: uec1101, with entity id: 4194323)
info: - 21: table: 2F8132F8 (Unit of type: uec1501, with entity id: 4194324)
info: - 22: table: 62E18ED8 (Unit of type: uec1101, with entity id: 4194325)
info: - 23: table: 62E18320 (Unit of type: uec1101, with entity id: 4194326)
info: - 2: table: 576EA168 (Unit of type: uec1101, with entity id: 4194305)
info: - 3: table: 576EA5F0 (Unit of type: uec1101, with entity id: 4194306)
info: - 4: table: 5D969E60 (Unit of type: uec1501, with entity id: 4194307)
info: - 5: table: 5CBA0E88 (Unit of type: uec1101, with entity id: 4194308)
info: - 6: table: 53F68460 (Unit of type: uec1401, with entity id: 4194309)
info: - 7: table: 53F68230 (Unit of type: uec1101, with entity id: 4194310)
info: - 8: table: 65ADF4D8 (Unit of type: uec1201, with entity id: 4194311)
info: - 9: table: 5BE0FE88 (Unit of type: uec1101, with entity id: 4194312)
IsAlly shows as true for the unit brain owner.
Log for later protect civilians objective:
info: Have a mission, Title=<LOC X01_M02_OBJ_020_010>Save the Civilian Outpost;
Description=<LOC X01_M02_OBJ_020_020>Protect at least 50% of the civilian structures in the
outpost to the east.; Target.captured=nil; reprs of Target=
info: - Image: /textures/ui/common/faction_icon-lg/uef_ico.dds
info: - NumRequired: 21
info: - PercentProgress: true
131
info: - ShowFaction: UEF
2.34.5) Campaign – Units to destroy
E.g. mission 3 adds Seraphim bombers to destroy that the player can’t see. In case this means that
the bombers won’t be known by M28 to be there, if a unit is included as part of an objective.
This also highlighted an issue with my T3 arti targeting logic – M28 built a mavor, but it wouldn’t
target where the experimental bombers were being built, despite knowing they were there, because
the logic for determining the damage from the T3 arti would only take into account currently visible
units. I’ve therefore adjusted so it will take into account other (unseen) units as well.
2.34.6) Campaign – naval build locations that are out of bounds
On mission 3 I was puzzled why M28 wasn’t building a naval factory, and after a long time debugging
to confirm engineers were being given the order to build a factory and not having that order cleared,
I realised it was because the water zone chosen is mostly out of bounds at the start of the game:
Meaning the midpoint of that zone would be invalid.
I therefore want to adjust the water zone midpoint so that if a water zone midpoint is outside the
playable area, but the min or max segments are in the playable area, then I want to adjust the
midpoint to be in the playable area.
Checking the zone (with the midpoint drawn in red this time) confirms it is now in a valid location:
132
2.34.7) Unit cap adjustments
●
●
●
Don’t build a unit from a factory if we already have 50 of that unit (e.g. this is intended to
cover asfs, gunships, SACUs, engineers) if the 2nd worst unit cap threshold has been reached.
Don’t try and build T1 units with engineers if are near the unit cap (i.e. including energy and
mass storage).
If are in campaign and close to the unit cap, then try building a game-ender.
After making these changes, M28 went from needing more than 2 hours to eke out a victory on FAF
mission 3 (relying significantly on the Rhiza teammate) to 1 hour 22m.
●
●
●
One thing games on the missions highlighted was the need for a higher AI unit cap – even
with its logic to ctrl-K units, the 500 unit cap is just too low to handle.
I therefore want to do something similar to the AiX logic where unit caps can be specified
(since it looks like in campaign the option is hidden).
However, from playing the original SC campaign on FAF with an uncapped unit mod enabled,
and still suffering from lower unit caps, I’m assuming that there is logic to reset this unit cap
(e.g. original SupCom will change the unit cap by mission). I‘ll therefore refresh the unit cap
every time the map size changes and every time a new objective is added.
Acknowledgements:
●
Azraeelian Angel – Replay where there was a desync
2.35) V6 - Replay adjustments
This version was mostly a case of making imrpovements for issues I saw watching some human vs
M28AI replays Azraeelian Angel sent me, including replays 20058912 and 20049298.
1. If a T2+ unit or ACU gets within range of a skirmisher that would be doing an
attack-move, the skirmisher should instead do an issueattack at that unit.
2. Indirect fire units should still attack the enemy if they outrange the enemy direct fire
units and have significantly more threat than the enemy indirect fire units.
133
3. MMLs should now give off a small threat level for combat purposes and so be more likely
to attack if they outrange the enemy.
4. When deciding whether to attack with outranged units, greater consideration should be
given to nearby enemy zone threats.
5. If we have a unit with an indirect fire aoe attack and the enemy is in range of it but we
have temporarily lost vision of it, and the unit hasn’t fired recently, then a ground fire
attack should be used (assuming the unit isn’t moving).
6. Azraeelian Angel highlighted a number of games desyncing where the common factor
was M27 and M28 were both on the same team. I’ve therefore added a warning
message if there are 2+ players in the game with both M27 and M28 AI.
7. If M28’s ACU is hit by a TML while upgrading, it should cancel the upgrade and move
away from the enemy TML.
8. T2 mexes and ACUs shouldn’t be treated as safe to upgrade if the enemy has TML in
range of them and they don’t have TMD coverage.
9. Cloaked enemy ACU – If the enemy has a cloaked ACU or SACU, and we have a gunship
threat of at least 1.5k, then the gunship should request a priority air scout be assigned to
it (so the air scout can reveal the omni).
10. If have at least 100 mass per tick or if lower 40 per M28 brain, SACUs should switch from
assisting the quantum gateway to assisting the other units (previously the threshold was
80 per M28 brain)
11. Increased the amount of AA to be built for core land zones generally, and if far behind on
air.
12. Increased the amount of AA to be built for minor zones based on the number of mexes in
that zone.
13. Switched most of the logic for working out if we don’t want any more MAA to be based
of MAA threat (previously it was based of all groundAA threat, i.e. including SAMs)
14. Factored in the number of M28 brains to the decision on whether to build a gameender
15. If there is a gameender underconstruction then engineers shouldn’t be assigned to build
another gameender.
16. Mexes should be treated as safe to upgrade if they have survived at least 5 minutes and
there are no enemies in the zone.
17. Island factories should try and get a basic level of threat even if the enemy isn’t on the
island yet.
18. ACU should be less likely to upgrade if at core base unless it is close to the midpoint, if it
is low health.
19. Slightly more AirAA should be built when we already have a base gunship threat of
approximately 3 T3 gunships.
2.35.1) Bug fixes
●
●
Fixed a bug that prevented size 1 buildings (and hence T2 PD since T2 PD would want 1 T1 pd
built first) being built
Fixed a bug where Air experimentals wouldn’t be assisted properly when under construction.
2.35.2) Having sniperbots avoid fatboys
While doing a sandbox testing of one of the changes (where I cheated in a couple of fatboys to stop
M28 overruning me as a human) I noticed M28 would suicide sniperbots into the fatboy.
Investigating further, the reason was the land zones are so small that the fatboy in a non-adjacent
134
zone can hit a zone (i.e. 2 zones away) – in the below example, the sniperbot was in the pink area,
the fatboy in the darkblue main spawn area:
My planned solution to this is:
●
●
●
●
●
Have an enemy fatboy monitor thread
Whenever an enemy unit with a DF range exceeding 80 is detected, it is added to the fatboy
monitor thread
Against each unit, it records the last zone that it was in
Whenever this zone changes, all zones adjacent to adjacent land zones get the unit recorded
in a table for that zone of ‘nearby enemy fatboy threats’
When calculating the enemy combat threat for a zone, the threat of the ‘nearby fatboy’ table
is added, and we aren’t treated as outranging the enemy unless our best range exceeds 100
(i.e. we have a fatboy of our own).
2.35.3) Pre-release checks:
135
●
●
●
●
●
Forbidden pass vs RNG (UEF vs Aeon) – Win in 56m10 - RNG almost managed to kill M28’s
ACU with a GC when it looked like the game was over (in M28’s favour) by sneaking it up
through the water; after that it was M28’s game.
Setons 4v4 vs RNG – I stopped before the replay ended to make some minor improvements
but it looked a likely M28 win (M28 had 2.5:1 eco and had beaten 2 of RNG’s bases)
Twin Rivers 2v2 vs RNG – RNG Win (M28 couldn’t handle RNG’s early Aeon guncoms due to a
bug preventing T2 PD from building)
Burial Mounds 1v1 vs RNG – RNG Win (there was a bug with air experimentals being built
which led M28 to have an air experimental part-complete blocking build locations and not
being assisted further, crippling its eco – I’ve fixed one of the two issues this highlighted, and
will see if the second (power generators trying to be built ontop of the air experimental)
recurs or was fixed as a consequence).
Africa 4v4 Time taken (excl first 2 secs): info: ProfilerOutput: Total time taken to get to 6001=
59.426670074463; Total time of any freezes = 0.86589473485947; Longest tick
time=0.17135620117188; tick ref = 5520 to 5521
2.35.4) Acknowledgements
●
●
Azraeelian Angel – Multiple player vs M28 replays
Relent0r – highlighting M28’s ACU could be killed by a single TML firing multiple missiles, and
a replay where M28’s ACU would upgrade while taking damage and almost dead.
2.36) V7 – Replay and campaign issues
2.36.1) Bug fixes
1. If M28 got enough eco to justify building a game ender there was a bug which would break
both its engineer logic and land unit management for the majority of land units.
2. ASFs should no longer try and ‘protect’ a novax satellite from enemy air units.
3. Resolved an issue with identifying the nearest plateau/land zone to a location to record
overrides for locations where the segment position’s ‘plateau’ from navutils is different to
the recorded plateau (which would then cause an error when trying to identify the
plateau/zone details).
4. Corrected typo with the ‘disable M27 mod’ that referred to M28 instead of M27.
2.36.2) Misc changes
1. When deciding whether to get more factories, the average number of factories per M28
player should be considered (previously the total number of factories for all M28 brains
would be considered)
2. Made core zone localised mex upgrade logic more likely to trigger sooner
3. Don’t get omni unless have at least 2 T3 mexes in the zone (or 1 T3 mex and no T2 or T1)
4. Similarly don’t get preemptive SMD unless have 2 T3 mexes (or 1 T3 mex and no T2 or T1)
5. SMD shouldn’t be built for minor zones unless the enemy has a nuke launcher or the value is
very high.
6. AirAA should be less likely to attack enemy air units in a zone adjacent to a priority unit to
defend where there is significant groundAA in that zone.
7. ACU should be more likely to run if the enemy has T2+ PD is nearby.
8. Changed monkeylord to be treated as having a range of 30 instead of 64 if it’s owned by an
M28AI.
136
9. Added higher priority MML builder that takes priority over the ‘initial couple of tanks’ if an
adjacent zone needs indirect support and we don’t have many MML.
2.36.3) Basic pre-release check
Africa vs RNG (4v4): Win in 47m
2.36.4) Acknowledgements
●
●
●
QAI300 – Replay highlighting bug with land units not moving (caused by the logic that
triggers when engineers are to build a game ender).
Radde – 4 human v 4 M28AI replay highlighting mass overflow issues at the T2-T3 stage
Azraeelian Angel – More human vs AI replays
2.37) V8 More replay and campaign improvements
2.37.1) Changes based on replay 20105758
1. ACU should no longer try to ‘expand’/move to a land zone that is a core base (e.g. on
teamgames it could sometimes travel to an allied M28 base to claim mexes, wasting valuable
time)
2. Core base engineers should prioritise unbuilt mex zones that aren’t core base zones
(previously they could end up traveling to an adjacent core zone owned by an M28
teammate instead of getting mexes early on).
3. If the ACU is near a T2 PD it should treat the location as safe to get an upgrade even if there
are enemy threats.
4. Changed the initial high priority tank builder to be a team based test (since with more
teammates there should be more ACUs to cover potential initial attacks).
5. Reduced the number of high priority units to be built when a factory reaches T2, to be
reduced by the number of active M28 brains (i.e. so not as many are built as a high priority in
large teamgames).
6. Slightly increased the amount of indirect fire units built as normal
7. Lowered thresholds for T2 arti to be built in response to enemy sniperbots.
8. T2 arti should be built in response to an enemy firebase that is threatening a core base.
2.37.2) Changes based on replay 20106323
9. When considering if enemy threats should be included from adjacent zones, only those that
are 95%+ complete should be included
10. When we have far more threat than the enemy in a zone, and a very high threat (e.g.
equivalent to 5+ percies), units should move instead of attack-moving – in the hope that e.g.
a monkeylord won’t attack-move enemy engineers and land scouts.
11. When deciding to retreat units from a zone, M28 would only retreat units actually in that
zone. However, this would lead to an issue where that zone was taking control of units in
adjacent zones (due to being a higher priority), causing those units to not receive any orders
until they entered the current zone (which in some cases might not happen), resulting in
units suiciding into the enemy. I’ll now try it where in the retreat scenario the units have
their priority cleared so the next highest adjacent zone takes control of them.
12. Where multiple engineers are assigned to build a mex in a zone, they should each try and
build a different mex location.
137
13. Adjusted the start land zone mex assignment to reduce the search radius for further away
mexes where a significant number of mexes are already assigned – the issue is illustrated by
how zones were assigned on this map originally:
The initial land zone assignment for start positions is shown in black below, with blue being the
enlarged range after assigning nearby mexes:
138
This would lead to strange behaviour as the grey ACU 2nd from the top-left would treat a massive
area as it’s ‘core’ zone, despite some of that being almost in the middle of the map. After tweaking
the values (to reduce the search distance for additional mexes after the initial assignments to be
based on the total number of mexes above 2 in the core zone), it gives:
139
Which is much closer to what I want.
To check this doesn’t lead to wildly different results on other maps, I check theta passage (where it’s
important the hydro is seen as part of the same zone), and this still appears consistent:
140
2.37.3) Changes based on replay 20106849
14. Fixed a bug with the resource sharing logic that meant resources wouldn’t get shared
properly which in turn meant M28 would think it was mass stalling if any one of the AI had 0
mass.
15. Added redundancies to the mass stall manager so if at least 2k mass is stored then it will
start unpausing, even if it thinks it has low mass.
2.37.4) Changes from testing on M5 and M6 of the FA campaign
16. Reclaim attempts should no longer be made on reclaim with a different terrain pathing result
if it is outside the engineer’s build range and the engineer’s build range and size is less than
the reclaim segment size, to reduce the risk of engineers stuck trying to reclaim something
just outside their range.
17. (Note for FA Campaign mission 5 I decided not to try and have the AI move Brackman and
leave it to the player, due to the risk of the AI suiciding the megalith)
141
18. Mission 5 highlighted an issue with invulnerable buildings such as the QAI mainframe and
cybran network nodes being treated as enemies, leaving to M28 sending its gunships and
armies to attack these locations (and do nothing). I’ll therefore ignore tracking units that are
flagged as not being able to do damage or be killed by the campaign script.
o However, this caused an issue as it turns out a lot of units are flagged as not being
able to be killed when initially created, so I’ll only exclude units that are flagged as
not being able to take damage; not being able to be killed; and are a civilian
structure, and then only if it is a campaign map.
19. Mission 6 highlighted how you can start off with no ACU, and M28 would just move its SACU
around not building anything. I therefore want to search for SACUs if after a while of
searching I have no ACU, and to then increase to consider engineers if there are no SACUs,
with the first such unit then being treated as an ACU.
20. Amphibious units should only move (as a rally point) to the water zone midpoint if we have
more antinavy threat than the enemy in that water zone, otherwise they should move back
to the nearest friendly base.
21. When deciding whether to attack or retreat from a water zone, amphibious units should also
take into account nearby units (resolving an issue I saw on mission 6 where 4 GCs set off to
the enemy island base, but as soon as 2 of them passed from 1 water zone to the adjacent
one, it thought there weren’t enough GCs to take the island as it only had 2 damaged GCs,
and ended up retreating all 4 GCs).
2.37.5) 20131411
22. Added high priority land factory HQ upgrade at high gross mass incomes (but with a lower
threshold than before for large teamgames), in the hope that with good eco M28 will get to
T3 and hence experimentals sooner than before.
23. The separate ‘build a game ender’ logic for very high resource values should switch to
building land experimentals if we have fewer than 5 land experimentals per game-ender.
24. Adjusted the decision making on what experimental to build so if we have a game-ender we
want a greater number of fatboys (for UEF).
25. Fixed a bug where certain engineer experimental construction actions weren’t being tracked.
26. Orders to engineers to construct an experimental should be aborted if we have one under
construction in a nearby zone already and don’t expect to complete it in the next 45s
27. If an engineer starts constructing an experimental and there are nearby experimentals under
construction and we don’t have huge amounts of mass then the construction should be
aborted and the unit reclaimed.
28. Added higher priority air scout builder for if the factory hasn’t built an air scout before.
2.37.6) Other changes
29. Allied radar should now be taken into account when deciding whether to build radar.
30. Improved accuracy of mass storage income calculation – track when storage is built or
destroyed, or mex is build or destroyed, and update the mass storage income each time for
the adjacency bonus
31. Early upgrades should be more likely with very high mass (to avoid unusual scenarios early
game where the AI might overflow resources – more of a preemptive potential logic, as it
only happened when I cheated in a T2 mex and T2 pgen for the AI to help with testing, but in
theory a campaign might gift such resources to the player)
142
32. When choosing the location for mass storage, the adjacency bonus should be factored in
(meaning T3 mexes get prioritised over T2, and on maps like astro craters places that benefit
2 T2 mexes get prioritised over other T2 mexes)
2.37.7) PreRelease basic check
Africa 4 M28 AiX 1.5 vs 4 RNG AiX 1.5
M28 win in 36.5m - A close game until the late stage, M28 and RNG had similar income until early
experimental stage where M28 fell behind, but M28 maintained a good kill:death ratio, and soon
obtained an unassailable lead thanks to its air control and gunships. M28 generally seemed to do a
good job of spending its mass with almost no mass in storage throughout the game.
Acknowledgements
Radde, Azraeelian Angel – Both provided a number of human vs AI teamgame replays which formed
the basis of most of the changes this update.
2.38) V9 – Ahwassa and misc
2.38.1) Experimental bomber targeting rework
●
●
●
●
●
●
Improve approach to experimental bomber targeting – currently, M28 just looks for the first
zone with enemies and then gets the best target for the bomb out of those enemies,
prioritising zones near the base first then zones near the bomber in order of distance. I want
to make the following changes:
o Once get to the ‘cycle through zones by distance for targets’, if we have a land zone
that has targets, then consider the next 6 zones and include any that are within 100
of the zone chosen before going on to pick the best target. Also do this if there are
targets in the zone the bomber is in.
o Get the closest >80% complete enemy T3 MAA/T2+ Naval AA or SAM, and the
closest ‘high value category’ >80% (or 25% if experimental level) unit (T2+
building+navy, T3 land), and target this (or if a significantly better value AA target is
nearby, target this – i.e. this is a test based both on the distance of the target to the
bomber, and the value of the bomb, but subject to limits in both cases.
▪ Factor in the
o If there is no AA, then go with current approach of targeting the highest value target.
o Increase the value of a target by 10% if it is within 15% of the bomber’s facing
direction.
Significantly increased the threshold of enemy AirAA required for an experimental strat
bomber to retreat from, both generally, and to increase both based on nearby groundAA and
on the total number of experimental bombers owned.
Fixed a bug where experimental bombers would run from groundAA instead of AirAA.
Once an experimental bomber has turned around after firing its bomb, it should keep
moving towards the rally point for a few seconds (where it is relatively far away) before
getting new orders, to reduce the risk it turns around (trying to target the new target) and
ends up flying over enemy AA.
When making changes I realised there was a major bug with the Ahwassa targeting logic
where if it wanted to target a structure it would get the best location to fire a bomb, but
never actually fire the bomb there!
When getting the damage dealt by a bomb for units who wont be killed outright, the value %
of the unit’s mass cost should be limited to the % of its max health that will be dealt. This
143
●
●
●
means the ahwassa should be more likely to target a T3 army accompanying a GC compared
with targeting the GC.
Fixed a bug where units with 0 enemy combat threat and 0 friendly combat threat but which
contained enemy units would be ignored (e.g. a zone with only enemy mexes, pgens etc.
would be ignored)
Expanded the factors taken into account with whether to ignore a zone, so zones with an AA
threat but no combat threat should still be considered even if they have a friendly combat
threat, to strike a balance between e.g. a couple of T1/T2 tanks meaning the ahwassa won’t
bother
Adjusted the experimental builder to be more likely to build an ahwassa even if lacking in air
control - instead of being based only on the number of Yolonas that the team owns, it’ll be
based on the number of game enders (e.g. if the team has 1 mavor/scathis/rapid fire
artillery, then it will want at least 3 Ahwassas before trying to get Seraphim to build a
Yolona). Also made it more likely to be built when we have high numbers of land
experimentals or T3 arti.
2.38.2) Replay 20137011v7 M28
1. Fixed a bug with determining if the enemy has AA threat in an adjacent zone (which would
use the current zone instead of the adjacent zone). I suspect this may make gunships far
more timid than before so may require future refinement, but it was also potentially the
cause of scenarios where gunships would fail to attack poorly defended enemies.
2.38.3) Replay 20141289 v8 M28:
1. Increased ACU aggression so it now factors in allied threat in adjacent zones when deciding if
it needs to run.
2. Restricted calculation of the enemy best range to units that are almost constructed to avoid
units running from e.g. under construction experimentals.
3. Increased the amount of build power to be assigned to T2 arti production when faced with a
very large long range enemy threat.
4. Engineers assisting a shield shouldn’t be reassigned as part of the logic to reassign engineers
with lower priority actions.
5. GCs should no longer be treated as having an indirect fire weapon (in addition to a direct fire
weapon). Also adjusted the logic for managing land units so if they have the same range as
the enemy (but are in the part of the logic that is based on outranging the enemy or having
equal range with far greater threat), units with the same range should consider attacking.
6. Experimentals that have enemy experimentals near them should move towards them instead
of attack-moving towards them in some cases (broadly where they would normally retreat
but the enemy has experimentals that are too near them so they will attack to do some
damage rather than dying doing nothing).
2.38.4) Replay 20142350 v8 M28:
1. There was a bug where units that weren’t completed would be sent for land zone orders
(resulting in an error message when this was attempted with an ahwassa) – this should no
longer happen.
20142613
144
1. If MML are firing and their missiles are being stopped by TMD, the zone should flag it wants
indirect support and be slightly more likely to build MML.
2. RAS SACUs should assist shields that cover T3 arti or gameenders as a top priority if the
enemy has its own arti. They should also assist if the shield is under construction.
2.38.5) Other (non-replay based) changes
1. Quantum gateways (and hence RAS SACUs) should be built on 20km maps even without a
resource modifier if the AI has some spare mass and all T3 mexes in the zone
2. Units such as the megalith with a direct fire range of 64+ should now attack-move even if
they have significantly more threat than the enemy (given they cant retreat easily and their
range means it is better to adopt a gradual approach of attacking the enemy).
2.38.6) FAF develop upcoming release – compatibility
The upcoming FAF develop changes (expected late June 2023) change how AI brains work in FAF, with
custom AI each having their own brain class to use.
Jip helpfully talked through how to update M28AI to reflect this new approach:
https://youtu.be/bpdHM2-QXQk
Copying the code Jip DM’s me re this with minor tweaks (to reflect the location where I’m keeping
M28’s brain class, which is a new file M28Brain.lua) and it appears to be working although I’ll want to
test some more when the changes come out to make sure.
2.38.7) Basic pre-release check
3v3 on TwinRivers vs RNG as AiX 2.0 – A fairly close game – RNG killed 1 M28 with an early
experimental along with the majority of its base and maintained an eco lead, but M28 was still able
to handle the experimental attacks (just). However I came across a bug so didn’t let the replay play
out fully. The main reason for the game was to check M28 still seemed to be achieving similar
results, and it managed a health 4:1 kill:loss ratio for much of the game so without analysing in detail
it hopefully is ok.
I also did a part-game of 2 M28 AiX 4.0 against each other on Africa to check for any very obvious
bugs.
2.38.8) Acknowledgements:
●
●
Jip – video call and code for the new AI brain class in the upcoming FAF develop release
Radde and Azraeelian Angel – replays
2.39) V10 – Building a game ender under T3 arti fire
2.39.1) Special shielding logic - Planning
I’ve seen a few replays now where M28 (with a resource boost) out ecos the enemy team but the
enemy gets T3 arti and gradually blasts M28 into submission.
Back when I was still planning on updates to M27 I did some sandboxing on possible shield defence
against an enemy mavor. Shield cycling was only really viable for UEF and Seraphim T3 shields as an
option, but a potentially easier approach to defending would be to aim to have 3 T3 shields covering
a high value target (mavor/T3 arti), 2 of which are constructed and 1 which is near-complete. 1 of
the constructed shields is marked for assistance, and the other gets ctrlk’d when depleted (so it can
145
be rebuilt). That then means I need 1 T3 shield to be able to survive long enough for the shield to be
ctrlk’d , the destruction animation to cease, and a new shield to be constructed.
The problem with this is that Cybran shields suck, so I really need to try and build non-Cybran shields
to protect somewhere (e.g. it might be worth allowing any shield to be built as the first shield, but
the 2nd+3rd should be seraphim if that’s an option, or if not then UEF or Aeon).
For example, a UEF T3 shield has 17k health, and a Seraphim T3 arti does 5k damage a shot. This
means that I could in theory survive 4 T3 arti shots a cycle with the above approach.
The T3 arti shots fire every 6 seconds, with Aeon and UEF T3 shields needing 4k/5k build time (Sera
requires 5.8k but can absorb an extra T3 arti shot).
I’d noted the destruction animation speeds as being (in order from fastest to slowest) Aeon, Cybran
ED4, UEF, Seraphim.
Testing again, with a UEF T3 shield, even with lots of RAS and building on the old wreck (something
that would be even harder for AI to achieve) I wasn’t able to maintain shielding against 4 T3 arti.
I therefore need to eliminate/reduce the build death animation as a factor, so want to have all 3
shields in a cycle of being destroyed and rebuilt. I.e. the idea would be:
●
●
●
●
●
Identify 3 shields that cover the game ender/T3 arti, and ideally cover each other and (even
better) are adjacent to each other so engineers can reach all 3.
Keep engineers in a location where they are in build range of all 3 shields.
‘Idle’ state – have 2 shields at 99% construction, and 1 shield complete.
When a shield drops to 0 shield health, then complete 1 of the 99% complete shields and
ctrl-K the shield that has gone to 0 health (don’t ctrlK if we have no under construction
shields though)
Start rebuilding the shield in the wreck location of the old shield (so it starts 50% complete).
o This will require checking every tick for if the location can be built on.
Given how much work this will be, I only want to attempt it to build a game-ender (rather than T3
arti), and only if my AI has a large gross mass income (at least 100 per tick mass).
I also want to consider trying the shield cycling approach if there is enough build space, since it has
the advantage of not requiring mass to maintain. However, this depends on how easy it is to manage
the shield cycling.
In particular, I want to know whether shields will recharge when disabled (if not depleted fully), in
which case it becomes much easier to do shield cycling. However it looks like they don’t recharge at
all when disabled, in addition to having quite a while before they will become enabled again.
A UEF T3 shield will become active somewhere between 5-6 seconds from being enabled.
This means that shield cycling logic would need to have multiple shields active at different levels. I
was able to fit 5 T3 UEF shields and 1 T3 arti with 3 T3 PGens in an area (with all shields covering
each other). When active the shields heal 131 hp/s, and when down they effectively heal 708 hp/s.
Therefore, with perfect timing (which I expect to be effectively impossible) 5 shields would provide
3540 hp/s healing.
146
However, I would need to reliably estimate the point 6s away that a shield would fail, in order to
enable a new shield. In addition, aoe damage would cause multiple shields to take damage at the
same time, in addition to FAF’s shield overflow logic.
Therefore while it would probably be well suited to defending against a single mavor attack, it would
be weak against multiple T3 arti due to the less predictable nature of shots.
UEF Shield SACUs provide another option, but they’re not well suited to prolonged/attritional games,
as while they have a large health pool (52k) they regen at only 150hp/s normally and 241 hp/s when
depleted. So while they’d be able to reduce the risk of a shield not being active, and would also fit
into a smaller area, I’d need far more of them.
The small size of the shield means they’d be unable to protect all the T3 power around a mavor, only
the mavor itself (and up to 2 T3 power adjacent to it)
Testing in sandbox, against 4 power assisted T3 arti, I would need to enable the UEF SACU shield
preset around the 35%-40% level (broadly, the point at which the shield would fail if it took 7 T3 arti
hits, being 4 + (4-1), since the new shield wouldn’t activate in time from when the previous T3 arti
volley hit, meaning it needs to survive 1 fully volley plus any remaining shots of the current volley
(which could be anywhere between 0-3 arti shots).
Doing some rough sandbox testing, with 6 SACU shield presets active/recharging, the first one that
was failed was on 42% when the 6th one’s shield failed against 4 power assisted T3 seraphim arti (and
that was with imperfect cycling where I activated some shields too late – so in reality it would be
below 42% if done ‘correctly’).
Assuming it would be 40%, then that implies I would need 15 UEF Shield SACUs to continuously
protect 1 mavor/game ender from 4 T3 arti. Obviously that many shield SACUs would require a lot of
T3 pgens that couldn’t be protected (each shield SACU costs 1k energy to maintain).
The mass cost alone is 7600 to build 1 SACU, meaning a total mass cost of 114k.
So while it is less than the 4 T3 arti (70k each, so 280k total), if factoring in the game ender cost it
ends up much more. E.g. a Mavor and 15 Shield SACUs would cost 339k. This despite how the T3
arti would be wrecking havoc on M28’s eco and buildings outside the shield SACU range.
Compared with a RAS SACU, it costs 6.6k and yields 11 mass per second, so the mass equivalent
income of this setup of 114k shield SACUs (ignoring power, which has a big impact) would be at least
190 mass per second.
That mass would allow a UEF T3 shield to be built over the wreck of an old T3 shield every 8.7s.
However, 4 T3 arti would be firing c.20k damage (enough to overwhelm a T3 shield) every 6s.
So overall it doesn’t seem like a massive difference between the two approaches:
●
●
Shield SACUs vs T3 shield building: Pros:
o Less build space required
o Easier to code the logic
Cons:
o Less resource efficient (incur the cost of the shield SACUs regardless of whether
they’re used)
o Smaller area protected
o Requires large power grid
147
o
o
Requires UEF tech
Potentially subject to change (although this has been rumoured for 2+ years and not
yet happened)
Revisiting the idea of having Seraphim T2 shields be built, I’d originally discounted this because the
shield was too weak to withstand a single mavor shell (meaning the aoe from a mavor could
sometimes damage the building being covered by the T2 shield, even if the shield was on full health).
However, it could be an option to defend against mass T3 arti, given the individual damage from each
shell is much less.
A Seraphim T2 shield costs just 700 mass, or 350 if being built from 50% (a T3 UEF shield is 1.65k
from 50% health. So, if the aim was to protect a single high value building (game-ender) from T3 arti
I could in theory get away with just building T2 shields. I’d also need less build power, needing only
1.25k build power to fully build a T2 searphim shield vs 5k for a T3 UEF shield.
Regardless of which approach I use, I need logic to send an engineer from one faction to another
zone, so I can get the best shields needed (especially if I have a cybran on the team given how poor
their shields are).
2.39.2) Special shielding logic - M28 planned approach – game ender and shield
coverage idea
(strikethrough text denotes originally planned changes which I decided would be too much effort for
now as it took much longer than hoped to get the shield assist logic working).
For now I’ll try a simplified approach that makes use of M28’s existing systems where possible:
●
●
When determining the best location to build for a game-ender or T3 arti, adjust the building
size as follows:
o Yolona Oss and Scathis (size 8) – look for an experimental bomber (size 16) build
location
o T3 arti, Mavor and Rapid Fire Artillery (size 8) – look for a Czar (size 24) build location
When construction is started on a game ender (and isn’t to be aborted), or a 100% complete
game ender is created for which construction start hasn’t been run, and it’s owned by
M28AI, change the normal logic for updating whether a location is buildable as follows:
o First look for 3 shield build locations that can be built close to each other and the
game-ender/T3 arti, and record these locations against the gameender
o At the same time record the best engineer shield tech available – use the team table
to track what factions we have T3 land factories for, and then cycle through core
zones to identify the nearest zone with that faction. Prioritise engineers as follows:
▪ Seraphim
▪ Aeon
▪ UEF
▪ (Other – no faction requirement)
o Record the closest such factory to the gameender position against the gameender,
and record against the factory the unit(s) that it is recorded as being the nearest
source of engineers for. Also record against the zone the factory is in, in a table of
factories with priority engineers.
o If the factory dies, update this recording for the game-ender. If the game-ender dies,
update for the factory.
148
o
●
●
●
Factory builder - if it has any game-enders assigned as needing its engineers, and
check if there are at least 3 T3 engineers of its faction traveling to the same zone as
the game-ender. If not, then build an engineer as a near-top priority.
▪ Determine this by having a flag for if we need engineers of a particular
faction as part of the ‘want BP’ approach for engineers for the zone in
question, so the factory builder can just check for this flag status.
o When attempting to build a unit, if a particular faction is required (e.g. for this logic
or for any other logic I might introduce), then a flag should be set that the zone
wants engineers of that faction.
o Then look for if there are any T3 power adjacency locations (if dealing with T3 arti,
mavor or rapid fire artillery) and record these locations against the game ender
o Also record the game ender against the land zone
o When the game-ender dies, update the land zone to no longer have it recorded
Treat any such recorded shield build locations and power build locations as being ‘blacklisted’
for normal construction.
Have a high priority action for both core and minor land zones – if the zone has any factory
that is a key faction engineer builder (see above), check if the zone of the game-ender it is
getting engineers for has at least 3 T3 engineers of the desired faction. If not, then send an
engineer of that faction to that zone (subject to how many engineers of that zone are already
traveling there).
o Need to factor in the optional variable to the ‘send engineer’ existing action and/or
add a new action since will want to send it both the zone to travel to and the faction
wanted.
Have a high priority ‘refActionSpecialShieldDefence’ action for engineers if the zone has a
gameender:
o Focus on the game ender in the zone with the most invested mass
o Track the engineers assigned as such against the game-ender unit they are trying to
protect, and if the game-ender has no active engineer shield thread, start a new
thread to protect it, that runs as long as the game-ender is alive.
Active shielding thread:
●
●
●
●
●
Check if we have build shields in all of the preassigned locations.
o If we have any M28AI owned structures in the location that aren’t shields, then ctrl-K
these to create space.
o Track the actual shield units against the game-ender being shielded, along with the
number of the valid locations, so can easily compare the size of the shield units table
against
If not already started construction, then limit to the faction of the shield that we want (see
above re recording the best faction available).
Build T2 seraphim shield if enemy has no mavor, otherwise build T3 shield.
When a shield construction is started, if it is by an engineer with the relevant active shielding
action, then the shield should get recorded against the game-ender assigned to that
engineer.
When deciding how many shields are available and under construction:
o We want 1 shield that is both fully constructed, and has an active shield (ignoring
power stall).
o If there are no shields that are constructed with > 0 shield health, then construct
another shield.
149
o
●
●
●
If we have 3 shield units, and at least 2 are constructed, and at least 1 has 0 health,
then ctrl-K the shield with the lowest health (also do this if all 3 are constructed even
if none have 0 health).
Want 500 build power assigned (enough to build a T3 UEF shield every 5 seconds) if are
defending against arti, otherwise just want 100 build power assigned.
Adjust any power builders so if there is a gameender with power adjacency locations and no
assigned power, then will build the power in that location.
When deciding on the build location for a gameender, prioritise the location furthest from
the enemy base (assuming multiple options).
o Have a high priority engineer builder to build the game ender in that zone
o Determine the faction(s) of engineers wanted for that zone and get them sent over
as high priority actions.
Other changes made while testing for this:
● Increased the threshold for a team to be considered to always not have low mass to 400 per
tick from 200.
● M28 took 30m with a paragon and being cheated lots of engineers and still failed to have a
game-ender completed, with mass overflowing and engineers sitting idle. This was because
it thought it had low power. Now if it has a gross level of power similar to that of a paragon
it should proceed as if it doesn’t have low power.
● When searching for a part-complete building to assist, it should be ignored if that unit has
been assigned for special shielding (so e.g. a unit trying to build a shield as part of the normal
shielding logic doesn’t complete a near-compelte shield that has been prepared as part of
the special shielding logic.
● I noticed a likely bug where if reclaiming an enemy unit the tracking was being done in the
wrong order so the the unit would track the action then have that tracking cleared.
● During testing I came across strange results where engineers would stop assisting a shield
construction 2 ticks ahead of the shield being completed, but the shield would still be
completed, and this would happen before it showed as completed on screen/in game. I
therefore will stop construction once a shield hits 99%, or it is estimated to be 2.5 ticks from
completion.
● Another issue that came up during testing was that when getting units in a rectangle, it
would get units outside that rectangle – e.g. in the below screenshots, both mexes build
location was outside the rectangle (drawn in blue), but getunitsinrect would still return
them, resulting in the mexes being killed despite not being a blocking unit:
150
This also highlighted a number of other issues:
●
●
●
●
●
Killing mexes and hydros wont make any difference to freeing up build locations (so I want to
ignore these when searching for buildings to destroy)
The ‘kill’ logic was triggering when construction was started of a part complete building.
Investigating further, there can be a fraction of a second between when the shield monitor
thread checks for if a location is buildable, and when the event for construction being started
triggers, meaning it thinks it can’t build somewhere, and that there is no shield in that
location, but actually there is a shield that has just started construction in that location. This
is because the thread is forked when it triggers to avoid the risk that something in M28 that
goes wrong causes the core FAF game to go wrong. To try and get around this:
o A delay is required before constructing a shield, to require construction to be wanted
for at least 1 tick before, so I’ll track the ‘previous action’ of engineers doing this
active shielding, and not do anything when they want to construct a shield for the
first time (as well as clearing their actions if previously they were assisting a shield, in
case the shield is one that is almost constructed).
If there is a shield blocking a build location, then that shield should be incorporated into the
special shielding logic.
Engineers shouldn’t be treated as available (with the general approach) if they are assigned
to special shield defence.
Added in a similar builder for minor land zones that have gameenders (for niche cases on
large mass games).
2.39.3) Adaptive dark card and map zone adjustments
151
Adaptive dark card’s land zones had an issue where the central area would get included with one of
the start zones:
The initial creation from this looks ok:
152
The issue was with the assignment of areas without a zone. I made the following adjustments to the
logic:
●
●
●
When dividing up parts of a map with no zone, the points chosen should be symmetrical on a
square map
Reduced the distance for assigning segments to an existing zone on maps that are 512 or
smaller in size.
Small adjustments to the zone generation code relating to this which I hope might make it
fractionally faster although the difference wouldn’t be noticeable (noted more so if I look
back I know I made some changes to things including a potential bugfix that shouldn’t have
had an impact).
After these, the zone generation gave:
153
154
●
●
●
There were also error messages which would happen at the start of the game due to civilian
controlled underwater mexes, as the code assumed that any mexes would be owned by a
brain with enemies. I therefore added a delay to when this check is done, and if there are
still no enemy brains then it should search for any non ally brains.
Another issue was that some of the water zone team variables weren’t setup by the time the
civilians were identified/revealed in the water, leading to other errors.
Hidden warning message about mexes at the start of the game that would result from the
initial map setup logic thinking the mexes weren’t valid/available (due to a building being on
it), and then the logic for recognising the unit later triggering and thinking there was an
inconsistency as a mex managed to be built somewhere that wasn’t considered available for
construction.
155
●
When the zone was being extended and caused the PD to be included, it resulted in the
Orange ACU in the above diagram trying to attack the PD instead of building the factory. I
therefore want to have the ACU ignore enemy structure threats if it is still doing its initial
build order, and also ignore threats that are far away from its current position if it’s doing its
initial build order.
2.39.4) Profiling late-game with experimental bombers
The following is a log of almost 6 minutes in the late-game where M28 had 3 active Ahwassas (as the
game was slowing down occasionally), although I don’t know how reliable it is as I might have paused
the game during part of this:
info: ProfilerOutput: No.1=ManageExperimentalBomber; TimesRun=360; Time=54.929748535156
info: ProfilerOutput: No.2=GetDamageFromBomb; TimesRun=26309; Time=54.724731445313
info: ProfilerOutput: No.3=IsTargetUnderShield; TimesRun=941156; Time=38.741760253906
info: ProfilerOutput: No.4=ManageAllLandZones; TimesRun=3572; Time=18.12939453125
info: ProfilerOutput: No.5=ManageSpecificLandZone; TimesRun=16904; Time=14.75634765625
info: ProfilerOutput: No.6=GetBestAOETarget; TimesRun=116; Time=8.796875
info: ProfilerOutput: No.7=ConsiderLandOrWaterZoneEngineerAssignment; TimesRun=16904;
Time=5.1810913085938
info: ProfilerOutput: No.8=GetCurrentAndMaximumShield; TimesRun=4583176;
Time=5.1098022460938
info: ProfilerOutput: No.9=ConsiderCoreBaseLandZoneEngineerAssignment; TimesRun=1800;
Time=3.38037109375
info: ProfilerOutput: No.10=RecordGroundThreatForLandZone; TimesRun=16904;
Time=3.2745361328125
info: ProfilerOutput: No.11=ConsiderActionToAssign; TimesRun=35270; Time=2.1931762695313
info: ProfilerOutput: No.12=GetCombatThreatRating; TimesRun=85054; Time=2.1148071289063
info: ProfilerOutput: No.13=ManageAirAAUnits; TimesRun=360; Time=2.0195922851563
info: ProfilerOutput: No.14=ConsiderMinorLandZoneEngineerAssignment; TimesRun=15104;
Time=1.6680908203125
info: ProfilerOutput: No.15=ManageRASSACUsInLandZone; TimesRun=1583; Time=1.6134643554688
info: ProfilerOutput: No.16=ManageGunships; TimesRun=359; Time=1.254150390625
info: ProfilerOutput: No.17=FilterToAvailableEngineersByTech; TimesRun=16904;
Time=1.1463012695313
info: ProfilerOutput: No.18=OnEnhancementComplete; TimesRun=22; Time=1.0410766601563
info: ProfilerOutput: No.19=RecordClosestAdjacentEnemiesAndGetBestEnemyRange;
TimesRun=16904; Time=0.9609375
156
info: ProfilerOutput: No.20=CanBuildAtLocation; TimesRun=366467; Time=0.8946533203125
The time managing these bombers is unsurprisingly very high (as given the significance of the unit
they will do far more intensive calculations than others).
However, it looks like the main cause of this is checking if a unit is under a shield or not at 38.7s.
One possible way of shortening this might be to as part of the land zone and water zone
management do a check on if there are shields in that zone, and to then only run this logic if there
are. However, this runs the risk of inaccuracy (1s delay on mobile units, and shields being on the
edge of a zone where they can provide cover to units not in that zone). I’m also not sure how much
it would actually help as in this case the bases were heavily shielded so it’s likely most units being
considered did have shields in them.
Another option is to reduce the number of times the function is called in the first place, but this
would require further filtering of the experimental gunship targeting logic.
Looking at the function itself for potential optimisation, it is making use of GetUnitsAroundPoint as
the way of identifying nearby shields. I could therefore have a time check of the time a unit was last
checked for if it has a mobile shield, coupled with a true/false flag, and only refresh if the last check
was more than 0.9s ago (menaing e.g. in the case of multiple experimental bombers the time taken
isn’t increasing in proportion to the number of bombers, while for things like novax it should still be
responsive to a target going under a shield). However, this would need to make use of a table
approach given the different potential input parameters for the function.
After this change the game didn’t desync (suggesting the decision made by the experimental bomber
was unchanged) but the stats were much better, even if allowing for pausing the game potentially
affecting things (i.e. the % of the time of getdamagefrombomb taken up by checking if a target is
under a shield is lower):
info: ProfilerOutput: No.4=GetDamageFromBomb; TimesRun=26309; Time=13.608703613281
info: ProfilerOutput: No.14=IsTargetUnderShield; TimesRun=941156; Time=1.3123779296875
The game still ran slowly, but not as slowly as it might otherwise have done.
I’ll therefore spread out the logic for experimental bombers over a max of 3 ticks to help alleviate the
issue.
2.39.5) SC Campaign – UEF Mission 1
●
●
This has the issue that only mexes can be built initially, whereas M28 assumes it can build a
land factory. I therefore add some backup logic for campaigns where if the ACU doesn’t have
an order it will try building a mex; then power; then a factory (land, then air, then navy).
This then revealed another issue – if M28 builds the mexes (as player 2), the next part of the
campaign doesn’t take place. Investigating further, the campaign script will only check
‘HumanPlayer’ armies. E.g. the objectives such as build 3 mex or build 3 PGens are limited to
Armies = {'HumanPlayers'}, in the SCCA_Coop_E01_script.lua function StartMission1Part1().
This comes from ScenarioFramework which in turn comes from SimObjectives.lua, which has
a function ArmyStatCompare which receives the ‘Target’ variable (which for this objective is
a table containing Armies as one of the keys).
157
●
●
●
●
●
●
The list of brains to consider is based on the line local armyBrainsList =
MakeListFromTarget(Target), so I did a destructive hook of this, copying the code with the
change that an M28AI brain will count as a human.
When overflowing mass (/at 99% stored) without low power, further factories should be built
regardless of eco conditions and pathability.
Increased the likelihood of MAA being built at T1 (and generally) – previously it wouldn’t be
built at T1 in most cases on the assumption that we could get T2 (which is far better). Now it
will still consider building it if the enemy has significantly better air to ground threat than our
MAA (with this threshold decreasing after 10 minutes).
Increased the travel distance that land factories will search for enemy targets to build units
to counter when more than 50% mass is stored to 100% of the distance to the nearest
enemy base, along with a minimum range of 250 (M28 works on the assumption for normal
skirmish that if there are no enemies within 75% of the nearest enemy base then it is better
to put the mass into ecoing and experimentals, which isn’t an option in campaign, resulting
in land factories idling while overflowing mass).
Fixed a bug that meant adjacent land zones weren’t being recorded. I don’t get how I have
gone so long with M28AI with this bug as at first glance the bug should’ve meant no adjacent
land zones would ever be recorded, yet they still seemed to have been so I’m guessing I
wrote some sort of redundancy that usually but not always kicks in.
Once we have at least 150 combat units in a land zone then they should attack even if the
enemy appears to have more threat.
2.39.6) FA Campaign Mission 6 – Further changes/Capture the control centre
One bug highlighted to me was that M28 would reclaim the control centre instead of capturing it
where the enemy AI had captured it.
To help potentially resolve this, I first need to be able to figure out when a unit has been captured
(since the old unit gets destroyed and a new unit created in its place, meaning variables I assigned to
the old unit such as that it should be captured will be lost).
Doing a hook of the following achieves this, where tUnits is a table of the newly captured unit(s), and
bCaptured is true if the unit is captured:
import('/lua/SimUtils.lua').TransferUnitsOwnership(tUnits, iArmyIndex,
bCaptured)
I therefore want to check the following:
●
●
●
●
●
●
When a unit is captured, if it is a campaign map, then check if the captured unit is on a
different team now to the first M28AI in the game.
If it is, then check if the land zone has any capture targets.
If so, then cycle through each recorded capture target, and check if their unit ID is the same
as the unit that has been captured.
If so, then add the captured unit to the table of capture targets.
Also add a flag to capture targets both at the time of the objective and on any recapture to
note they shouldn’t be reclaimed but instead should be captured.
Adjust the engineer ‘reclaim enemy’ special logic so instead of trying to reclaim the nearest
enemy it should try to capture it if it is flagged as being capturable.
158
After doing all of this it still didn’t work so I investigated further and it turns out this objective
doesn’t use the ‘AddObjective’ function that all the others until this point have.
To fix this, I therefore add in some manual code whenever an objective is added or the map size
changed to check for if the control centre exists and is capturable, and if so I add it as a capture
target.
Other Mission 6 changes
● Added some redundancies for when the enemy start position isn’t on a land zone, including
searching slightly around the nearest enemy base for a land zone, and using a more precise
terrain label check to see if the enemy base is in the same island as the factory.
● There was a bug with SACUs getting upgrades where M28 wouldn’t realise they already had
an upgrade and would keep trying to get it.
2.39.7) MML synchronised shots
I’d held off adding this logic to M28 for a while as for M27 it rarely got featured. However, M28 tends
to build more MMLs than M27 so I figured I’d add in something basic to help give it a slight boost on
those scenarios.
If zone has mml that has been intercepted by tmd in the past 15s, and mml outrange enemy df and
indirect fire, check if have only non-aeon tmd (ie both have non aeon tmd and no aeon tmd), and no
loyalists, in either the current zone or an adjacent zone, and that we have an indirect threat in the
current zone of at least 700.
bConsiderSpecialMMLLogic
If satisfy the above, then when cycling through units for scenario 1 and 2 add mml to a new table
instead of giving orders.
tMMLForSynchronisation
Special logic for table of MMLs:
First record a table of all enemy shields and tmd in cur and adjacent zones, and filter this down to
those that we lack intel on which are at least 30% constructed
—want to stay at least 5 out of range of any PD or mobile units (normally would be 10+)
Find the closest TML+TMD
Attack-move towards it if we are more than 5 out of range, otherwise issue a ground attack at this
target’s location. Also record any such (within 5 range) MML in a table of attacking MML in the cur
zone.
Determine whether firing cycles of mml are far enough apart to warrant disabling weapons:
●
●
●
●
Want at least 4 MML before considering synchronising
want missiles fired within 1s of each other
however if unit hasnt fired for a long time then dont want to disable
if 75%+ of mmls in sync or 25%+ havent fired for a long time then dont disable
When enabling the weapon, if mml is flagged as having a time the weapon was last disabled then set
this to nil and clear from the team table(see below)
159
Otherwise disable the MML weapons:
When disabling the mml weapons, record the time the mml weapon was disabled, and include in a
table in teamdata.
Team cycler – every second check for table of weapon disabled inits. If time last wanted the weapon
disabled was more than 2.1s ago then enable the weapon
When testing I noticed that my MML threat was being included based on the ‘direct’ threat value
given (which is much lower) which may have led to some strange decisions. I’ve therefore updated
so that indirect fire units have their indirect fire threat calculated if they have no direct fire weapon.
Another issue is that some MMLs fire a salvo, meaning the estimate of the time between shots isn’t
accurate. To help mitigate this (as the original logic meant the MML would all be paused because the
code didn’t think they were ready to fire yet), I’ll also calculate the average time for the MML until
they’re ready to fire, and enable firing if 80% of the MML are within 1s of the average, or if the
difference between the longest to fire and shortest to fire is less than 1.5s.
I also noticed when testing there was a bug with DF support units with trying to get the closest
indirect fire unit to support, and that DF units were a bit too aggressive when the enemy was
adjacent to the core zone (given the size of land zones means often this can be near the middle of
the map), so I adjusted to make it less likely outranged units will attack from the core zone if the
enemy is in an adjacent zone.
2.39.8) Ecoing adjustments
●
●
●
●
●
Changed how priority mex upgrade logic works to try and spend a base level of mass
upgrading mexes, and to focus on safe mexes where the team has some, rather htan trying
to upgrade 1 mex per brain.
Also adjusted the thresholds so even when mass stalling M28 will try to upgrade mexes. This
resulted in further adjustments being required to when land factories upgrade (with a high
priority upgrader added where there are lots of T3 mexes nearby).
Emergency PD builders should factor in all PD along the path to the nearest enemy meaning
if there is an allied base with PD in it then less PD should be built in the rear M28 player’s
base.
HQs should be less likely to be paused in a mass stall
Idling air factory HQs should consider building engineers if there is at least 1k mass stored.
2.39.9) Other changes
1. If an engineer attempts to build a factory and fails (e.g. a T3 Seraphim engineer tries to build
a T3 Air factory when the owner only has a T3 UEF air factory) then it should now try and
build a factory of that type of any tech level instead of failing to build anything.
2. Fixed a bug where fatboys could be treated as being gameenders (due to being an
experimental artillery unit based on categories).
3. Fixed a bug where no build locations would be identified for SMD (which in theory should’ve
meant no SMD ever got built, but I’m guessing some redundancy logic meant SMD would
very occasionally be built, hence why I didn’t notice it sooner – thanks to Fearghal for
flagging the issue).
4. Fixed a bug that would cause M28 to break in rare cases on maps, e.g. such as on the map
Abhor (thanks to Relent0r for flagging this map) – it happened where there was an
inconsistency between the plateau based on the terrain label for a segment, and what was
160
actually recorded as the plateau for that segment’s assigned land zone. Also thanks to
J_W_W who highlighted the same bug on Concord Lake
5. Gunships should try and avoid active friendly M28 nuke targets (to reduce cases where a
massive gunship ball gets wiped out by a friendly nuke).
6. Fixed a bug with experimental bomber targeting where it would target all units when trying.
2.39.10)
●
●
●
●
●
●
●
Acknowledgements:
Fearghal (highlighting issue with SMDs not being built; also later confirmed by tyne141)
Radde – several replays against a 1.5 resource mod M28
J_W_W and Relent0r – errors where M28 wouldn’t work on a couple of maps
Pedro_Lima – highlighting FA Mission 6 issues with capturing the control centre
Unknown – I made a note that someone had highlighted one of the custom campaigns
where M28 attacks PD at the start, but can’t find the comment now (I recall it was an issue
either with golden crystals or raid. Since golden crystals has PD near the start I’m assuming it
was that map, and a bugfix made more generally looks like it’s solved that issue).
Russtymango Naval replay
(sorry if I missed anyone out – I had some rl issues so wasn’t able to look at a number of
issues prior to then)
2.40) V11 – Transports, optimisation and M2 SC UEF
2.40.1) Optimising for naval maps - Selkie Isle
Thanks to Radde who highlighted how long it takes for M28 to load at the start of the game on this
map – running profiling it was taking
Profiling:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=172.03498840332
info: ProfilerOutput: No.2=RecordIslands; TimesRun=1; Time=161.7625579834
info: ProfilerOutput: No.3=SetupLandZones; TimesRun=3; Time=5.7037582397461
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=3.5779571533203
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=11134; Time=3.5703010559082
info: ProfilerOutput: No.6=SetupWaterZones; TimesRun=2; Time=1.2650299072266
info: ProfilerOutput: No.7=AssignMexesALandZone; TimesRun=1; Time=0.98745727539063
info: ProfilerOutput: No.8=CreateWaterZones; TimesRun=1; Time=0.68331909179688
info: ProfilerOutput: No.9=RecordWaterZonePathingToOtherWaterZones; TimesRun=1;
Time=0.52227783203125
info: ProfilerOutput: No.10=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.50600051879883
Splitting the RecordIslands function into 3 parts based on how the code is setup, it’s clear that the
third part (calculating pathing) accounts for all of the time taken:
info: ProfilerOutput: No.2=RecordIslands; TimesRun=1; Time=162.75033569336
161
info: ProfilerOutput: No.3=RecordIslands: Pathing; TimesRun=1; Time=162.7501373291
I already only consider pathing to islands with mexes in. One further possible optimisation would be
to only consider generating pathing for zones with mexes in them.
Checking where the pathing gets used currently, it affects the following:
●
●
●
●
●
●
●
ACU that doesn’t have a land zone to move to
Engineers moving to an island from a core base (shouldn’t be impacted by the change since a
core base would be expected to have mexes in it)
Engineers moving to an island from a minor zone – could be impacted, but not a big deal
given core bases should be able to cover any engineer requirements
Factories checking if a nearby island wants engineers or amphibious combat units – unlikely
to be impacted given factories tend to be in core zones (exception would a factory built on
an island as a forward base, but even then often the factory will be on a location with a mex,
and even if it doesn’t end up building amphibious combat units this isn’t a big part of M28)
Land units looking for somewhere to support where there’s no land zone (i.e. on the current
island) to support – this is the main issue/use case where having no island could cause
issues. However, this is only relevant for amphibious/hover units. They also have a
redundancy to move towards the enemy base if they have nowhere to support, so hopefully
this would help and it would only be relatively niche cases where there could be problems,
e.g.:
o Units try moving to an island to support it
o They come across a land zone on the way that has no mexes
o That land zone has a greater ‘value’ than the zone they came to, meaning units take
their orders from it
o Units get sent to the enemy base, and that is in a different direction
o They go back to the original land zone and the cycle repeats.
However, I’m hoping this should be rare given most of the time a zone with no mexes will be
smaller than one with mexes, and all else equal will have a lower value, and often the zone
needing support will be in the same direction as the enemy base.
Another usage is checking if a path is safe; from memory this is only really used by engineers,
so again shouldn’t be a major issue.
Applying this restriction, the results are:
info: ProfilerOutput: No.2=RecordIslands; TimesRun=1; Time=81.08757019043
info: ProfilerOutput: No.3=RecordIslands: Pathing; TimesRun=1; Time=81.087310791016
I.e. it’s roughly halved the time taken on this map, so I’ll definitely retain this change.
The main factor in this time, splitting things out further, is logic that identifies the nearest land zone
on an island by pathing, with this taking almost all of the 81s:
info: ProfilerOutput: No.4=RecordIslands: Excl land path; TimesRun=312; Time=79.514221191406
Another possible change might be if there’s some way I can add a distance cap on larger maps
(20km+) for the islands considered, for example switching to recording straight line distance after a
certain point (with an approximate penalty).
162
Another possibility is to have a land zone copy the pathing of an adjacent land zone if it has the
pathing recorded, although it’d need to figure out a way to avoid 1 zone being copied for the entire
island.
Switching to distance based results reduced the time taken to less than 1 second:
info: ProfilerOutput: No.57=RecordIslands: Excl land path; TimesRun=312; Time=0.003082275390625
I’ll therefore try the following as a compromise since it looks like I have a bit of room to spare:
●
●
●
●
●
On any 10km+ map, with at least 45 land zones (30 for a 20km+ map), enable the ‘straight
line distance check’ logic.
Get the straight line distance to identify the 3 closest land zones from an island.
For each of these 3, calculate the actual travel distance.
Use this to pick the closest of the three.
i.e. this means doing the travel distance check only 3 times instead of for every land zone.
After implementing the above, the time taken is:
info: ProfilerOutput: No.4=RecordIslands: Excl land path; TimesRun=312; Time=8.0988731384277
i.e. this is almost 10% of what it was taken before.
Re-running the profiling, the total time taken now is:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=20.780799865723
info: ProfilerOutput: No.2=RecordIslands; TimesRun=1; Time=10.612384796143
info: ProfilerOutput: No.3=RecordIslands: Pathing; TimesRun=1; Time=10.612213134766
info: ProfilerOutput: No.4=RecordIslands: Excl land path; TimesRun=312; Time=7.8371429443359
info: ProfilerOutput: No.5=SetupLandZones; TimesRun=3; Time=5.5336036682129
I.e. the total time has decreased from 172s originally to just 21s!
2.40.2) Dropping far away locations on the same island
Currently M28 will consider dropping on plateaus, and far away islands. However on maps like strip
mine there are far away locations on the same island that are best to drop by transport. I therefore
want M28 to handle this. This should also hopefully provide the framework for transport dropping
on UEF Campaign mission 2.
●
●
●
●
Search for any land zones with at least 3 mexes that are within 55% of the distance between
our base and the enemy base, and which are on the same island as a friendly M28 base.
When looking for potential transport locations, if the transport doesn’t have a target under
the normal plateau/island approach, check for the ‘same island far away’ zones and include
any of them where relevant.
Ignore zones that we have engineers or factories or significant structure value on, or that
have dangerous enemy units.
While testing this I realised there was a bug with the mod distance recorded for zones.
However, it turns out I wasn’t actually using this for anything ( must have added it because
M27 makes much more use of the information).
163
●
Any such locations that are successfully dropped should be flagged similarly to an island
expansion point so a land factory is built. For this I’ll add in an override option so the logic
that says if a zone is an expansion zone will check for the override; then when a transport
tries to drop on a land zone it will set the override to true.
2.40.3) Strip mine – 2nd air fac build order
●
●
●
●
On maps like strip mine, M28 should try and do a ‘1st land 2nd air’ build order with its ACU,
meaning it can make use of the new logic to drop engineers at the far away locations.
As part of this, it should try and get the equivalent of 10 T1 PGens
It was also power-stalling from building 4 T1 mexes before the hydro, so if there are 5+
mexes in the zone it should only try and build 3 T1 mexes before having the ACU assist the
hydro (since otherwise the 3rd engineer starts building a mex and causes the powerstall).
Testing strip mine it also took a long time to load – the profiling results are below:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=80.346221923828
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=77.549865722656
info: ProfilerOutput: No.3=AssignMexesALandZone; TimesRun=1; Time=38.173263549805
info: ProfilerOutput: No.4=RecordPathingBetweenZones; TimesRun=1; Time=37.814758300781
info: ProfilerOutput: No.5=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=6162; Time=37.804725646973
info: ProfilerOutput: No.6=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.76557159423828
One of these functions is assigning mexes a land zone. I suspect this is so long because the time
taken will increase exponentionally with the number of mexes on the map.
Identifying mexes near the start position
It potentially calculates the travel distance for each mex to every start position (it will check if the
mex is closer on a straight line distance than the closest travel distance, and if so it calculates the
travel distance).
●
●
While the above figures were for a 1v1, on large teamgames this could be optimised by only
considering the 3 closest start positions, on a straight line distance, sorting them from closest
to furthest away, calculate the travel distance on the first one, and then keep going only as
long as that travel distance is more than any remaining straight line distance. However I spot
another easier simplification for now – they were calculating the travel distance to find the
closest base, if the straight line distance was below the search threshold. Now that search
threshold will be the lower of the closest base found so far, and the distance.
Another optimisation similar to the above but slightly easier to code – I’ll find the closest
start position on a straight line basis; calculate the travel distance, and then consider the
travel distance of all other start positions whose straight line distance is less than that travel
distance.
Adding mexes near each other mex
● This currently only uses a travel distance calculation. However, I can first do a straight line
distance check to see if the mex is close enough to even consider (since the travel distance
will only be further than the rough distance).
164
Profiling after changes:
info: ProfilerOutput: No.1=SetupMap; TimesRun=4; Time=41.668556213379
info: ProfilerOutput: No.2=SetupLandZones; TimesRun=3; Time=38.939647674561
info: ProfilerOutput: No.3=RecordPathingBetweenZones; TimesRun=1; Time=37.405723571777
info: ProfilerOutput: No.4=ConsiderAddingTargetLandZoneToDistanceFromBaseTable;
TimesRun=5852; Time=37.395477294922
info: ProfilerOutput: No.5=AssignRemainingSegmentsToLandZones; TimesRun=1;
Time=0.73818588256836
info: ProfilerOutput: No.21=AssignMexesALandZone; TimesRun=1; Time=0.0051860809326172
I.e. this has reduced the time for this function from almost 38s to a tiny fraction of a second, which is
better than I’d hoped, and means the load time for the wider map is much more bearable.
2.40.4) UEF Campaign mission 2
I’ve not been looking forward to getting M28 to work on this mission because the objectives will
likely require a lot of custom code - - the initial objective requires a building to be repaired. However,
due to how M28 calculates pathing it thinks it can reach the objective by land (which it could if the
map was expanded), and it doesn’t (yet) have logic to build a transport to carry engineers
somewhere on the same island.
Even if I get this logic working, the later objective involves building a certain composition of units and
moving them to parts of the map, something that will definitely require some hard-coded logic to
handle.
Repairing the initial objective
I’ve been wanting to add logic more generally for transports to carry engineers to far away zones, so
I’ll do this first (on the strip mine map), and then return to the first objective here to see if I can
tweak that logic (moved order of notes so this follows the implementation of the ‘drop engieners
somewhere on the same island’ logic above – i.e. considering M2 was the factor that led to this).
For M2, this can hopefully now work as follows:
●
●
●
When an objective is added with a unit to be repaired, check if the unit to be repaired is in a
core zone or adjacent to a core zone.
If it isn’t, then add it as a location to be considered for transport drops. I.e. make sure the
zone is in UpdateTransportShortlistForFarAwayLandZoneDrops.
When deciding whether to drop a location, if it doesn’t require BP check whether that is
because it has engineers traveling there which are far away, in which case still try and drop
engineers there.
Defending the initial objective
After finally getting M28 to repair the objective, I ran into a new problem – it suffers attacks first by
enemy air units then by enemy land units, and would have nothing but a couple of T1 engineers to
defend with.
As a human player this is relatively easy to defend – transport over some T2 engineers, and
pre-emptively build some T2 PD and flak.
165
However, M28 doesn’t realise the attack is coming, so doesn’t try and get any defences until it’s too
late, at which point it has T1 engineers so can’t do much anything.
However, I also don’t want M28 building such defences pre-emptively normally (i.e. in a skirmish
game).
I see two potential solutions here:
●
●
Have M28 improve how it uses gunships and inties to defend
Have M28 recognise it’s a campaign map with an objective and build up land factories and a
base level of direct fire and AA threat at the location, and hope this is enough to handle the
enemy attack waves.
For example, I could have M28 treat the location as a ‘core base’, meaning it then builds land
factories and air factories, but I’m worried this could have unintended consequences on other maps
(e.g. with M28 suiciding units to an objective).
An alternative could be designating it as a ‘core expansion’ location, meaning M28 tries to build a
land factory there, although I thought it was already doing that (I later realise there was an oversight
where I’d only do this if there were a certain number of mexes in the zone).
Another option is to allow such locations as air rally points, meaning interceptors are closer and so
are more likely to intercept the transports.
However, before that I want to understand why when it had 2 T2 gunships and some inties it didn’t
try and attack the ground force that had no MAA in it.
Investigating, it was because M28 was trying to attack a unit that was outside of the playable area, so
for a campaign map M28 will only try and target a unit with gunships if it is in the playable area.
However, even after M28 engages the first ground force with its gunships (it only has 2 by that stage)
they take too long to arrive and deal damage too slowly to handle the threat, with the objective
being destroyed.
Other changes made in respect of issues spotted from the replay:
1. The location should be treated as a core expansion location with land factories built if there are
units to repair in the LZ (previously it’d only treat a drop location as an expansion if it had 3+
mexes)
2. Units to repair should be removed as objectives if they are at full health
3. Engineers shouldn’t try to reclaim segments whose midpoint is outside the playable area
4. The build power of engineers to reclaim an area should be capped at 5 if engineers have failed to
find something to reclaim in the last 3s.
5. Units should not be given any new orders for 1.5s after being built at a land factory (to reduce
the risk that they are given an invalid order causing them to stay on the land factory and prevent
it building more units).
6. Power should be built at any tech level if there are unit restrictions or a campaign map so t1
pgens still get built if we hit t2 and cant build t2 pgens.
7. Repair targetes should be added to the table of high priority targets to defend (however due to
the location recorded for the enemy base this doesn’t help with making AirAA units stay closer to
the facility, and I don’t want to change this logic due to the negative impact it could have more
generally).
166
8. One other possibility is to see if the transports are created off-map such that my inties can move
to their destination at this point, and hopefully beat them to the destination. Cheating isn’t a
concern here given a human player would know the drops are incoming and it’s a campaign map.
o The transports are created slightly off map but not by much – while my inties
attempt to target them off-map, this fails due to the transport being off-map.
Therefore I could speed up the intie response slightly if I was to target their
destination instead of the transport where the unit is off-map.
o I’ll therefore add a check for interceptors to try and intercept the air unit later on if it
is outside the playable area (on a campaign map).
o This replaces previous logic I had to ignore air units if in a zone that is outside the
playable area.
o On one replay, the transport was first detected at time 1077.59. The OnCreate event
was triggered at the same time. However, in the replay you can see the transport
order a few seconds before this, so I’m guessing that the campaign uses a different
OnCreate event that isn't being triggered.
o The transports themselves are created with:
●
ScenarioUtils.CreateArmyGroupAsPlatoon('Aeon', 'M15_Transports',
'ChevronFormation')
Which is the lua\sim\ScenarioUtilities.lua file
The function will return the platoon with the units in it
I’m therefore able to hook this and trigger the OnCreate event which happens 3s earlier than before.
I combine this with having M28 send ‘OnCreate’ units for assignment to a zone for all M28Brain
teams if it is a campaign map (on the basis a human would have knowledge of where any units
originate from) – I don’t want to do this on all games since it’d be an unfair advantage if M28 had
knowledge of units the moment they were created anywhere on the map (e.g. making sneak nukes
almost impossible).
Unfortunately after testing it seems that the navigator target can be completely different to the
actual target, so I’ll just ignore the navigator and target the enemy air unit instead.
Killing the final base
The above testing was all done on hard since I had a hope I might be able to get M28 to complete it
on hard. Changing the difficulty to easy, and having myself complete the ‘bulid x units and send here’
objective, I ran into the issue where even with a 1k unit cap M28 would get too close to the cap and
stop building, and ended up with almost 150 T1 MAA but only 5 MMLs, unable to break the final
base (instead stuck in a pattern of killing the enemy attacks, then sending its 5 MML to fire a few
shorts, before retreating when the next attack appeared).
The following changes were therefore made to try and reduce the likelihood of this:
1. When near the unit cap, land and air factories below the highest tech level available should
try to upgrade if not at low mass, and otherwise should only build if under immediate threat.
2. Air factories that are below the highest tech level and have built at least 25 T1 units should
try and upgrade.
3. Made it slightly more likely land factories should upgrade in high mass scenarios – i.e. only
requiring we have T2 tech (not T3) for one of the builders before upgrading T1 factories
when high on mass, and including a ‘factory lifetime build count’ as part of the condition.
167
4. When near the unit cap, previously no T2 land units would be built. Now though this should
only be the case if the factory isn’t at the highest tech level or we have had to destroy T2
land units to free up unit cap space.
5. Certain land units shouldn’t be destroyed as early as before if the highest land factory tech
level is less than 3 and it is a campaign map or unit restrictions are present (to reduce the
risk of ctrl-king units when they’re the best tech units available).
6. Added a cap on T1 MAA of 80 if are dealing with a T1 land factory (with the cap reducing if
we have T2 land)
7. If we have air control gunships should stay by the air support point instead of the air rally
point.
ACU available upgrades
The following code is run which prevents the ACU accessing certain upgrades – this causes a problem
as until now it hasn’t needed to check if an upgrade is restricted (instead it only checks if the upgrade
is available via the blueprint):
ScenarioFramework.RestrictEnhancements({'AdvancedEngineering',
'T3Engineering',
'HeavyAntiMatterCannon',
'RightPod Right',
'ShieldGeneratorField',
'TacticalMissile',
'TacticalNukeMissile',
'Teleporter',})
I therefore want it to maintain a blacklist of restricted enhancements via a hook of this function, and
to check this blacklist when deciding what upgrade to get.
This function calls the below:
SimUIVars.SaveEnhancementRestriction(restrict)
import("/lua/enhancementcommon.lua").RestrictList(restrict)
Reviewing the code for this shows a function in the file /lua/enhancementcommon.lua:
GetRestricted()
So hopefully this can just be used and I don’t need to use a hook.
Another issue was air units either staying at the original start (taking ages to intercept a threat), or
suiciding into the enemy base. The following changes were made to try and get the right balance for
this:
1. Changed the approach (added just above) of gunships moving to the air support location as the
air support location could be based on the gunship position, resulting in circular logic. Instead
gunships will use the rally point again.
2. However, the rally point will be moved towards the support location as long as it is safe to do so
(and we have air control).
3. In addition, the support location will be moved towards the enemy base as long as it is safe to do
so (and we have air control).
168
4. Meanwhile the closest safe base will be used (if there is one) if the closest friendly base is
dangerous – to help with cases in campaign maps where an allied ‘base’ is actually in the enemy
base.
5. Fixed several bugs with getting the gunship threat at a location (when deciding whether to
support with AirAA units) which would stop the AirAA logic from working.
6. AirAA units should only consider adjacent zones to a gunship or unit to support if the zone edge
is relatively close to the gunship or unit position.
7. Once gunships have found a target, they should continue considering up to 4 zones to see if any
of those aren’t too far away (compared to the first one where targets were found) but have
mexes or high value structures to protect.
8. For MMLs, in addition to considering whether they are in range of the enemy structure closest to
the midpoint, they should also check if the nearest enemy unit to them is a structure and (if so) if
they are in range of it in roder to attack it (due to issues with attack-move not working properly
for MMLs).
9. T1 power shouldn’t be ctrl-kd when near the unit cap if we have no T2+ power.
10. More gunships should be built at T2 if we have no T3 air fac and have air control (before there
was a lower unit cap at T2 air, on the assumption that T3 air could be obtained)
11. Gunships should engage a greater ground AA threat if they are in a zone with a significant
structure value or with T2+ mexes.
12. When we outrange the enemy (e.g. we have MMLs and they don’t) then units should get slightly
closer to enemy structures before doing a kiting retreat.
13. One issue was the enemy base land zone and some of the nearby land zones are massive (due to
very few mexes), resulting in gunships running too early from enemy threats. When gunships are
deciding if there is too much AA threat, I’ve added a range limitation on the adjacent zones that
get considered, so if the gunship is too far from the zone edge then it should be ignored (i.e.
similar to the logic added for AirAA targets above).
14. Slightly adjusted the lower power threshold to be less likely to think we have low power based
on the current energy compared with the energy when we last stalled.
15. Removed the 80 gunship unit limit for campaign.
16. T2 engineers should no longer be ctrl-k’d if we have no access to T3 factories.
After all of these changes M28 was able to slowly grind down the aeon base.
2.40.5) Other changes
●
●
Toned down the mex upgrade logic to try and reduce instances where lots of mexes would
be upgraded at once despite a mass stall.
Added support for Cybran to get the upcoming Nano upgrade in the expected 15 July FAF
beta release (although I’ve not been able to test it to confirm).
2.41) V12 – M3-M4 UEF campaign
2.41.1) Misc improvements and bugfixes identified from playing mission 3
1. Added support for reclaim objectives (for M3 UEF SC)
2. If there are units in a zone to be reclaimed, then more BP should be requested if any of
those units are reclaim objectives (i.e. the previous logic for M28 wouldn’t request
additional engineers just to undertake reclaim).
3. Increased the distance threshold for engineers to go to other islands that have requested
engineers by 50% on campaign maps
169
4. Added a lower priority action for 1 T1 engineer to travel to far away islands (up to 400
distance away) as a backup incase there are scenarios where there is an island far away
that we still want to send an engineer to.
5. Added a cap on gunships to stop crazy numbers (500+) being built – now up to 200 will
be built.
6. After the above, M28 was finally able to reclaim arnold’s wreck, and proceeded to wipe
out the (easy) campaign AI. However, this highlighted a flaw in the campaign objective
that makes it impossible for an AI to complete the game.
7. Torp bombers should now ignore small amounts of enemy AirAA threat when deciding
targets if we have air control.
8. A fourth air staging facility should be built when there are high numbers of air units and
a lack of air staging facilities.
9. Lowered the eco requirements to build sonar on campaign missions by 70%.
10. Fixed a bug/oversight where the damage assigned to a unit by bombers or torp bombers
wouldn’t get reset when the bomber’s targets changed.
11. Mass storage gifted at the start – instead of following the normal ‘give half of the units to
M28’ approach, mass storage should be linked to mexes and only transferred if the mex
ownership is transferred.
12. Units built from a naval factory should be treated as unavailable for 1.5s to make it less
likely they sit doing nothing (and hence block the naval factory from building)
13. When choosing a ‘bombardment’ target for naval units, a location inside the playable
area should be sought.
14. The playable area should now use the smaller of the scenario info playable area, and the
playable area set by campaign map size adjustments (to help address an issue where
locations inside the campaign ‘playable area’ would actually be just outside the actual
playable area and result in units not moving there)
15. When using the function to move ina direction and keep it inside the playable area, the
distance adjust of -0.1 should use the correct factor to keep units just inside the playable
area (sometimes they could end up being given a location just outside the playable area).
2.41.2) UEF Mission 3 objective flaw
The UEF mission 3 only triggers the logic about the princess having fled the island once Player 1
approaches, and only adds the objective to kill the aeon commander once this happens, meaning if
M28 kills the aeon commander then the mission never ends. I’ll therefore see if I can pick up when
the objective to kill the princess is added, and create my own check on the Aeon commander, and as
soon as it is detected by M28, treat the first objective as complete.
o
As part of the objective, it uses the following:
●
ScenarioFramework.CreateAreaTrigger(M4IslandApproach,
ScenarioUtils.AreaToRect('Aeon_Island_Area'), categories.ALLUNITS,
true, false, ArmyBrains[Player1], 1, false)
●
ScenarioInfo.M4P1 = Objectives.Basic(
'primary',
'incomplete',
OpStrings.M4P1Title,
OpStrings.M4P1Description,
Objectives.GetActionIcon('kill')
)
o
It also sets the actual objective with:
CreateAreaTrigger is:
170
local TriggerFile = import("/lua/scenariotriggers.lua")
CreateAreaTrigger = TriggerFile.CreateAreaTrigger
This in turn is:
---@see CreateMutlipleAreaTrigger() to pass in multiple areas
---@param callback NamedAreaTriggerCallback | AreaTriggerCallback
---@param area Area | Rectangle
---@param category EntityCategory
---@param onceOnly? boolean
---@param lessThan? boolean
---@param aiBrain? AIBrain
---@param unitCount? number defaults to any (or none with `lessThan` set)
---@param requireBuilt? boolean
---@param name? string
---@return thread
function CreateAreaTrigger(callback, area, category, onceOnly, lessThan,
aiBrain, unitCount, requireBuilt, name)
return ForkThread(AreaTriggerThread, callback, {area}, category,
onceOnly, lessThan, aiBrain, unitCount, requireBuilt, name)
end
Meanwhile the later objective (for Eris being killed) makes use of:
ScenarioFramework.CreateUnitDeathTrigger(ErisKilled, ScenarioInfo.AeonCDR)
I was wondering about creating a second areatrigger thread and copying the first via a hook, but that
would probably duplicate the objective and could cause other issues, so I’ll leave it (especially as if
the map ever gets fixed it could cause problems).
Instead, I’ll just call the ‘ErisKilled’ function that ends the game:
● When an objective is added, check if ScenarioInfo.M4P1 is empty
● Also check that the objective is active, via:
ScenarioInfo.M4P1.Active
●
●
If not, check if the objective has no units
If it has no units, check if there is a ScenarioInfo.AeonCDR unit
Where such an objective is identified, I’ll then create a custom ‘end game’ trigger for Eris:
o
o
o
Use the ‘CreateUnitDeathTrigger’ function as per the above.
However, has a custom EnemyACUKilled function
When this function runs, it checks if M4P1 is still active, and if it is, then it calls the
‘ErisKilled’ function.
2.41.3) UEF SC Mission 4
1. When considering combat orders for naval units, enemy units that are outside the playable
area should be ignored when getting the nearest enemy to the midpoint.
2. Fixed a bug with capture target objectives being recorded.
3. When looking for a mex to bombard, only mexes in the playable area should be considered.
4. When looking for a water zone to support, only water zones whose midpoint is in the
playable area should be considered (note even after this there’s an issue with this map as
there is a small bit of water north of the starting island that is in the playable area, leading to
171
M28 trying to send units there since it is pathable if the map was expanded – unfortunately
there’s no easy solution to this).
5. Engineers should no longer be built at a factory if the tech level that would be built is lower
than the lowest tech level that zone wants (i.e. the issue was that you can build a T3 air
factory but no T3 engineers; so M28 would want to get more T3 engineers, and keep building
engineers at the air factory, which would only be able to build T2 engineers, leading to a
massive overproduction of T2 engineers).
6. Added an extra campaign intro message and fixed a bug that meant 2 of the options
wouldn’t appear.
7. Added logic to try and build T2 factories instead of T3 if we have no T3 engineers and there
are unit restrictions, to try and avoid the scenario where T3 support factories try to be built
to deal with mass overflow but there are no T3 engineers to build them due to unit
restrictions.
8. M28 would massively overbuild inties once it reached T3 air, due to wanting to have at least
a couple of ASFs (but due to unit restrictions could never get them).
9. Reduced the threat given by T1 fixed AA to 150% instead of the normal 200% for structures.
10. Made it slightly less likely that land factories will get built where we are on a different island
to the nearest enemy base, and fixed a bug with the decision on whether to get a land or air
factory.
11. Fixed a bug with the decision on whether to upgrade a T2 support air factory to T3, which
would only consider it if there were active upgrades in the zone (that weren’t an air factory),
rather than also considering if there were no active upgrades at all.
12. Fixed a bug for island factories that meant they wouldn’t take into account the friendly
threat in adjacent zones but instead would only consider their current zone when deciding
what to build.
13. More factories shouldn’t be built if a land factor in the zone hasn’t built anything recently
and either the same is the case for an air factory or we wouldn’t be building another air
factory.
14. Increased the proportion of air factories to get vs land factories for campaign games to
compensate for an issue on this map where the nearest enemy base shows up as being on
the same island as our start position (when it isnt).
15. Increased the cap on air staging to 8 (where we have at least 50 air units per existing air
staging and have units wanting refuelling with no available air staging).
16. Added a cap on the number of inties (and airaa units generally) to be built when building in
proportion to our gunship threat, to avoid huge numberies of inties (500+) being built.
17. Increased the value of ponds in campaign, to account for how sometimes the enemy base
will appear to be on the same island as us (despite not actually being).
18. Engineers should consider expanding to an island where the enemy threat at that island is
less than 10 (previously they would only consider expanding if there were no enemy units at
all).
19. Added some redundancies to water zones for scenarios where they might not have a hover
label but do have a non-hover label (although these are very unlikely):
o Made sure that any midpoint has a hover pathing label
o Made sure that adjacent land zones are only added if they have a valid plateau
20. Fixed a bug that meant water zones wouldn’t reinforce adjacent land zones requesting
engineers.
172
21. Increased the distance to consider sending an engineer to another island to a minimum of
125 to account for cases where the enemy base is closer (i.e. campaign maps with poorly
placed enemy base positions).
22. Adjusted the threshold for units in water zones to attack the enemy when lacking a range
advantage – now the enemy threat in adjacent zones should be taken into account, but the
threshold is reduced from being 50% more than the enemy to being 30% more than the
enemy.
23. Also changed the logic for engaging from a core zome from an automatic attack to only an
attack if we have more threat than the enemy.
24. When checking for islands to support from the core zone, a check should be done for if the
island land zone is in the playable area.
25. Core zones should now consider sending engineers to other islands that are in the playable
area, and which have an enemy threat of less than 10 in adjacent zones (previously any
enemy units would cause engineers to not be sent).
26. Updated the types of units to be protected by TMD to include all T2 buildings other than
factories, and all T3 buildings other than factories.
2.41.4) Other changes (unrelated to M3-M4 of the campaign)
Changes made from running an M28 vs RNG game on white fires
● Fixed a bug with the logic for working out what faction’s factories are available to produce
engineers to shield a game-ender.
● Fixed a bug with getting engineers to move from a water zone to a land zone, and another
bug where if the engineers would be sent back to a land zone they could end up never being
treated as available.
● Made M28 more likely to want to build more power even when it thinks it isn’t low on
power, including factoring in higher AiX modifiers, and high amounts of mass stored.
● Capped the amount of build power to be sent to a water zone from a land zone.
● Increased the range to consider sending land experimentals to other islands if the enemy has
T3 arti/similar threats and fewer land experimentals.
Other changes
● Added pre-emptive change for FAF beta balance which will change energy storage capacity –
M28 had been using some hardcoded values, so I’m hoping I’ve picked up all of these (as
they now use a value htat is based on the blueprints at the start of the game).
Pre-release basic check
Another 3v3 against RNG on twin rivers, for a change M28 won comfortably, managing to stay just
behind RNG to the mid-game then quickly racing ahead on eco at the T3 stage.
2.41.5) V13 Hotfix
I forgot to disable M28 logic for AiX being 10x, resulting in a hotfix.
2.42) V14 M5-M6 UEF and M1-2 Cybran Campaign
2.42.1) Mission 5
1. Fixed a bug with gifting mass storages.
173
2. The switch from titans to percies and loyalists to bricks should only happen if the factory can
build bricks/percies. Nukes, TML and SMD should keep building missiles if we are close to
overflowing resources.
3. Hard-coded logic to have M28’s ACUs move to the gateway at the end of the mission so that
it can complete.
SML overwhelm logic
One thing I noticed was that M28 would build loads of nukes, due to wanting to build experimentals,
not being able to, so having a fallback of building any available experimental level unit (including
nukes), with nukes being the only one enabled.
However, it would also not fire those nukes if the enemy had SMD coverage.
I therefore want to support the extreme/niche scenario where M28 has say 6+ nukes, and the enemy
only has 1-2 SMD, such that M28 could just overwhelm the SMD with nukes:
●
●
●
Nukes, TML and SMD should keep building missiles if we are close to overflowing resources.
Once M28 has more than 1.5 + 1 times the number of nukes as the enemy has SMD, at at
least 4 nukes, it should try and overwhelm the SMD with Nukes.
When trying to overwhelm SMD, in addition to ignoring if a target is covered by SMD (similar
to the experimental nuke), the launcher should ignore if a nuke was fired at the target
recently (to avoid spreading out the nukes over every enemy SMD one at a time).
Land zone adjustment
The Zone creation is too big for the central zone:
174
Since both enemy nuke bases are assigned to the starting zone.
Based on mexes the position looks reasonable:
175
I’ll therefore reduce the distance threshold for a zone to be merged into an adjacent zone threshold
on campaign maps.
I will also reduce the size of the starting zones for campaign maps.
I also change it so that once a nearby zone is found, only segments up to the copy zone distance will
be assigned to that zone (instead of the entire square).
After these changes the result is:
Core assignment:
176
Final assignment:
177
Overall this looks much better, and should avoid the issues if units suiciding into the nuke base
(particularly an issue on harder difficulty).
2.42.2) UEF Mission 6 changes
For once the campaign mission completed with no changes needed. However, I noticed a few issues
where improvements could be made, mostly relating to naval type logic:
1. Fixed an issue with battleships not ground-firing a submerged tempest - on investigation I
realised it’s because the tempest blueprint Y size is more than the amount by which the
tempest is underwater. Fixing this means that tempests should now be ground fired.
2. Fixed a bug with determining the enemy’s best combat range, which would lead to naval
units suiciding themselves.
3. Fixed a bug with determining the enemy’s best antinavy range, which would only consider
enemy units in the current water zone not adjacent water zones (leading to subs suiciding
against enemy destroyers).
4. Fixed a bug where naval units were getting the nearest enemy unit even if it was in a
non-adjacent water zone.
5. Changed naval engagement logic to be based on allied threat in current and adjacent zones
vs enemy threat in current and adjacent zones (previously adjacent zones were ignored),
178
and updated submarines so their decision on whether to engage for a core zone is similar to
that of surface units.
6. Fixed a bug with identifying water zones for torpedo bombers to target when there are no
specified torpedo defence zones (this logic was a redundancy so in theory shouldn’t matter).
7. Fixed a bug with recording adjacent water zones to defend, where it would check if the
original zone had already been recorded instead of the adjacent water zone, and a bug that
wasn’t recording this check (meaning the same zone might get included multiple times)
8. Adjusted torpedo defence logic so they will consider further away areas the better our air
control is.
9. Made it slightly quicker for a quantum gateway to be built in campaign than it already was.
10. RAS SACUs should assist quantum gateways for longer in campaign.
11. Torp bombers should still be built at the highest unit cap threshold
12. Torpedo bombers should now check if the target is inside the playable area before trying to
attack it.
2.42.3) Cybran M1 changes
1. Added in a low priority bomber builder for when we are overflowing mass.
2. Added in a low priority air factory builder for when we are almost overflowing mass.
3. Added a low priority AirAA builder for any tech level where we lack air control and have a
decent amount of mass.
4. Low fuel air units should consider self destructing if there are insufficient air staging facilities
available.
5. Expanded bomber targeting logic so lower tech units near the rally point should be targeted.
6. Also added logic for bombers to target further away enemy units where there is air control.
2.42.4) Cybran M2 changes
1. Added a low priority bomber builder even if we have low mass for cases where we have no
gunships (i.e. so we can get some bombers for defence).
2. Added higher priority bomber builder for when there are adjacent enemy naval units.
3. Bombers should now consider adjacent water zones for targeting
4. One issue this mission highlighted was that the Aeon labs that need to be destroyed don’t
show as enemies, and have to be manually attacked. I therefore want very specific
hardcoded logic for this, due to the risk of having M28 attack targets it isn’t meant to.
However, I’m also unable to reclaim the temples which would’ve been the easiest way (for
my code) to handle this. I therefore add custom logic to have bombers ground-fire special
targets, and record these targets when an objective is added and the scenario contains a
table of these temples (since examining the campaign script it records the temples against
the ScenarioInfo data).
● This then caused an issue because M28 bombers would continue attacking ground
fire targets due to being treated as ‘busy’ (as their target would be close by), so I’ve
updated the tracking to allow ground fire orders to be linked to a unit so when that
unit dies, the bomber is treated as available.
5. Made it more likely that energy storage should be built, particularly on campaign maps, at
lower mass levels.
6. Made it more likely the ACU will get its first upgrade on campaign maps after a while when at
low eco levels, even more-so as significant amount of time passes.
7. ACU should try and get RAS upgardes if it only has 1 available upgrade
179
8. ACU should consider engaging nearby enemy naval units.
9. Bombers should try and focus down enemy AA units ahead of non-AA units.
10. Adjusted the range on the ACU’s overcharge – previously it would only conider units 1 inside
the ACU range, now it will consider units inside the range, provided the direction of the ACU
towards the unit isn’t far from the ACU’s facing angle.
11. When ACU is trying to run and is also trying to dodge a shot, regardless of what its last move
location was it should assume it is trying to move to the nearest base, to reduce cases where
it may try attacking the enemy while dodging due to not receiving new orders while dodging
(so going off the old orders that were to attack).
12. Bombers should only consider higher tech nearby units first when we have strat bombers.
13. Increased the proportion of air factories to build relative to land factories for campaign maps
further.
14. One issue with the map setup is that the water near the start position wasn’t showing as
being adjacent to the start position land zone, since it would cross briefly into another land
zone when traveling between the two points. My solution is to allow a certain number of
‘strikes’ before no longer treating a water zone as adjacent to a land zone, based on how far
apart the midpoints of the two zones are.
15. Fixed a bug where the RAS upgrade on an ACU wasn’t factored into M28’s calculation of its
gross mass and energy generated.
16. Increased the search range of bombers further where we have high numbers of them.
17. Added a limit on the number of frigates to be built.
18. Added a higher priority upgrader for land factories where we don’t have low mass
19. Added a higher priority upgrader for land factories based on the number of units the factory
ahs built (with the number required to upgrade being adjusted based on various factories
including mass levels, how close enemies are, and how many other upgrades are active).
20. Minor optimisation to logic for determining how many units a factory has built.
21. ACU should sometimes consider rebuilding a base in its current zone where it loses its core
base.
22. Land units should now try engaging enemy AA units in water (not just enemy direct fire
units), meaning the early shards that get attacked can often be killed by land units (instead of
having to suicide bombers into them)
23. More than 1 naval factory should be built with high amounts of mass stored.
24. For campaign maps the gross mass income threshold to build a naval factory is lower.
25. Changed the order/priority of the water zone logic for sending engineers to an adjacent
water or land zone – the previous priority orders will only apply if the zone has no factory or
engineers in it. Then 2 new lower priority reinforcements are added which will apply
regardless of if there are engineers/factories, but these are now lower priority than logic to
assist the naval factory. As part of this I’ve also expanded the logic to only send engineers if
we have sufficient tech level (previously it just checked if the zone wanted more build power,
so could send a T1 engineer when the zone only wanted a T2 engineer).
26. Added a higher priority torp bomber/gunship/bomber builder for when the enemy is in an
adjacent water zone with a combat threat.
27. Land factories in ‘expansion’ locations should no longer try and maintain a minimum level of
threat in adjacent zones if the factory itself has a large number of units it has built in its
lifetime, and no adjacent enemies, due to the risk of it being in a location with zones
adjacent to the adjacent zones, unless it has built nothing for the past 10s and doesn’t have
low mass.
180
28. The mission still requires input from the player though as the final objective requires player
1’s ACU to enter the gateway. Strictly speaking I suppose the user could setup the game
lobby with the AI as player 1 though, so I’ll add code to have the ACU move the quantum
gate once the objective is added regardless of what player it is. Fortunately because I had to
do the same thing for M5 of the UEF campaign it’s far easier to implement this (taking a
couple of lines of code and not needing to be rerun numerous times to get working).
2.42.5) Misc
1. Fixed a bug added in v12 with certain naval unit management.
2. ACU is less aggressive for campaign missions (based on feedback I had from Pedro_Lima on
Golden Crystals suiciding)
3. Made it less likely that SMD, TML and Nukes will be unpaused for more missiles at low gross
mass values, even with lots of mass stored (i.e. they’ll now check that M28 doesn’t have low
power, and will require 99% mass stored instead of 80% at lower gross mass levels).
4. Fixed a bug with determining locations where naval units can hit a mex, where the
pre-recorded locations wouldn’t check if the location was in the same pond (only if it was in
water) – now it will check if it’s in the same pond, to avoid units from one pond being given
orders to try and move to another pond.
5. Units should be more aggressive if they are at a core base or expansion point and the enemy
is almost in range of that location’s midpoint.
6. Adjusted the decision on whether to request indirect fire reinforcements, so that if we have
more mobile DF threat in a zone than the enemy, and more than we have indirect fire threat,
then adjacent zone threats should be taken into account and if the enemy has structure
threat in an adjacent zone, then indirect units should be requested.
7. Added a workaround for a really strange error where an engineer would be given a move
order yet not move (but show in the FAF engine as being moving) – engineers will now
record their position when moving and if they haven’t moved more than 0.01 in 10 seconds
will be cleared.
8. Added code provided by Sprouto to reduce the risk of errors relating to too many orders
being given at once.
Basic pre-release test – Another 3v3 against RNG on Twin Rivers, M28 looked in trouble early on
when all 3 RNGs rushed M28 with 3 monkeylords at the same time, but M28 was able to recover
with the loss of only 1 of its ACUs and quickly got into an unassailable position.
2.42.6) Acknowledgements:
Sprouto – code to try and mitigate issues where orders weren’t being processed – it didn’t fix the
issue, but I’ve left the code in incase it’s of help for a potential future issue.
Pedro_Lima – Mentioning that M28’s ACU was being a bit too aggressive and dying on one of the
campaign maps.
2.43) V15 M3-6 Cybran
2.43.1) Cybran mission 3
Starting this on hard it was clear that M28 isn’t going to be able to compete (without spending lots of
time on mission specific logic) – M28 takes what would normally be the correct approach of getting a
factory and power with its ACU, while sending its amphibious tanks to attack an undefended enemy
base. However, this leaves civilian buildings exposed to the enemy attack, and since M28 only has
181
half of the starting units it also takes a while to defeat the northern air base. Instead I’ll assume the
human player will send their units to defend the town while M28 attacks the air base. Having the
human playerpatrol their starting 4 amphibious tanks to the west of the town though meant the
town survived and M28 then proceeded to win the game.
However, M28 failed to scale up its build power fast enough and was overflowing mass for much of
the game despite constantly building new air factories. I therefore made the following changes:
1. Core expansion – Increased the number of land factories to build at high mass storage levels.
2. Air factory – Added logic to build a second air factory at the same time as the first for high
mass stored values.
3. When getting units to build, land factories should only consider other zones that are in the
playable area (to avoid it constantly producing say MMLs due to an enemy building just
outside the playable area).
4. When deciding what to do with land combat units, a check should be done that the nearest
enemy unit is in the playable area.
5. When deciding whether to attack from an ‘island beachhead’, units would attack if they
outnumbered the enemy threat in that zone itself. Now they will only do this if there are
enemies in the zone (to reduce the tendancy M28 had of suiciding units into the western
base, provided the attacking patrols are dealt with).
2.43.2) Cybran Mission 4 – General changes
1. Increased the value of large ponds, and ponds on campaign maps, in order that the pond
near the start position is considered worth building in.
2. Added some more caps to the number of inties to be built to try and avoid going much over
400.
3. Added a low priority air staging builder for core expansions.
4. If the bombardment location is outside the playable area (or is targeting the enemy base)
then a search should be done for the nearest enemy unit in a land zone to try and attack
(which results in the destroyers storming the initial southern base instead of trying to attack
an off-map location in the north-west).
5. Added a hook whenever a trigger is created for the capture of a unit, so that the unit is
added as a capture target if it is not an ally (as the previous approach of identifying a capture
objective wouldn’t pick up every case – such as the final objective to capture nodes which
used a different approach). As part of this I also fixed a bug with the previous code which
could happen when checking if a capture target had already been added.
2.43.3) Mission 4 - Not attacking an enemy on purpose
This mission has a strange scenario for an AI where attacking the main enemy base causes you to
lose the game.
My plan for a potential solution is to just have the AI play defensively and rely on the human player
to complete the objective. I.e. I’ll have a global ‘Pacifist’ flag – if they are not in a core base or
adjacent to a core base, or in a core expansion, then the following will no longer be managed, i.e.:
●
●
●
●
LandCombat (based on zone flags)
Naval combat (based on zone flags)
Bombers (not torpedo bombers) – only use defensively (i.e. limit the adjacent search range)
Gunships – only use defensively (i.e. limit the adjacent search range)
182
Meanwhile the following would still be used:
●
●
●
Engineers (e.g. reclaiming) – since I figure they’d die before getting near
Torpedo bombers (as the base isnt on water)
AirAA (as they’d only kill enemy air units which hopefully won’t trigger a mission failure).
However, checking how the script works, it appears to record the triggers and units at the start of the
game. I therefore want a more localised blacklist logic:
●
●
When an objective is added, check if we haven’t triggered the EMP and we have the data on
the units, and havent yet recorded the blacklisted locations. If so, then:
o Create a table of all the land zones that form part of ScenarioInfo.M3_Base
(expected to just be 1 zone)
o Cycle through all adjacent land and water zones, and blacklist them.
o Disable the land combat and naval zone management for blacklisted zones (so they
shouldn’t request reinforcements or engineers), after setting flags for wanting
reinforcements and BP to false
o Manually disable considering targets in blacklisted zones for Bombers and Gunships.
o If a gunship is in a blacklisted zone, disable its weapon (otherwise enable it).
I later disable airaa logic in blacklisted zones as well, since the crash damage triggers the
mission failure.
2.43.4) Misc changes
●
●
●
Removed code added in the previous update to try and increase the memory allocated to
certain tasks as I suspect it may have been the cause of an issue where all replays would
desync from the start.
Fixed a bug with torp bombers not working when the enemy has nearby naval units.
Land factories shouldn’t be paused in a mass stall if they have built fewer than 5 units, to try
and reduce cases where engineers would start building on an island or plateau and the
factory would be paused and take ages to complete.
2.43.5) Billy Nuke defence
●
●
Defending against a billy nuke: My original plan was:
o I want this logic to work for any mobile TML unit, and also units like cruisers, missile
ships and nuke subs, so that M28 is capable of depending against them.
o Record enemy ACUs that have TML or billy nukes in a special table of threats for the
team.
o Have core and minor zones check if that table is empty, and if not, check if we are
within 50 of being in range of the manual missile.
o If so, make sure we have at least 4 non-Aeon or 1 Aeon TMD in that direction.
o Also set the flag to defend against arti, so that we try and get heavy shielding up.
However, after drafting the first half of this, I realised there was an easier and more
performant approach/one less at risk of massively slowing down the game:
o Monitor the position on a team-wide basis of all enemy mobile TML units
o Where the unit moves more than 10 from its last previously recorded position, use
the existing TML/TMD logic to identify targets and threats.
183
o
●
●
●
I’ll also only consider 1 enemy unit per tick where I am calculating all of the targets
and whether theyre covered by TMD – so if e.g. I was to expand this to cover MMLs
and the enemy built 100 MMLs, it shouldn’t risk slowing the game down to a crawl.
In theory, this means I may also be able to make use of this logic for if I get the TML upgrade
on an ACU.
When putting through these changes, I make the following adjustments to the TML/TMD
logic:
o Aeon TMD are treated as being worth 2 normal TMD
o UEF Billy Nuke is treated as being worth 4 TML (when working out how many TMD to
get)
o Aeon missile ships are treated as being worth 3 TML
o The AOE of the TML is taken into account (instead of a hardcarded value of 2)
o Mobile TML are treated as having 10 more range than a fixed TML (to allow for them
moving slightly)
In theory I could use the same logic to build up TMD against enemy MML, but for now I’ll
hold off as M28 doesn’t try and build firebases outside its core land zone, and should be
building air and land to attack MMLs attacking its core zone (which on average are likely to
be a better mass investment).
2.43.6) Cybran Mission 5
●
●
●
Changed bomber targeting logic to consider water zones as well as land zones at high
available bomber levels.
Added an override cap on the number of T1 and T2 MAA to be built.
Part-complete buildings should be assisted if there are available engineers in the zone (to
cover cases where we start building a unit then stop).
2.43.7) Cybran Mission 6 – Objective 1 (destroy the Czar)
Even on easy this proves a difficult mission – M28 is attacked by strats and battleships early on, so
can’t just eco to a strong position, and while it gets asfs it doesn’t build enough to come close to
handling a Czar. It correctly (for a normal game) builds lots of SAMs and aircraft carriers to protect
itself from the Czar but this doesn’t help for when the Czar attacks the control centre.
Trying with M28 as a 1.5 AiX, on Easy, it suffered a similar problem. Even increasing to AiX 2.0 still
didn’t resolve things.
I therefore made the following changes to try and improve performance and the number of asfs built
by the time the Czar attacks:
1. ACU should try and claim any unclaimed mexes in the starting land zone at the start of the
game even if its gross mass income is relatively high
2. T1 mex upgrades shouldn’t be paused in campaign maps in a mass stall (since generally the
risk of them being taken out is lower than a normal game due to the greater focus on
ecoing).
3. Fixed shields shouldn’t be built in campaign early on if we are low on mass, and should only
be built between 10-20m if the arti is at least 60% done (if we have low mass).
4. Once we have a ground AA threat equivalent to roughly 4 SAMs, more should only built if we
don’t hae low mass if the enemy has no air units in the zone and we don’t have a high level
of gross mass.
5. Engineers given to the player at the start of the game should now be shared with M28AI.
184
6. If trying to build a random experimental due to the desired one not being available, if we
decide to build a game-ender it should be cancelled if we don’t have a high level of gross
mass income.
7. Game enders should only be built once we have built at least 3 experimental level units.
8. Air factories should be more likely to be built on campaign maps after the 2nd land factory
(given issues with enemy bases often being nearby when they’re not), provided we have
access to T2+ air factory tech.
9. If there is an experimental air objective that is owned by an enemy unit, then AirAA units
should be much more likely to engage it than normal targets, disregarding significant
amounts of ground and air AA threat, provided M28 has the equivalent of 20+ asfs.
10. Added delayed logic when the map expands to check for specific objectives (since mission 6
doesn’t use the normal approach to adding an objective for a unit target), which will
manually check for if the table of czars to kill exists in the scenarioinfo (it gets added to here
when the czar finishes construction).
11. Attached air units (e.g. air units inside a czar) should no longer be attempted to be targeted.
12. Added logic to try and get more air factories, with up to 10 total factories (air and land) if the
enemy has a significant air to ground threat.
13. T3 naval factory shouldn’t be started early game on campaign maps if we are low on mass
(and more generally if low on mass they shouldn’t be started if the T2 factory hasn’t built
many units).
14. Allied radar and sonar that is created (e.g. spawned at the start of the mission) should be
taken into account when deciding if we need radar/sonar.
15. Add a high priority action for airaa – if campaign map, and have czar and control centre as
valid units, and enemy has air to ground threat of at least 10k, and czar is within 120 of the
control centre, then attack it with everything.
16. Added a chat message when the ‘suicide into Czar’ logic triggers
17. Made it more likely more factories should be built even where the gross mass income isn’t
that great, if the enemy has a large air to ground threat.
18. After seeing 60 asfs die to the Czar without taking out its shield, which I’m assuming is due to
the aoe abilities on the Czar, I then changed the air attack logic so against the Czar it will use
an attack order (whereas previously it would use a move order once it got near it).
19. ASF production - If the enemy has at least 10k air to ground threat, we already have a base
level of torp bombers (or don’t need them) and gunships, and have asf threat less than the
lower of 20k and 50% of the enemy air to ground threat, then build asfs as a high priority.
20. Reduced the amount of ground AA to be built at very high threat levels, as well as when only
T2 AA is available.
21. Further reduced the amount of AA to be built in campaign maps early on (since on this map
the Aeon player starts with a huge air force that could easily wipe out the base if it attacked,
leading to M28 overbuilding SAMs and flak when it could be ecoing).
Prior to these changes, M28 couldn’t get past the first objective even at AiX 2.0 on easy difficulty.
After these changes, it’s able to manage AiX 1.0 on hard! (that said, there’s no significant difference
in difficulty between easy and hard for this objective).
Changes from the rest of the mission:
1. Final objective – if M28 captures black sun, then M28 should ‘fire’ it (i.e. it should end the
game after a slight delay) – I’ll check this whenever M28 captures a unit (since the objective
to fire black sun triggers when black sun is owned by the UEF).
185
2. Experimental gunships should use an attack order instead of a move order (i.e. they should
ignore the normal gunship logic)
3. Adjacent enemy structure threats should only have 20% of the threat value included where
we have at least 10k threat in the zone.
2.43.8) Other changes
●
●
Land factories should be less likely to start upgrading an HQ at the same time as another HQ
(previously they would try and upgrade once reaching a certain number of units built – this
threshold is increased significantly where there is already an active HQ upgrade).
Part-complete fixed shields should be ignored when considering units to assist (where there
are part complete buildings in the zone).
2.43.9) Acknowledgements:
●
FenixThorn – replay highlighting issues with M28 upgrading multiple HQs at once
2.44) V16 M1 Aeon
2.44.1) General changes
1. Added some redundancies in the map generation to try and cope with Seraphim mission 2,
before discovering the reason for the errors was that the navigational mesh wasn’t working
at all. Adding a full redundancy for this would be a lot of work and slow down the M28 code
generally so I’ll probably wait to see if it’s possible for it to be fixed.
2. Hopefully fixed a compatibility issue with the StoneAge mod that would cause the game to
crash. I’m not sure on the cause, but after checking for the specific way StoneAge adds unit
restrictions (which is different to if a player adds them) pre-emptively I was able to avoid 3
games crashing (playing for 20+m) compared to the game I ran without the mod that crashed
after about 15m.
3. Fixed a bug with determining how many engineers to build early-game
4. Fixed a bug in the logic for deciding if more factories are wanted that would result in lots of
errors.
5. M28 should try and go second air if it has friendly teammates closer to each enemy than it is.
6. Air HQs building a transport shouldn’t be paused during a mass stall.
7. Air HQs building their first unit shouldn’t be paused during a mass stall.
8. Removed the logic for flagging a unit as a capture target when a capture trigger is added
unless it’s a campaign map and the unit is mobile (to stop e.g. M28 trying to capture RNG
engineers).
9. Fixed an issue where FAF prevents engineers reclaiming an actively constructed building or
experimental – now engineers will try and target other units.
10. Engineers should try and reclaim the closest dangerous enemy unit in its build range if it has
one (in priority to non-dangerous units) – i.e. any unit with a combat range (direct or
indirect) or a unit that can reclaim.
11. Units shouldn’t try and go to an air staging unit that hasn’t finished being constructed.
12. Land factories on islands or plateaus should no longer be paused in a mass stall if they have
built fewer than 10 units.
186
2.44.2) Aeon Mission 1
Naval factory production
I wanted to add some logic where if M28 failed to build a land or air factory, it would move to the
nearest water zone inside the playable area and build one there.
The problem is that the water zone midpoints of all water zones were outside the playable area, with
the closest one shown below:
This then caused another problem – the ACU would head to the water to try and build the factory
before it had completed the mex and power objectives.
I therefore adjust so the logic only triggers if the ACU is capable of building a naval factory.
Other changes for mission 1
1. Added a low priority builder for subs, surface combat, and AA units from a naval factory up
to 30 of each, when overflowing mass.
187
2. Added a low priority bomber builder when almost overflowing mass and not at the highest
tech level.
3. Added a unit cap on t1 AA boats for most build conditions
4. Added a redundancy for engineers to not get new capture orders if they are already
capturing a unit, to avoid cases where they would keep switching between different
perceived capture objectives ending up not completing any of them.
5. ACU should try and build an air factory if we have none in the zone and are unable to build a
land factory.
6. Added a builder for bombers, torp bombers, gunships and airaa units based on if the enemy
has units in a zone when considering nearby zones in a straight line distance.
7. ACU should try and build power if high on mass and below 500 energy per sec
8. More naval factories should be built if we have 1 and are high on mass.
9. Naval units that don’t appear to be in a water zone should be reassessed periodically (rather
than forgotten about and effectively becoming invisible to M28).
10. Fixed an issue where a water zone could flag that it had dangerous enemies in an adjacent
zone, but no enemies in an adjacent zone (i.e. if it couldn’t find enemies in an adjacent zone
it would override the flag for if there were enemies, but wasn’t also overriding the similar
flag for if there were dangerous enemies).
11. Fixed a bug where to determine if there were enemies in an adjacent water zone, every
water zone was being considered (so it would always return true, leading to strange
behaviour).
12. Torpedo launchers should now be included in the units to be assigned to a water zone, as
previously they weren’t showing up in checks of nearby enemies (leading to units suiciding
into them).
13. Submarines should now record if their last shot is blocked, with this logic working for
underwater shots (previously it was only intended for above ground direct fire shots).
14. Submarines that get too close to the enemy should attack-move to it instead of moving even
if their shot appears to be blocked, due to an issue where subs don’t fire at an enemy unit if
ontop of it
15. Retreating/supporting water zone units should only be treated as having received an
assignment if they belong to the water zone giving the orders (similar to the approach taken
for land units).
16. When entering a new water zone units should have their assignment value set to 0
17. When choosing the best build location only those in the playable area should be considered.
18. When searching for potential build locations, the ‘cycle size’ (i.e. the n for every nth entry
that gets considered) is reduced if it is more than 30, to 50% (for water) or 75% (for land),
due to issues where every build location (5k+) was mapped for the water zone in M1 near
the starting base, but none of the 79 locations chosen for consideration via the nth cycle
approach was in the playable area.
19. The ACU when trying to build a land factory should search for buildable locations (to mean
we reach the max mapped buildable locations much sooner).
o Massively increased the number of locations/segments to search when the ACU is
trying to build a naval factory in campaign, since even with these changes it wasn’t
working through enough fo the water zone segments to locate those inside the
playable area.
20. Added factory rally points, to try and further avoid issues where a unit gets built from a
factory and blocks future units at that factory.
188
21. Naval factories no longer require a water start to build engineers with a higher priority when
not low on mass.
22. If an ACU was recently given a build order but doesn’t have a valid order (e.g. due to unit
restrictions) engineer tracking should be cleared so engineers don’t try to assist it when it is
doing nothing.
23. MAA ships should factor in enemy anti navy range when deciding whether to retreat.
24. Fixed a bug with air factories for deciding whether to build bombers if they have no gunship
threat and minimal bomber threat.
25. Engineers should no longer be treated as available if their unit state shows them as capturing
or reclaiming.
26. Added redundancy to end the game at the end after c.40s due to the game not seeming to
end normally (it might have been because it was a desynced replay, and I don’t really want to
re-run it again to try and clarify further).
Water zone creation adjustment
I was still having an issue after these changes of units suiciding into the enemy torp launchers. After
further investigation, the issue was with the water zones being too small in some places:
189
i.e. units in water zone 4 would try to move to water zone 8, and end up passing in range of the
torpedo launchers in zone 9. Those torpedo launchers would effectively be invisible, as M28 would
only check adjacent water zones (i.e. zones 8 and 10) when considering what to do with units in zone
4.
I made the following adjustments to try and resolve:
1. Adjusted the water zone creation logic so if zones are created based on underwater start
positions they will be slightly smaller but nearby other zones will check they aren’t within
roughly 15 segments of this.
2. Reduced the distance by which the initial start point for a water zone will search for nearby
water from 50% of the search interval to 35%, to try and reduce cases where tiny water
zones get created.
3. When assigning water segments that have no zone assigned, before creating a new zone they
should check if the location to the right and bottom-right are also unassigned (and if not
then look to add the zone to the nearest zone).
190
4. When deciding to assign to the nearby zone, it should look for adjacent segments in the
opposite direction to the loop, since otherwise it would almost always end up picking the
previous segment’s zone leading to very thin potential paths.
After these adjustments, the result is:
So while not perfect, the tendency for a very small water zone that keeps 2 other water zones apart
is reduced.
Naval attack logic – current approach
After all the above changes I still ahd the problem of units suiciding into the enemy T2 torps,
meaning M28 could never build up a strong enough force to assault them (and I don’t want to
change its core logic of ‘kill enemy navy first before trying to bombard shoreline structures, despite
this map clearly being intended that you kill the shoreline structures first).
Where naval units are outranged by the enemy, the threat currently works on the following basis:
‘calculate all our threat in this and adjacent zones, compare to enemy threat in adjacent zones, and
attack if we have significantly more’, with a few adjustments such as excluding most of the enemy
191
structure threat in adjacent zone (to try and avoid M28 thinking the 20+ T2 torp longers far away are
a threat when it is trying to attack somewhere much closer).
This results in a single file line of frigates that charges towards the T2 torp launchers, then retreats as
soon as they enter the zone with the torp launchers (since they now have less allied threat as they’re
considering differnet allied adjacent zones) and greater enemy threat (since all the torp launchers are
now in the same zone not an adjacent zone).
I therefore plan to completely rework how this decision is done (again):
Naval attack logic – new approach (when don’t outrange the enemy)
● For each unit record its highest indirect/DF/Antinavy range (for optimisation so I can then
just refer to this range once)
● When recording threats, record threats with a range of at least 50 in a separate table
● Include 100% of the long range threat that is almost in range, and only 20% of any other
threat.
o i.e. include 20% of all threat, then +80% of the almost in range threat
o Decide whether a unit is almost in range based on its best range, and if it is within
that of getting inside the water zone min/max X and Z box (with the range adjusted
down for structures)
● When deciding whether to attack if outranged, if the enemy unit is in another zone, the test
should be done based on that other zone (not this one), so we are less likely to attack and
then immediately retreat.
● In addition, a slightly higher threshold is required to attack an adjacent zone with no combat
units in it than it is to attack a zone that already has combat units (to reduce the risk that we
attack, then 1 unit takes damage and we immediately retreat).
● Meanwhile when retreating, units should stay in the current zone if there is enough available
threat to beat the enemy when factoring in available units. In addition, they should flag as
needing reinforcements.
After all this I finally got M28 into the position where it could attack and kill the torp launchers, only
to be foiled by an indestructible T2 PD (which wouldn’t die even when on 0 health) – checking the
map script this looks intentional. However, after a while like this M28s engieners were able to
capture/reclaim the AA defences and the czar attacked. That said, I’ll try and avoid this scenario by
having units treat their shots as being blocked if they’re targeting an unkillable unit, meaning they
move closer (which in this case should hopefully resolve things as they were trying to attack-move to
the torpedo defences).
2.44.3) Acknowledgements:
●
●
Sladow – flagging the incompatibility with the stone age mod
Saver – replay with error/crash
3) Future possible todo points by area
Non-M28 related - Consider logarithmic score screen – see Jip DM with link to code
FenixThorn replay in DM (Setons) – cant get it to run
goblinsly2 replays for M27
M27 upcoming FAF beta acu enhancement change
192
Human-AI interface:
●
●
●
Relent0r mentioned about AIHandlePing sorian function
Sprouto mentioned LOUD can interpret chat for orders.
Ideas of possible inputs:
o Specific text prompts for gl/hf/similar type messages at start
o Get AI to adjust land threat at a location (i.e. increase/decrease [threat type] at
position by [x]
o Get AI to fortify a chokepoint/setup a firebase
3.1.1) SC Campaign – complete on easy
3.1.2) Spread out SAMs
Spread out SAM/GroundAA construction to provide greater area control:
●
●
First SAM/Fixed AA of the cur tech built as normal
2nd+ - If no enemy air to ground threat in the current zone, apply spread out logic; the first
one gets built as far towards the enemy base as possible in the current zone
o Start by
3.1.3) New navmesh
Test out Jip’s new navmesh to see if it works as expected on both campaign and skirmish
3.2)
●
●
3.3)
●
●
●
●
●
Non-M28 related (so don’t forget)
[Unrelated point – look into hitboxes and stinger? But see if balance team responds first so
not wasting time?]
[Unrelated point – SpawnACU – test on SC orig campaign as review says it doesn’t work]
Misc changes
Relent0r suggestion – on 20km+ maps add a distance cap for engineer sharing between
bases
When reserving locations for an experimental, if the active monitoring code actually triggers,
then before the main while loop if we only have 2 build locations for the shields, consider if
we have t1 buildings blocking the expected 3rd location and there are no other buildings
there, and if so consider ctrl-king those units if they are T1 non-mex non-hybro buildings (i.e.
lower value)
See v10 changes made, strikethrough text re changes to blacklist T3 power build locations for
T3 arti gameenders.
Don’t build PD at base if have a friendly base inbetween our base and enemy threat – e.g.
twin rivers ended up having one base build lots of T2 PD when in the rear position (3v3 vs
RNG AiX 2.0)
If detect enemy has experimental, or are UEF in teamgame with team mass of at least 250,
want to build a fatboy as a high priority – i.e. even if have low mass (and also want to start it
sooner if don’t have low mass) – consider having a new ‘buildfatboy’ action? And then if are
deciding on experimental to build will check for queued fatboy construction in the zone?
o Could more generally have logic to rush the first land experimental, where will
devote 50% of team’s income to the land experimental production, and pause all
non-essential production if mass is low while building this.
193
●
●
●
Coordinate ahwassas to attack shielded positions if have multiple?
If ahwassa is 5s from firing its bomb then don’t retreat when enemy AirAA appears
Similar logic to M27 re cancelling upgrade if ACU taken significant damage recently; also
want ACU to be closer to main base to do an upgrade (in core base) - i–e. atm just have a
check if <50% health, think need to improve further so if ACU is in core base will wait until it
is 20 from the start of the core base or is near T2 PD
3.3.1) Campaign notes
Omni in a forward base with low intel coverage (to help give us intel of parts of map further away)
Make forward base core base if it’s the closest forward base to
3.3.2) General misc
1. V8 – Cybran t2 destroyers were getting too near enemy and getting into T2 PD range
(when pond was won) – e.g. Africa right hand pond – review bombardment logic
2. Should land experimentals move away from an enemy nuke target similar to a yolona?
Might be a bit too powerful so alternative to approximate human behaviour would be to
move either close to base or towards enemy base based on nuke target?
3. [When next FAF develop releases as at May 2023] – Remove hook of navutils for the
precise getlabel alternative code
4. Very high gunship threats – consider targeting enemy ACU? Would mean changing
weapon prioritisation as well as the unit to target.
5. Enemy has significant air to ground threat and ACU is flagged as needing to retreat to
core base – if ACU is at core base then build a T3 shield, and then have ACU shelter
under it.
6. When upgradeable building is created – add to pond if itsenemy and we had vis of its
predecessor? – eg eenemy upgrades t2 air to t3- want to kow they havet3
7. Add in logic for defensive nukes, especially if have yolona – Testing in sandbox, a UEF
missile launcher firing directly at the position of a GC (that is moving towards it) that is
approx. 125 away ends up missing entirely – i.e. even at short distances it requires
predicting where the enemy will be; given complications with friendly bases being
nuked, would be risky.
8. Factory HQs shouldn’t be upgraded from T2 to T3 if there are T1 mexes in the same zone
as the factory unless there are both active upgrades in the zone, at least 50% mass
stored, 2k mass stored, or the enemy has T3 air
9. Need to treat mexes as safe if they’re inbetween friendly bases and no enemies in
adjacent LZ
10. Arent escorting land experimentals with MAA - see if can reproduce in a shorter
timeframe to test
11. Need to get better at escorting battleships with cruisers if far behind on air, and keeping
cruisers out of harm’s way
12. First units from a land factory on an island tend to be MAA; however if we are on an
island rather than plateau then a tank would be better if there’s no air to ground threat
nearby.
13. Gunships returning to rally point/support point to refuel –stay in formation so they’re
not clumped together when moving out again?
14. Cruisers are staying within enemy T2 arti range – need to review bombardment and
more general logic for them.
194
15. Have non-primary engineers released if have a higher priority action
16. Only build the first land factory adjacent to a mex, not others (so don’t have to worry
about losing storage adjacency)
17. High mass scenarios – consider building land experimentals at non-core bases that have
land factories in them (but only land experimentals) – e.g. if we have >40 mass income,
and >=50% mass stored (or 20k if lower), then trigger the builder.
18. Engi reclaim logic (test via sandbox) – consider switching reclaim targets if are reclaiming
a building and have enemy reclaimable mobile unit get within 2 of our build range (so we
don’t e.g. keep reclaiming a factory while an enemy engineer comes to reclaim us)
19. Consider better pathing logic for distance between nearby water zones – e.g. dual Italy
20. Mobile shield assignment – will need to have them only assist surface units if their shield
is small
21. Similarly for mobile stealth
22. Also don’t want naval shield boat to shield amphibious or hover units since they could
move back onto land while the shield boat couldnt
23. Usage of subs may need revisiting once have naval production logic in place – wont have
been properly tested yet
24. Capturing units – have zone based logic so each zone records civilian units that can be
captured and engineers will try to capture if they are of value
3.4)
●
Experimental type units
Czar and soulripper – for now they use gunship logic
3.4.1) T3 arti wars logic
Various things that started considering for M27 or new logic that could look to implement:
●
●
●
●
3.5)
●
●
●
Use of Shield SACU to provide shielding? Probably too slow a refresh rate to be worth it
though, except if building game-ender, where it could provide a valuable backup option for
when shields fail
Syncrhonised T3 arti firing to break through shields
Prioritising faction with highest T3 arti to be built (i.e. Aeon) – can potentially do this now
although not sure that logic (that is in place for shielding game-enders) works properly so
would need to test
Reserving build locations for power, and/or picking a location where can fit T3 power
adjacency and shielding – have got for shielding now, could try and add for power
Air logic
T1 bomber engi hunter (similar to M27) – for the first 1-3 T1 bombers (potential future todo
action)
T3 strat mex hunter (similar to M27) – only intended for the first 2 strats, with strats not built
after this (potential future todo action).
T2 bombers (plus any other T1-T3 bombers) used defensively where enemy has a large AA
threat, in suicide missions (as a last resort) – i.e. this is M27’s main focus, whereas I want it to
be much less of a focus for M28 with the main focus being gunships (potential future todo
action).
195
3.6)
●
●
3.7)
Land zone reinforcements
When deciding whether to build indirect fire units for a LZ that wants indirect fire - Will want
indirect fire units with a range greater than enemy DF (so T3+ only if enemy structure range
is >=58), and with a threat equal to enemy DF and IF threat.
When units are traveling to a land zone, record that they are traveling there; when deciding
on whether to produce for a land zone, factor in the threat already assigned to travel there,
and only build more to satisfy that LZ if it doesn’t already have enough threat.
Mobile stealth
I may have already implemented the below?
Deciding if a LZ wants mobile stealth
● If a land zone contains T2+ skirmisher or T2+ indirect fire units, flag that it wants a mobile
stealth generator.
Stealth assignment
● Assign to the closest LZ that wants a mobile stealth generator and doesn’t have one
assigned.
Deciding if mobile stealth is available for assignment
● If it has an assigned LZ, and that LZ doesn’t want mobile stealth, then make the stealth
available
● Also make available if it doesn’t have an assigned LZ.
Mobile stealth orders
● If we aren’t in the target land zone, move to the target LZ midpoint
o If we are in the assigned land zone, find the friendly unit closest to the enemy base,
and aim to be max(50% of the shield radius – shield speed – unit speed), 3) in the
opposite direction
3.8)
Economy management
3.8.1) Factoring enemy TMLs and long range units into upgrade decision
For now the below function just consideres if enemies are in the current or adjacent LZ:
SafeToUpgradeUnit
M27 had logic factoring in if there were TML in range – once have TML and TMD code implemented
review this.
3.8.2) Power stall enhancements
Active powerstall logic if ACU in combat and <80% health and is able to use overcharge but cant, and
we have at least 50 gross E per tick
3.9)
ACU logic
3.9.1) ACU upgrades - alternatives
mirror M27 approach re getting T2, RAS etc. if enemy base is far away
3.9.2) Underwater actions – consider what happens/if need any extra code
Ignore logic for attacking nearest enemy if the ACU is underwater.
196
3.10) Engineer logic
3.10.1) Reserved build areas
●
Whether should try and setup reserved building location idea – in particular for T3 arti and
shields.
o E.g. one option is to just have certain parts of the map that remain unbuilt on except
for emergency defences so that we can build a T3 arti there if we don’t face much
early game pressure – so t3 arti we would try and build behind the base if there’s a
large enough space; at start of game we’d try and find a location for a shield that at
T3 would cover the most mexes without denying adjacency, etc.
o Could also e.g. have a ‘reserved for building size x’ location, so if we fail to find
somewhere to build randomly, we would make use of this, and once used we would
then get a new ‘reserved for building size x’ location
3.10.2) TMD defence – factoring in unit upgrades
Search for below – M27 had logic but will want land zone based approach so likely redoing TMD
defence logic
--T1 mexes - if start upgrading, then flag for TML protection --TODO in a
future version
3.11) Quick reference land zones
This is to help me when debugging.
3.11.1) Theta passage
22 Jan 23 version:
197
V10 land zones:
198
199
3.11.2) Four-Corners (23/03/2023):
200
Download