Skip to content

AI triggers and team production

Draws one weighted AI trigger per house on a timer and turns the TeamTypes it names into recruitment and production demand.

Features & systemsScenarios & AIAI & teams

Changed in 0.1.0

This section introduces the two entities the rest of the page turns on. Anyone already writing TeamTypes and [AITriggerTypes] records can skip to the pass in outline.

A team is a group of infantry, vehicles and aircraft that one house owns and that acts together; a computer house’s raids, its attacks and its base defense all arrive as teams. Teams are not written into a scenario directly. What a scenario carries is a TeamType — the definition a team is built from — and a team is created from that definition and then filled, either with objects the house already owns or with objects it builds for the purpose. A team that reaches full strength is started, or set in motion, and begins working down its Script — after a pause to close ranks, which is the first thing team execution covers. What it does from that point onward is outside this page.

A TeamType is one INI section that points at two others. The table names the three sections and what each one contributes to the mechanics below, so a reader tracing a misbehaving team knows which of the three to open.

Where it is writtenWhat it contributes
The TaskForce section named by TaskForce=The roster: up to six <count>,<ObjectType ID> lines. The total of those counts is the team’s full strength — the number of members it wants. It is a roster figure and has nothing to do with health
The Script section named by Script=The ordered team missions the team carries out once it starts. One of them matters here: reaching Success is the only thing the track record counts as a win
The TeamType section itselfWhich of those two to use, which country House= hands the team to, which Waypoint= it starts at and recruits around, and the settings in the next table

A team raised through an AI trigger belongs to the house whose pass raised it, whatever country House= names; that setting decides the owner only where a trigger action creates the team instead.

Eight further TeamType settings reach the decisions on this page. The table points each one at the section that covers it, so a reader who came for one setting can go straight there.

SettingWhere it acts
Max=How many teams of the type one house may hold. A TeamType that never sets it is rejected before the draw, so no AI trigger can raise it
IsBaseDefense=Marks the type defensive, which is what the team budget counts, caps and culls
Reinforce=Keeps recruitment open after the team has started
Recruiter=Lets recruitment look outside the team’s group
Group=The group recruitment matches on, and the group it stamps onto each member
Priority=Which of two teams wins a member they both want
Autocreate=Sets the autocreate mark from the start
AreTeamMembersRecruitable=What a joining member’s autocreate-recruitable state is overwritten with

Autocreated is a mark on the TeamType rather than a description of anything the engine does on its own. Autocreate=yes sets it as the rules are read and being drawn by an AI trigger sets it too, permanently. All the mark decides is which of an object’s two recruitable states a team of that type consults; recruitment covers the pair.

An AI trigger is one line in [AITriggerTypes]. It has nothing to do with the tags, events and actions a map uses: nothing springs it, and it does nothing of its own beyond naming teams. Each line carries a first TeamType and optionally a second, one condition to test, three weights, and a set of gate fields — difficulty flags, a side, a campaign owner, a skirmish flag. A house never asks for a team directly. It asks for a trigger, and takes whichever TeamTypes that trigger names.

Written out in full, one trigger and the three sections behind it look like this.

AI.INI, AIFS.INI, or map file
[TaskForces]
0=MyRaidForce
[MyRaidForce] ; example TaskForce
Name=Raid force
0=3,E1
1=1,TTNK
[ScriptTypes]
0=MyRaidScript
[MyRaidScript] ; example Script
Name=Raid
0=0,1 ; attack anything
1=49,0 ; Success; without it every raid team records a failure
[TeamTypes]
0=MyRaidTeam
[MyRaidTeam] ; example TeamType
Name=Raid team
House=Nod
TaskForce=MyRaidForce
Script=MyRaidScript
Max=4
Priority=8
[AITriggerTypes]
MyRaidTrigger=Raid a blacked-out base,MyRaidTeam,<all>,0,3,GAPOWR,0000000000000000,8,1,20,1,0,0,0,<none>,0,1,1

The trigger record’s fields are covered by the AI triggers page. Three of the values above are worth naming here: Max=4 is what makes this TeamType reachable at all, Priority=8 lets a raid team take a member off a team left at the default 7, and condition type 3 fires while the enemy’s power output sits below its drain, so the comparison block is unused here and the condition object is never counted.

