I have been wanting to get a working AI for my mod SC:TA for some time now, however in it's current form, it is not easy to add support for new factions into the existing AI, especially as the new factions do not follow the same mechanics as the FA ones. i therefore eventually decided to write a new one from scratch. While this may sound crazy, at least on the surface it' not as big a project as it appears.
Firstly, some context.
Total Annihilation AIAnyone who has played with the stock AI in Total Annihilation will know that it's fairly rubbish. However, there are some excellent 3rd party AIs available, the one I used was Queller. The ability to mod the AI was actually fairly limited. It mainly consisted of assigning a probabilistic weighting to each unit that determines it's likelihood of being built, as well as a limit on the number of each unit that can be built. The next thing that a construction unit built was then determined by the randomly selecting from it available build options, biased by each units weighting score. By simply adjusting these weights and limits, you could affect the effectiveness of the AI. There was more logic behind the scenes (causing the AI to build resource units when low for instance) but the core of the AI was these weights and limits.
When making a new AI, its easiest to break it down into two sub-systems: Building and Attacking. I will start by discussing the building sub-system. Please note that this is still heavily a Work In Progress
Building sub-systemIt's important to consider two key differences between the fa buildings and TA buildings:
1. There is no adjacency.
2. Buildings that are built next to each other do not allow mobile units to move between them.
The first point means that there needs to be no particular logic to placement of structures relative to one another. The second point however, requires a base layout that will not cause units to get trapped.
I also wanted the AI to be more data-driven than internal logic driven, which would mean modifying an AI would be as simple as editing some tables, rather than proper lua. It also lets AIs be tailored per map if desired.
All this AI data is stored in a single table called, surprisingly, "AIunitData". Each unit in the game will have an entry in this table. Currently, units that do not appear in this table will not be built, but I can easily make this instead build them with default values.
The entry looks like this:
Code:
<unitCode> = {
Weight = {
Base = 0.1,
Expansion = 0.1,
Naval = 0.1,
Defense = 0.1,
},
MaxDistance = {
Base = 100,
Expansion = 50,
Naval = 50,
Defense = 20,
},
Limit = -1,
Moods = {
{Required = {'CATEGORY1', 'CATEGORY2'}, Excluded = {'CATEGORY3'},},
{Required = {'CATEGORY4'}, Excluded = {},},
},
Special = false,
SpecialPlacement = nil,
SpecialPlacementLookDistance = 0,
ExpectedMassUse = 0,
ExpectedEnergyUse = 0,
MinimumSeparation = 0,
},
<unitCode>: The unit's blueprint name ("ARMCOM", "CORMEX" etc.)
Weight: Build Probabilities at different marker sites. These are weighted against other units that a construction unit can build to determine the final unit that is built.
MaxDistance: How far out from a give marker type a unit can be built (not used for mobile units built by factories)
Limit: The maximum number of units of this type that can be built. -1 = unlimited.
Moods: See Below.
Special: Indicates this unit wont be built unless some criteria is met. Currently handled on a case by case base in lua code, for things like dragon's teeth and minelayers.
SpecialPlacement: Whether the unit needs to be built on a special marker type (such as "Mass" or "Hydrocarbon"). nil can be built anywhere
SpecialPlacementLookDistance: How far from itself a construction unit will look for the above markers. It will choose the closest free one.
ExpectedMassUse: Plan to use this to monitor total resource use (to prevent multiple factories and/or construction units starting construction at once).
ExpectedEnergyUse: Plan to use this to monitor total resource use (to prevent multiple factories and/or construction units starting construction at once).
MinimumSeparation: This unit shouldn't be built closer than this amount to another one of itself (for things like radars).
Moods:Moods are like states, however multiple can be active at any one time. Currently, they are:
Code:
ECONOMY --Resources are low, build economic structures
MASSSTARVED --Mass is nearly empty
ENERGYSTARVED --Energy is nearly empty
MASSFULL --Mass reserves are full
ENERGYFULL --Energy reserves are full
ENERGYSURPLUS --Energy reserves are full and a large energy income
PRODUCTION --Used for factories, True when a reasonable amount of resources are available
UNITPRODUCTION --Used for unit production. True when a small amount of resources are available
DEFENSIVE --Used for constructing (usually expensive) defences. True when a medium amount of resources are available
INTEL --Used for constructing Intel structures. True when a small amount of resources are available.
DEFAULT --Always True
These are currently hardcoded, I plan to move them out into a table, in the same file as the unit data, so all modifyable data is in one file (and can be swapped depending on difficulty, map etc.)
These moods can then be used to filter which units/structures can be built at any one time. Each entry in the Moods table in the AIunitData entry is a build condition (logical OR, any 1 condition needs to be true for the unit to be able to be built). Each condition has two parts, Required and Excluded. EACH mood listed in the "Required" part needs to be true, while NONE of the moods in the "Excluded" part can be true. If these checks are met, then the condition is true and the unit can be built.
Once a list of units that a given construction unit can build is created according to the rules above, one is chosen at random (weighted accordingly). Then a position is found to build it. I've imitated the Total Annihilation base building style, building in 8x8 blocks with a 8 square wide gap between each block. Note that the blocks are actually bigger than this, as only the *centre* of the unit needs to be in the block. This typically results in 4-6 structures per block, depending on their size, and it keeps corridors clear for unit movement.
Attacking sub-systemTo be completed.
To be continued...