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.
TeamTypes and AI triggers in brief
Section titled “TeamTypes and AI triggers in brief”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 written | What 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 itself | Which 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.
| Setting | Where 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.
[TaskForces]0=MyRaidForce
[MyRaidForce] ; example TaskForceName=Raid force0=3,E11=1,TTNK
[ScriptTypes]0=MyRaidScript
[MyRaidScript] ; example ScriptName=Raid0=0,1 ; attack anything1=49,0 ; Success; without it every raid team records a failure
[TeamTypes]0=MyRaidTeam
[MyRaidTeam] ; example TeamTypeName=Raid teamHouse=NodTaskForce=MyRaidForceScript=MyRaidScriptMax=4Priority=8
[AITriggerTypes]MyRaidTrigger=Raid a blacked-out base,MyRaidTeam,<all>,0,3,GAPOWR,0000000000000000,8,1,20,1,0,0,0,<none>,0,1,1The 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.
The pass in outline
Section titled “The pass in outline”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.
When the pass runs
Section titled “When the pass runs”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 by | Effect |
|---|---|
| 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 yard | On, for a non-human house outside a campaign game |
| A house passing from a human player to the computer | On, provided the house still holds a construction yard |
The team budget
Section titled “The team budget”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 counts | What this pass does |
|---|---|
Team count below TotalAITeamCap, or defensive teams below half the team count rounded down | Nothing 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 defensive | The 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.
Which triggers are eligible
Section titled “Which triggers are eligible”Each trigger is put through these gates in order, and the first one that fails rejects it:
- It names a first TeamType.
- It is defensive, or the house is not restricted to defensive triggers.
- It is not defensive, or defensive triggers were not passed over above.
- It is a map-local trigger, or the scenario does not set
IgnoreGlobalAITriggers=yes. - It is enabled.
- Outside a campaign game, it is marked as available in skirmish.
- Its flag for the current difficulty is set.
- In a campaign game, its owner is not
<none>and is either<all>or this house’s country. - 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.
- Its tech level requirement is within the house’s tech level.
- Its condition holds.
- Every member of both TeamTypes’ TaskForces is something this house can build.
- 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.
Difficulty
Section titled “Difficulty”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 chose | Slot the computer house holds | Flag the trigger must set |
|---|---|---|
| Easy | 2, the [Difficult] section | Easy |
| Normal | 1, the [Normal] section | Medium |
| Hard | 0, the [Easy] section | Hard |
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.
Defensive teams and the enemy
Section titled “Defensive teams and the enemy”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:
- the house has no enemy;
- All of:
UseMinDefenseRule=yes, and the house’s live defensive team count is belowMinimumAIDefensiveTeams.
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.
Conditions
Section titled “Conditions”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 against0. - 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.
The weighted draw
Section titled “The weighted draw”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.
The track record
Section titled “The track record”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.
| Outcome | History term added | Delta added | Counters afterward |
|---|---|---|---|
| Success | The successes so far minus half the runs so far, held at no less than zero | AITriggerSuccessWeightDelta | One run, one success |
| Failure | That same successes-minus-half-runs figure taken before any floor, scaled by AITriggerTrackRecordCoefficient, then held at no more than zero | AITriggerFailureWeightDelta | One 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.
From suggestion to team
Section titled “From suggestion to team”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.
Recruitment
Section titled “Recruitment”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:
- It is not already on a team of this same TeamType.
- It is alive, it is not in radio contact, and its house is the team’s house.
- Its current mission is one that allows recruitment.
- Its recruitable state for this kind of team is still set, as below.
- If it is already on another team, that team’s
Priorityis strictly below this team’s. - It is not an aircraft that carries a weapon and has no ammunition.
- 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.
Production demand
Section titled “Production demand”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.
-
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.
- All of: its TeamType is
-
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.
-
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.
-
Choice. With probability
FillEarliestTeamProbabilitypercent 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.
Parsed settings without effect
Section titled “Parsed settings without effect”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.
Related settings
31 of 31
| Key | Description | Value | When omitted |
|---|---|---|---|
AIHateDelaysglobal rules | The frames a computer house waits before picking its first enemy, one entry per difficulty. | list of integers | (empty) |
AITriggerFailureWeightDeltaglobal rules | The amount added to an AI trigger's weight each time one of its teams fails. | floating point | -1 |
AITriggerSuccessWeightDeltaglobal rules | The amount added to an AI trigger's weight each time one of its teams succeeds. | floating point | 1 |
AITriggerTrackRecordCoefficientglobal rules | The multiplier applied to an AI trigger's history term when one of its teams fails. | floating point | 1 |
AreTeamMembersRecruitableTeamType | The autocreate-recruitable state written onto each object as it joins a team of this type. | boolean | yes |
AutocreateTeamType | Which of an object's two recruitable states a team of this type reads when it recruits. | boolean | no |
AutocreateTimeNo effectglobal rules | Parsed number that the engine never uses. | floating point | 5 |
CompEasyBonusglobal rules | Whether a computer house drops one difficulty slot when the session holds more than one human player. | boolean | yes |
DissolveUnfilledTeamDelayglobal rules | The frames an empty team outside a campaign waits before dissolving itself. | integer | 5000 |
FillEarliestTeamProbabilityglobal rules | The 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, TaskForce | The recruitment group a team draws its members from, taken from the TaskForce when the TeamType leaves its own unset. | integer | -1 |
HouseTeamType | The country whose house in the match owns teams built from this TeamType. | HouseType | none |
IgnoreGlobalAITriggersscenarios | Whether the scenario passes over every AI trigger that was not defined by the map itself. | boolean | no |
IsBaseDefenseBuildingType, TeamType | Gives a BuildingType the computed ratings the computer picks base defenses by, or counts a TeamType against its house's defensive team total. | boolean | no |
LooseRecruitNo effectTeamType | Parsed TeamType flag that the engine never uses. | boolean | no |
MaxTeamType | The number of teams of this TeamType a house may hold at once. | integer | 0 |
MaximumAIDefensiveTeamsglobal rules | The number of defensive teams above which a house passes over its defensive AI triggers, one entry per difficulty. | list of integers | (empty) |
MinimumAIDefensiveTeamsglobal rules | The 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 effectTeamType | Parsed TeamType flag that the engine never uses. | boolean | no |
PriorityTeamType, Sounds | How firmly a team holds its members against other teams, or how hard a sound competes for a playback channel. | integer | Varies by type |
RatioAITriggerTeamHouse (per-scenario) | The percent chance that a house consults its AI triggers when its team countdown expires. | integer | 100 |
Recruitablemission behavior | Whether an object in this mission may be taken onto a team. | boolean | yes |
RecruiterTeamType | Whether a team of this type may recruit an object whose group does not match its own. | boolean | no |
ReinforceTeamType | Whether a team of this type keeps recruiting after it has begun to move. | boolean | no |
ScriptTeamType | The Script whose team missions a team of this type carries out. | class | none |
TaskForceTeamType | The TaskForce whose composition a team of this type recruits and builds toward. | class | none |
TeamDelaysglobal rules | The frames a house waits between team creation passes, one entry per difficulty. | list of integers | (empty) |
TechLevelNo effect in some scopes7 applicable types | The level an object type demands, the level a house is given, or the level a non-campaign session hands every house. | integer | Varies by type |
TotalAITeamCapglobal rules | The number of live teams a house may hold before its AI triggers stop springing, one entry per difficulty. | list of integers | (empty) |
UseMinDefenseRuleglobal rules | Whether a house must hold its minimum number of defensive teams before any other AI trigger may spring. | boolean | yes |
WaypointTeamType | The waypoint a team of this type starts at and recruits around. | string | none |
No keys match the current filters.
History
- Changed0.1.0In development