A house raises teams on a countdown of its own. Each time that countdown reaches zero the house asks for one suggestion: a single AI trigger is drawn at random from those whose gates and condition currently hold, weighted by a record the trigger keeps of how its past teams fared, and one team is created from each TeamType it names — the first, and the second when it has one.

Filling those teams is then the teams’ own work, and the members they are still missing are ordered through the same production the house runs for everything else. A computer house orders structures, vehicles, infantry and aircraft together in one pass; a house whose credits have fallen below AIAlternateProductionCreditCutoff narrows to one class at a time, and no house narrows in a campaign game.

The countdown starts at 175 * <the house's position in the house list> + TeamDelays, so houses do not all run their pass on the same frame, and it is reset to TeamDelays alone after every pass. Every house runs the pass, computer or human. Whether it reaches the AI triggers depends on two further gates: a 1 to 100 roll must come out at or below the house’s RatioAITriggerTeam, and the house’s AI-trigger switch must be on. When either fails the pass ends with no suggestion and the countdown restarts anyway; a failed roll does not raise some other kind of team instead.

The switch starts off for every house, and exactly four things turn it on or off. The table shows what each of the four does to it; outside a campaign game the third row is the only one that fires on its own, which is what the caution below turns on.

Written byEffect
AI triggers begin…On, for the house the action names
AI triggers stop…Off, for the house the action names
An MCV deploying into a construction yardOn, for a non-human house outside a campaign game
A house passing from a human player to the computerOn, provided the house still holds a construction yard

Before any trigger is examined the house counts its live teams and how many of them come from an IsBaseDefense=yes TeamType. Every team the house owns is counted, including teams the scenario placed.

Those two counts then select one of two branches. The table sets both out; the difference that matters is that only the second branch ever deletes a team, and that the second branch passes defensive triggers over regardless of how few the house holds.

The house’s countsWhat this pass does
Team count below TotalAITeamCap, or defensive teams below half the team count rounded downNothing is deleted. Defensive triggers are passed over for this pass only while the defensive count is above MaximumAIDefensiveTeams
Neither: the house is at or above TotalAITeamCap and at least half its teams are defensiveThe house’s oldest defensive team is deleted outright, the team count drops by one, and defensive triggers are passed over for this pass whatever the defensive count is. A house holding no defensive team at all deletes nothing and passes nothing over

Selection then proceeds only while the team count is below TotalAITeamCap.

Each trigger is put through these gates in order, and the first one that fails rejects it:

  1. It names a first TeamType.
  2. It is defensive, or the house is not restricted to defensive triggers.
  3. It is not defensive, or defensive triggers were not passed over above.
  4. It is a map-local trigger, or the scenario does not set IgnoreGlobalAITriggers=yes.
  5. It is enabled.
  6. Outside a campaign game, it is marked as available in skirmish.
  7. Its flag for the current difficulty is set.
  8. In a campaign game, its owner is not <none> and is either <all> or this house’s country.
  9. Its side field is unrestricted, or it demands GDI and the house acts like GDI, or it demands Nod and the house acts like Nod.
  10. Its tech level requirement is within the house’s tech level.
  11. Its condition holds.
  12. Every member of both TeamTypes’ TaskForces is something this house can build.
  13. Neither TeamType has reached its Max.

The tech level requirement is not taken from the record: it is the highest TechLevel among the members of the trigger’s TaskForces, so a trigger demands exactly what its teams demand — with one trap: a member whose TechLevel is -1 pushes the requirement to 11, silently making the trigger unspringable.

The three difficulty flags on a trigger mean Easy, Medium and Hard as the player chose them, and that reading holds for every house in a campaign game and for computer houses outside one.

In a campaign game the flag tested is simply the one matching the scenario’s difficulty. Outside a campaign it takes two steps. The test reads the house’s own difficulty slot, which for a computer house is already the inverse of the setting the player chose, and then asks for the flag at the opposite end again. The table traces one computer house through both steps. Its left and right columns agree, which is the point: the two inversions cancel, and the flags mean what they are named.

