Extended Edition Modding: Proto, Techtree, and Victory Scripts
By WarriorMario · extended
Edit unit data and technologies, understand prerequisite and effect syntax, build an automatic upgrade, and inspect victory-condition scripts.
Start with the Right File
Extended Edition’s gameplay data separates a unit’s basic definition from the technologies that change it. Work on those two layers before moving to scripts:
| File |
Purpose |
protox.xml |
Unit definitions for the expansion-based game: hit points, movement, costs, actions, classes, and flags. |
techtreex.xml |
Technology definitions: requirements, availability, and effects on units or players. |
proto.xml and techtree.xml |
The corresponding original-game data files. Check which data set your game mode uses before editing. |
trigger2/basicvcnomain.xs |
The basic victory-condition script, including the rule that checks whether a player has lost. |
Keep an untouched copy of every file you edit and a separate working copy. Make one change at a time, test it, and save a working version before moving on. An XML editor helps you match tags and find definitions by their internal names.
This tutorial uses Extended Edition’s data format. Unit, technology, and file names below refer to that format.
Read the XML Structure
XML elements normally have an opening tag, a value or child elements, and a closing tag:
<maxhitpoints>20.0000</maxhitpoints>
Attributes sit inside the opening tag:
<cost resourcetype="Food">50.0000</cost>
A unit definition contains many such elements between <unit ...> and </unit>. A technology has the same overall structure between <tech ...> and </tech>.
The unit definitions belong inside the existing <proto ...> root; technologies belong inside <techtree ...>. Keep each file’s existing root and version attribute. A missing closing tag, an unclosed quote, or a definition pasted outside the root makes the XML invalid.
When adding a definition, give it an unused internal name and ID. A dbid must also be unique in its data file. A displayed name is not a substitute for an internal name: effects and prerequisites look up the internal identifier.
Editing the Proto
Identify the Unit
A unit starts with a line such as:
<unit id="0" name="Hawk">
The numeric ID and internal name identify this definition. If you copy a unit to create a different one, change those identifiers and its dbid rather than creating duplicate entries.
Other name-related fields serve different purposes:
| Field |
Purpose |
displaynameid |
A reference to the localized name shown to players. |
EditorNameID |
A localized name used in the scenario editor. |
icon |
The unit’s small interface icon. |
portraiticon |
Its larger selected-unit portrait. |
cultureicon |
An icon chosen according to culture. |
Some icon fields use a civ attribute for culture names such as Greek, Egyptian, Norse, and Atlantean. Here “civ” refers to the culture, not a major god such as Zeus or Thor. Keep existing art and text references while testing a gameplay change so that an unrelated missing asset does not obscure the result.
Health, Sight, and Movement
<initialhitpoints>15.0000</initialhitpoints>
<maxhitpoints>20.0000</maxhitpoints>
<los>12.0000</los>
<maxvelocity>4.0000</maxvelocity>
This fragment gives a unit 15 hit points when created and a maximum of 20. Set both health fields when you want a different fully healthy starting unit.
los is line of sight and maxvelocity is movement speed. Other movement fields include movementtype, turnrate, and TurnRadius. Movement types include Land, Water, and Air; changing a number alone does not replace the rest of a unit’s movement and action setup.
For a simple first experiment, change the two health values of an existing unit, create a fresh instance in the editor, and check its health. This verifies that you are editing the definition the game actually uses.
Cost and Training
A unit can have separate cost entries for each resource:
<cost resourcetype="Food">50.0000</cost>
<cost resourcetype="Gold">40.0000</cost>
<trainpoints>20.0000</trainpoints>
The resource names are Food, Wood, Gold, and Favor. Training time depends on the unit’s trainpoints and the producing building’s buildingworkrate:
Training time = trainpoints ÷ buildingworkrate.
Check both definitions when a unit trains at an unexpected rate. The same work-rate idea applies to a building processing research points.
Garrisoning, Replacement, and Lifetime
| Field |
Purpose |
MaxContained |
Maximum units that can garrison inside the unit. |
ContainedSpeedBonus |
The speed contribution associated with a garrisoned unit. |
ContainedAttack |
The attack contribution associated with a garrisoned unit. |
BirthReplacement |
Another unit spawned when this one is created. |
DeadReplacement |
A replacement spawned when this unit dies. |
lifespan |
The unit’s limited lifetime. |
ResourceDecay |
Decay of resources carried by a resource object. |
PartisanCount and PartisanType |
The number and type of partisans associated with the definition. |
Replacement fields refer to internal unit names. Do not point a birth replacement back to itself, or make a chain that returns to its starting unit: repeated creation can become an infinite loop.
Treat transformations and limited lifetimes carefully. A transformed unit can retain a lifetime from its earlier form; replacing its appearance does not necessarily remove the timer.
Placement and Other Unit References
AllowedCulture restricts a definition to the named culture. SocketProtoUnit identifies a required placement socket, while ProjectileProtoUnit names the projectile used by a ranged action.
The fields obstructionradiusx and obstructionradiusz describe the space occupied by the unit in the horizontal plane. Check the placement and pathing result in the editor after changing them.
minimapcolor uses red, green, and blue components in the range 0 to 1. For example, a full red component is 1.0, not 255.
Classes and Flags
A class groups units so that the engine or a technology can address them together:
<unittype>MythUnit</unittype>
Definitions can belong to several classes. Examples include HumanSoldier, MythUnit, AbstractPharaoh, and LogicalTypeNeededForVictory. The last one matters to the victory-condition script discussed below. Do not remove class membership merely because it does not change the visible unit statistics.
Flags switch on specific behavior:
<flag>DoNotAutoAttack</flag>
Useful flags to recognize include:
| Flag |
Behavior |
GatherDirectToPlayer |
Gathered resources go directly to the player. |
DoNotAutoAttack |
Prevents automatic attacks. |
Invulnerable |
Makes the unit invulnerable. |
NotSelectable |
Prevents normal selection. |
DoNotShowOnMinimap |
Hides the unit’s minimap marker. |
GodPowerExclusion |
Excludes the unit from god-power targeting. |
SharedGarrison |
Enables shared garrison behavior. |
ForceBuildingData |
Allows building-related data, such as training and research, on a nonbuilding definition. |
Use flags for a specific purpose and test the resulting behavior. Copying an entire flag list from an unrelated unit can carry over restrictions that are hard to see in the interface.
Editing the Techtree
The proto supplies a unit’s starting definition. The techtree changes that definition during play: an upgrade can add health, modify damage, reduce a cost, or make a unit available.
Technology Structure and Status
A technology has an internal name, a type, identifiers, a status, and optional requirements and effects:
<tech name="Example Upgrade" type="Normal">
<dbid>900001</dbid>
<status>OBTAINABLE</status>
<prereqs>
<specificage>Classical Age</specificage>
</prereqs>
<effects>
<effect type="Data" amount="10.00"
subtype="Hitpoints" relativity="Absolute">
<target type="ProtoUnit">Villager Norse</target>
</effect>
</effects>
</tech>
Choose an unused name and dbid; 900001 is an example, not a reserved ID. This fragment defines an automatic upgrade once its requirement is met. It is not a research button.
The status controls how a technology becomes active:
| Status |
Use |
OBTAINABLE |
An automatic technology that can activate when its prerequisites are satisfied. |
AVAILABLE |
A technology available for research. |
UNOBTAINABLE |
A technology that cannot currently be obtained. |
ACTIVE |
An active technology. |
PERSISTANT |
A repeating technology; keep this spelling used by the data format. |
A manually researched technology also needs the appropriate research arrangement in the game data. Giving it a cost alone does not place a button on a building.
Common optional fields include displaynameid, icon, rollovertextid, prereqsnotmetrollovertextid, researchpoints, resource costs, and delay. Keep presentation fields separate from the technology’s internal name and effects.
Prerequisites
Requirements go inside <prereqs>. An age requirement uses the age name:
<specificage>Classical Age</specificage>
A technology can require a previous upgrade:
<techstatus status="Active">Medium Archers</techstatus>
Culture and civilization groups offer alternatives within the group:
<culture>
<culturename>Greek</culturename>
<culturename>Atlantean</culturename>
</culture>
This permits Greek or Atlantean players; it does not require a player to be both cultures.
For major gods, use a civilization group:
<civilization>
<civname>Loki</civname>
<civname>Thor</civname>
</civilization>
Internal names must match the data. For example, Oranos is represented by Ouranos in this format.
A unit-count condition has an internal unit name, count, state selection, and comparison operator:
<typecount unit="Villager Norse" count="5"
state="noneState aliveState" operator="gte"></typecount>
The operators include gt (greater than), lt (less than), e (equal), gte (greater than or equal), and lte (less than or equal). The example uses the state selection shown in the game-data pattern for this kind of check.
Understand Data Effects
Data effects identify a target, a property, an amount, and a way of applying that amount:
<effect type="Data" amount="10.00"
subtype="Hitpoints" relativity="Absolute">
<target type="ProtoUnit">Villager Norse</target>
</effect>
Here the target is an internal proto unit name, the property is hit points, and the effect adds 10.
The relativity attribute makes a substantial difference:
| Relativity |
Meaning |
Absolute |
Add the specified amount. |
Assign |
Replace the value with the specified amount. |
Percent |
Multiply the current value by the specified factor. |
BasePercent |
Add a fraction of the base value; 1.05 represents a 5% increase. |
For a starting value of 10, two separate Percent upgrades of 1.05 produce 11.025. Two BasePercent upgrades of 1.05 produce 11.0. Decide whether upgrades should compound before choosing a mode.
An effect that addresses attack damage also needs an action and damage type:
<effect type="Data" action="HandAttack" amount="1.05"
subtype="Damage" damagetype="Hack"
relativity="BasePercent">
<target type="ProtoUnit">Swordsman Hero</target>
</effect>
Swordsman Hero is the internal name used for the Murmillo in this example. The effect changes the hack damage of its existing hand attack.
An upgrade does not create an attack action or damage component that is absent from the proto. For example, increasing pierce damage will not give pierce damage to an attack that has no such component. Inspect the existing action first. A damage parameter in a proto action has the form:
<param name="Damage" type="Pierce" value1="0"></param>
This is an action parameter, not a complete action definition; it belongs in the appropriate existing action structure.
Costs and Availability
A cost effect specifies the resource being changed:
<effect type="Data" amount="0.90" subtype="Cost"
resource="Food" relativity="Percent">
<target type="ProtoUnit">Villager Norse</target>
</effect>
This reduces the current food cost by 10%. Cost effects can also target technologies rather than proto units:
<effect type="Data" amount="0.90" subtype="Cost"
resource="Wood" relativity="Percent">
<target type="Tech">All</target>
</effect>
The second fragment changes wood costs of technologies. An effect without a relevant resource cost to change will not create that cost.
To enable a unit, use an enable effect:
<effect type="Data" amount="1.00"
subtype="Enable" relativity="Absolute">
<target type="ProtoUnit">Granary</target>
</effect>
The corresponding disabling pattern uses an amount of 0.00 with Assign. Availability is separate from the unit’s training or building command, so check that the player has the relevant command as well.
Other Effects
A technology can alter another technology’s status:
<effect type="TechStatus" status="Available">Example Upgrade</effect>
This is useful for a chain of upgrades. Do not make a technology repeatedly set its own status: a self-referential chain can freeze or crash the game.
Text and sound effects use their own effect types:
<effect type="TextOutput">12345</effect>
<effect type="Sound">sound-file-name</effect>
These are syntax examples. Use an existing localized string ID and sound reference from your data, or supply the corresponding resources for your mod.
A transformation can be issued as a console command:
<effect type="ConsoleCommand">unitTransform("Villager Norse", "Villager Greek")</effect>
This transforms matching Norse villagers into Greek villagers. It is a broad change rather than a command for one selected unit; test it with a small controlled group before using it in a larger design.
Worked Upgrade: Tougher, Cheaper Norse Villagers
This automatic technology gives Loki and Thor’s Norse villagers 10 additional hit points and reduces their food cost by 10% once the player has at least five matching villagers.
Add the definition inside the existing techtree root. First confirm that its name and dbid are unused:
<tech name="Norse Villager Resilience" type="Normal">
<dbid>900002</dbid>
<status>OBTAINABLE</status>
<prereqs>
<civilization>
<civname>Loki</civname>
<civname>Thor</civname>
</civilization>
<typecount unit="Villager Norse" count="5"
state="noneState aliveState" operator="gte"></typecount>
</prereqs>
<effects>
<effect type="Data" amount="10.00"
subtype="Hitpoints" relativity="Absolute">
<target type="ProtoUnit">Villager Norse</target>
</effect>
<effect type="Data" amount="0.90" subtype="Cost"
resource="Food" relativity="Percent">
<target type="ProtoUnit">Villager Norse</target>
</effect>
</effects>
</tech>
Test each part separately:
- Start as Loki or Thor with fewer than five Norse villagers. Record their maximum health and food cost.
- Reach five matching villagers, then inspect the health and cost again.
- Repeat as Odin to check the civilization restriction.
- Test a fresh game after restoring the unmodified file to compare the baseline.
Dwarves have their own definition; this effect targets Villager Norse. If a prerequisite never fires, check the internal names, the unit count, and the technology status before changing its effect.
Inspecting Victory-Condition Scripts
The basic victory conditions live in trigger2/basicvcnomain.xs. These scripts use rules, statements, and braces rather than XML tags. A rule has a name, scheduling settings, and a body:
rule BasicVC1
minInterval 4
maxInterval 5
active
{
// Existing rule body.
}
This shows the rule structure, not a replacement for the complete victory script. Keep the existing rule body and surrounding file while examining it.
The basic defeat check changes the script’s player context, counts living units in the LogicalTypeNeededForVictory class, and defeats a player when none remain. The script restores its previous player context after the loop. Those context operations matter: a count taken in the wrong context can inspect the wrong player.
Add a Diagnostic Message
Find the existing count in BasicVC1 and add one chat line immediately after it:
int count = 0;
count = count + kbUnitCount(
i,
cUnitTypeLogicalTypeNeededForVictory,
cUnitStateAlive
);
trChatSend(0, "Number of units required for victory alive: " + count);
Insert the message into the existing rule; do not append a second rule with the same name. Keep the player loop, context changes, and defeat check intact.
Run a small test game and watch how the count changes as units in that class are created or removed. The message runs whenever that part of the rule executes, for each player examined, so remove it after testing to avoid repeated chat output.
Statements end in semicolons. Conditions use parentheses and blocks use braces. Comparison operators such as ==, !=, <, and >= compare values; = assigns a value. A missing brace or a mistaken assignment can change the rule’s behavior even when the code looks nearly correct.
Check Your Changes
When an edit does not work, narrow the problem before adding more code:
- File selection: Confirm that the game mode is using the data file you edited.
- XML structure: Match opening and closing tags, keep attributes quoted, and keep definitions inside the existing root.
- Identifiers: Check unique IDs and exact internal unit, class, and technology names.
- Activation: Confirm the technology’s status and each prerequisite separately.
- Effects: Check the target, action, damage or resource type, amount, and relativity.
- Testing: Create fresh units or start a fresh game when checking a changed base definition. Compare one change at a time against a known working copy.
- Scripts: Keep context changes and existing victory checks intact, use a temporary diagnostic message, and remove it when the result is clear.
A small working change is the best foundation for the next one: establish a correct unit definition, apply a targeted technology, and use script diagnostics only where you need to inspect behavior.