Setting the player choseSlot the computer house holdsFlag the trigger must set
Easy2, the [Difficult] sectionEasy
Normal1, the [Normal] sectionMedium
Hard0, the [Easy] sectionHard

A human house switched into the pass outside a campaign sits in slot 1 whatever the session was set to, so it always tests the Medium flag.

The per-difficulty lists in [General] get no second inversion. Each is indexed with the raw slot, so entry 0 is the hardest game setting and entry 2 the easiest: TeamDelays, TotalAITeamCap, MinimumAIDefensiveTeams, MaximumAIDefensiveTeams, FillEarliestTeamProbability and AIHateDelays.

A trigger counts as defensive when its first TeamType is IsBaseDefense=yes and its second is either absent or defensive as well.

A house is restricted to defensive triggers — that is, a trigger that is not defensive is rejected outright — while either of these holds:

A house’s enemy is the house it holds the most anger toward, and damage is what moves that record. Outside a campaign a computer house left without an enemy also picks one on a countdown; nothing does that in a campaign, so a campaign house has no enemy until combat gives it one, and until then only defensive triggers can pass at all.

The condition types themselves are listed with the trigger record. What each one measures at runtime is this:

  • The two owning conditions count how many live objects of the record’s condition object type the enemy, or the owning house, currently holds, and compare that count against the record’s number with the record’s comparator. A condition object that resolved to nothing counts as zero, so an unrecognized ID makes the comparison read 0.
  • The yellow power condition ignores the record’s number and comparator entirely and tests whether the enemy’s power output minus its drain is below a hard-coded 100. The red power condition tests the same difference against 0.
  • The money condition compares the enemy’s spendable credits against the record’s number.

Every condition except the owning-house one reads the enemy, so a house without an enemy can only ever pass a defensive trigger that either has no condition or asks about its own holdings.

Every trigger that survives the gates is added to a distribution carrying its current weight, and one entry is drawn from that distribution. The three weights in a trigger record are truncated to whole numbers as the record is parsed, and the current weight is truncated again as it is added, so a trigger whose weight has fallen below 1 is added to the distribution but can never be drawn from it.

Each trigger carries a current weight held between a minimum and a maximum of its own, and two counters: the runs it has had and the runs that succeeded. The counters start at zero and all three weights at 1 unless the record supplies them.

When a team dies, every trigger whose first TeamType is that team’s type records an outcome — a success when the team reached the Success team mission at some point in its script, a failure otherwise.

Both outcomes move the weight the same way: a history term plus a flat delta, added to the current weight. The table sets the two paths side by side. The successes and runs in the history column are the figures standing before this outcome, because the counters are stepped only afterward; the difference to read off the table is that the coefficient scales the failure path alone.

OutcomeHistory term addedDelta addedCounters afterward
SuccessThe successes so far minus half the runs so far, held at no less than zeroAITriggerSuccessWeightDeltaOne run, one success
FailureThat same successes-minus-half-runs figure taken before any floor, scaled by AITriggerTrackRecordCoefficient, then held at no more than zeroAITriggerFailureWeightDeltaOne run

Either result is clamped back into the trigger’s own minimum and maximum. The history term is zero at exactly a one-in-two success rate and grows with the number of runs, so a long record moves the weight further than a short one does.

The recording is keyed on the first TeamType and nothing else. It does not check which trigger raised the team, or which house owns it, so every trigger in the scenario that names that TeamType first is credited with the same outcome, and a team the scenario placed by hand moves those records too. A trigger that only ever appears as somebody’s second team never has its own record moved at all.

Before the suggestion is returned the house’s teams are scanned once more. If any team it owns is reforming, or is not under way, and that team’s TeamType appears anywhere in the suggestion, the whole suggestion is discarded — the other team with it.

Every TeamType that survives is marked as autocreated, permanently for the rest of the session. Autocreate=yes sets the same mark from the start, but nothing in the selection above reads it: it is a recruitment setting, and being suggested applies it whether the section asked for it or not.

One team is then created from each surviving TeamType, subject to a second count test that is not the same as the eligibility test above. In a campaign game it compares Max against the teams of that type alive anywhere in the scenario rather than against the teams this house owns, so a campaign house can clear the eligibility test and still create nothing.

A team fills its TaskForce by taking objects its house already owns. It looks for them on a logic pass while all of this holds:

  • Any of:
    • the team is not under way: it has not been started yet, or it has since lost enough members to be sent back to regroup;
    • All of: it is short of full strength, and its TeamType is Reinforce=yes.
  • Any of:
    • the team belongs to a computer house;
    • the team has neither reached full strength nor been set in motion at any point since it was created.

The second group is what closes recruitment on a human house’s teams, and it closes it for good. Reaching full strength once is enough, and so is starting once; losing members afterward does not reopen it, and neither does Reinforce=yes.

Each pass fills at most one place in each TaskForce slot that is still short — except that a recruited transport brings its passengers with it, which can fill several places at once.

Candidates are ranked by distance from the cell of the TeamType’s Waypoint, or from the team’s own center when it has no waypoint, and the nearest one that may join is taken. An object is considered at all under any of these:

  • the team’s group is -2;
  • the object carries the team’s group;
  • the TeamType is Recruiter=yes.

A candidate outside the group is pushed 50 cells further away in the ranking before the nearest is chosen. The team’s group is the TeamType’s Group, which falls back to the TaskForce’s Group while the TeamType leaves its own at -1, and every member’s group is overwritten with it as the member joins.

A considered candidate then has to clear all of these, in this order:

  1. It is not already on a team of this same TeamType.
  2. It is alive, it is not in radio contact, and its house is the team’s house.
  3. Its current mission is one that allows recruitment.
  4. Its recruitable state for this kind of team is still set, as below.
  5. If it is already on another team, that team’s Priority is strictly below this team’s.
  6. It is not an aircraft that carries a weapon and has no ammunition.
  7. Its type matches one of the TaskForce’s member lines, and that line still has room.

Which recruitable state step 4 tests depends on the autocreate mark: an unmarked TeamType reads the object’s ordinary team-recruitable state and ignores its autocreate-recruitable state, and a marked one does the reverse. Both start set on every object. Joining a team overwrites the object’s autocreate-recruitable state with the TeamType’s AreTeamMembersRecruitable, so a team declared AreTeamMembersRecruitable=no locks its members away from every autocreated team that comes after it.

A team left with no members dissolves at once when it has already reached full strength or been set in motion. Outside a campaign it also dissolves DissolveUnfilledTeamDelay frames after it was created whether or not anybody ever joined it, which is the ordinary end of a team whose TaskForce the house could not fill.

Structures are chosen by the base planner. Vehicles, infantry and aircraft are each chosen by their own routine, and the three share one shape — though the vehicle routine first considers replacing a lost harvester and can spend its turn on that. A routine that already has a type selected and unbuilt does nothing further.

  1. Demand. A team of this house contributes to the tally under either of these:

    • All of: its TeamType is Reinforce=yes, and it is short of full strength;
    • All of: it is not forced active — a mark only a reinforcement group carries — and it has neither reached full strength nor been set in motion.

    Each unfilled place in a contributing team’s TaskForce for a type of the routine’s own kind adds one to that type’s demand, and the creation frame of the earliest contributing team is kept per type.

  2. Supply. Every object of the house that is recruitable — out of limbo, on no team, on a mission that allows recruitment and not held back by its own recruitable state — takes one off its type’s demand.

  3. Affordable. A type is a candidate while its demand is still above zero, the house can build it, and its cost is within the house’s spendable credits.

  4. Choice. With probability FillEarliestTeamProbability percent the house builds the type wanted by the oldest waiting team; otherwise it draws uniformly from the candidate list.

The uniform draw is not restricted to the types in most demand. The candidate list is emptied only when a type whose demand exceeds every demand seen so far is reached, so it holds every candidate found since the last such type, including candidates with less demand. Which candidates those are depends on the order the types are registered in.

The tally is sized from the current InfantryType, UnitType or AircraftType list. A team or runtime object whose type position lies outside that list is ignored instead of indexing unrelated storage.

A TeamType’s Prebuild and LooseRecruit are read into flags no decision consults, and so is its own TechLevel — a trigger’s tech level requirement comes from its teams’ TaskForce members instead. AutocreateTime in [AI] is read into the rules and reaches only a disabled block.

31 of 31

KeyDescriptionValueWhen omitted
AIHateDelaysglobal rulesThe frames a computer house waits before picking its first enemy, one entry per difficulty.list of integers(empty)
AITriggerFailureWeightDeltaglobal rulesThe amount added to an AI trigger's weight each time one of its teams fails.floating point-1
AITriggerSuccessWeightDeltaglobal rulesThe amount added to an AI trigger's weight each time one of its teams succeeds.floating point1
AITriggerTrackRecordCoefficientglobal rulesThe multiplier applied to an AI trigger's history term when one of its teams fails.floating point1
AreTeamMembersRecruitableTeamTypeThe autocreate-recruitable state written onto each object as it joins a team of this type.booleanyes
AutocreateTeamTypeWhich of an object's two recruitable states a team of this type reads when it recruits.booleanno
AutocreateTimeNo effectglobal rulesParsed number that the engine never uses.floating point5
CompEasyBonusglobal rulesWhether a computer house drops one difficulty slot when the session holds more than one human player.booleanyes
DissolveUnfilledTeamDelayglobal rulesThe frames an empty team outside a campaign waits before dissolving itself.integer5000
FillEarliestTeamProbabilityglobal rulesThe percent chance that the next vehicle, infantryman or aircraft chosen goes to the oldest waiting team, one entry per difficulty.list of integers(empty)
GroupTeamType, TaskForceThe recruitment group a team draws its members from, taken from the TaskForce when the TeamType leaves its own unset.integer-1
HouseTeamTypeThe country whose house in the match owns teams built from this TeamType.HouseTypenone
IgnoreGlobalAITriggersscenariosWhether the scenario passes over every AI trigger that was not defined by the map itself.booleanno
IsBaseDefenseBuildingType, TeamTypeGives a BuildingType the computed ratings the computer picks base defenses by, or counts a TeamType against its house's defensive team total.booleanno
LooseRecruitNo effectTeamTypeParsed TeamType flag that the engine never uses.booleanno
MaxTeamTypeThe number of teams of this TeamType a house may hold at once.integer0
MaximumAIDefensiveTeamsglobal rulesThe number of defensive teams above which a house passes over its defensive AI triggers, one entry per difficulty.list of integers(empty)
MinimumAIDefensiveTeamsglobal rulesThe number of defensive teams a house must hold before any non-defensive AI trigger may spring, one entry per difficulty.list of integers(empty)
PrebuildNo effectTeamTypeParsed TeamType flag that the engine never uses.booleanno
PriorityTeamType, SoundsHow firmly a team holds its members against other teams, or how hard a sound competes for a playback channel.integerVaries by type
RatioAITriggerTeamHouse (per-scenario)The percent chance that a house consults its AI triggers when its team countdown expires.integer100
Recruitablemission behaviorWhether an object in this mission may be taken onto a team.booleanyes
RecruiterTeamTypeWhether a team of this type may recruit an object whose group does not match its own.booleanno
ReinforceTeamTypeWhether a team of this type keeps recruiting after it has begun to move.booleanno
ScriptTeamTypeThe Script whose team missions a team of this type carries out.classnone
TaskForceTeamTypeThe TaskForce whose composition a team of this type recruits and builds toward.classnone
TeamDelaysglobal rulesThe frames a house waits between team creation passes, one entry per difficulty.list of integers(empty)
TechLevelNo effect in some scopes7 applicable typesThe level an object type demands, the level a house is given, or the level a non-campaign session hands every house.integerVaries by type
TotalAITeamCapglobal rulesThe number of live teams a house may hold before its AI triggers stop springing, one entry per difficulty.list of integers(empty)
UseMinDefenseRuleglobal rulesWhether a house must hold its minimum number of defensive teams before any other AI trigger may spring.booleanyes
WaypointTeamTypeThe waypoint a team of this type starts at and recruits around.stringnone

History

  1. Changed0.1.0In development

    Size AI production demand from the type registries