Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Evil Islands eXtender

Engine fixes and tweaks for Evil Islands: Curse of the Lost Soul.

eix implements most of the features provided by EI addon.dll, as well as SpellAddon, and adds fixes and features. Scripts can be written in the game’s own language (script reference) or in Lua (Lua reference).

  • Mods and plugins work the same way: same config.reg, ai.reg and spelladdon.ini.
  • Game: game.exe RU 1.06 (EI starter’s and GOG’s Russian builds). Other builds not supported yet.

Install

  1. Install EIStarter-UM from Universal Mod. Just the starter, not the mod.
  2. Download eix’s zip from releases.
  3. Replace the starter’s dinput.dll (beside game.exe) and addon.dll (in the starter’s folder) with eix’s. Keep the starter’s two files to switch back.
  4. Put spelladdon.ini in the game folder. A mod’s own spelladdon.ini, in its folder, takes precedence.
  5. Put eix.ini in the mod’s folder (Mods\<mod>): eix’s own settings, such as pausing on a party member’s low health.

To uninstall, put the starter’s dinput.dll and addon.dll back.

Reference

PageContent
RecipesCommon changes: which file, which keys, which values
Script referenceEvery function and command of the game’s script language, the original’s and eix’s
Lua, Lua referenceThe Lua host, and the functions and callbacks of a mod’s script
Addon and loaderSettings of config.reg and ai.reg, console commands, start-up behavior
SpellAddon optionsEvery spelladdon.ini option
Fixes and featuresFixes of game.exe defects that eix adds; its own features: game options, AutoPause, encumbrance, FLAC music, INI variables
eix.inieix’s own settings: engine fixes, AutoPause, encumbrance, FLAC music
DevelopmentBuilding, testing, releasing and publishing this site

Development

Building, testing and releasing: development.

ei-rebuild | ei-universal-mod | CursedLands

Source and releases: github.com/BGforgeNet/eix.

Recipes

Common changes: which file to edit, which keys to set, and to what. Each key is described in full on its reference page.

The files

FileWhereReference
spelladdon.iniThe mod’s folder (Mods\<mod>, beside its config.reg); without one there, the game folder’s. eix reads one of the two, without merging them.SpellAddon options
eix.iniThe mod’s foldereix.ini
  • Edit them in any text editor, then restart the game.
  • Write fractions with a decimal point: 0.25.
  • Updating or reinstalling a mod can replace its files: keep a note of the changes.

Running drain (stamina)

Running drains stamina at every game tick. The game takes the maximum stamina / 150 per tick, so a full bar always lasts the same time, however large it is. [rebalanceengine] in spelladdon.ini changes that:

[rebalanceengine]
rewriterunabilitytime=1
newrunsystem=1
runconst=0.5
Drain per ticknewrunsystemrunconst
A fixed amount: a larger bar runs longer1The amount
A share of the maximum, as in the game0The share; the game’s is 0.006666666666 (1/150)
A share of the maximum plus a fixed amount, runconstex2The share

rewriterunabilitytime=1 makes the game use runconst; keep it in every row.

With a fixed amount:

  • Lower runconst to run longer: 0.25 runs twice as long as 0.5.
  • runconst = M / 150 drains as the game does for a hero with a maximum of M stamina: 0.5 matches the game at 75. A smaller bar empties sooner than in the game, a larger one later.

Related:

  • eix’s encumbrance ([Encumbrance] Enabled=1 in eix.ini) raises the drain with the load: 1.25 times at the maximum load with the defaults. RunDrain=0 there removes that extra, 50 halves it.
  • [gameplay] automaticruncontrol=1 in spelladdon.ini stops a unit from running when its stamina runs out.
  • Any other formula: the Lua callback CalcRunningManaCost.
  • Every key: SpellAddon options, eix.ini.

Mod settings for map scripts

A mod can give players a setting in its own INI file and read it in its map scripts, through eix’s INI variables. Without eix the setting reads 0, so 0 should mean the game’s own behavior.

  1. Ship the INI file in the mod’s folder, with the default value:

    ; mymod.ini
    [Companions]
    Travel=0
    
  2. Read it in a map script with the game’s GSGetVar, naming ini.<file>.<section>.<key>:

    if
    (
      IsEqual( GSGetVar( 0, "ini.mymod.Companions.Travel" ) , 1 )
    )
    then
    (
      ...
    )
    
  3. Players change the value in mymod.ini and restart the game.

Notes:

  • A mymod.ini in the game folder serves when the mod’s folder has none.
  • A value is a number; 0.5 and 0,5 both read as a half. A missing file, section or key reads 0.
  • The value is not stored in the game: changing the INI and restarting changes it in every save.
  • The conditions of an if must all hold. For “either of two” conditions with values 0 or 1, write Not( IsGreater( Not( a ) , b ) ), or give each condition its own if with the same commands.

Script reference

The functions and commands of the game’s script language. Each signature is written as a TypeScript declaration. A function returns a value; a command returns void. The types are number, string, object and group; name? is an argument a call may leave out, and ...name may be given again and again. Names ignore ASCII case. A condition is a number: 0 is false.

Entries tagged eix are added or changed by eix: the functions and commands that addon.dll adds, and the changes that SpellAddon options make to the game’s own. The others are the original game’s. A note headed Differs from the original says where eix behaves differently.

Sections:

Script flow

Waiting, stopping and looping over groups.

Any

function Any(variable: object, group: group, condition: number): number

Returns 1 if the condition is nonzero for at least one object of the group, with the variable set to each object in turn; otherwise 0.

Every

function Every(variable: object, group: group, condition: number): number

Returns 1 if the condition is nonzero for every object of the group, with the variable set to each object in turn; otherwise 0.

For

function For(variable: object, group: group): void

Runs the loop body once for every object of the group, with the global variable set to that object. The group can be a diplomatic group, a game group, or the result of GroupAdd or GroupCross.

ForIf

function ForIf(variable: object, group: group, condition: number): void

Like For, but runs the body only for the objects for which the condition is nonzero.

KillScript

function KillScript(): void

Stops the running script. Map script modules only; the first command of a script body, as a rule.

Sleep

function Sleep(ticks: number): void

Pauses the script for the given time, in 1/15 s units. Works in map script modules only.

SleepUntil

function SleepUntil(condition: number): void

Pauses the script until the condition becomes nonzero.

SleepUntilIdle

function SleepUntilIdle(unit: object): void

Pauses the script until the unit has no order left.

Sum

function Sum(variable: object, group: group, expression: number): number

Evaluates the expression once for every object of the group, with the variable set to that object, and returns the sum of the results.

WaitSegment

function WaitSegment(segments: number): void

Pauses the called procedure for the given number of segments, 15 to the second. Map script modules only.

Numbers and logic

Arithmetic, comparisons and rounding. A condition is a number: 0 is false, any other value true.

Abs

function Abs(value: number): number

eix Added by addon.dll.

Returns the absolute value.

Add

function Add(a: number, ...b: number[]): number

eix Added by addon.dll.

Returns the sum of two or more numbers. Replaces the game’s function of two numbers.

And

function And(a: number, ...b: number[]): number

eix Added by addon.dll.

Returns the logical conjunction of two or more values.

Ceil

function Ceil(value: number): number

eix Added by addon.dll.

Rounds up.

Cos

function Cos(value: number): number

eix Added by addon.dll.

Returns the cosine of a value in radians.

DistancePointPoint

function DistancePointPoint(x: number, y: number): number

eix Added by addon.dll.

Returns the distance from the point (x, y) to the origin: the two implicit coordinates are zero.

Differs from the original: the last two values are 0 (the original reads past its arguments)

Div

function Div(a: number, b: number): number

Returns a divided by b.

Floor

function Floor(value: number): number

eix Added by addon.dll.

Rounds down.

IsEqual

function IsEqual(a: number, b: number): number

Returns 1 if the two numbers are equal, otherwise 0.

IsEqualString

function IsEqualString(a: string, b: string): number

Returns 1 if the two strings are equal, case included, otherwise 0.

IsGreater

function IsGreater(a: number, b: number): number

Returns 1 if a is greater than b, otherwise 0.

IsLess

function IsLess(a: number, b: number): number

Returns 1 if a is less than b, otherwise 0.

Max

function Max(a: number, ...b: number[]): number

eix Added by addon.dll.

Returns the largest of two or more numbers.

Min

function Min(a: number, ...b: number[]): number

eix Added by addon.dll.

Returns the smallest of two or more numbers.

Mul

function Mul(a: number, ...b: number[]): number

eix Added by addon.dll.

Returns the product of two or more numbers. Replaces the game’s function of two numbers.

Not

function Not(value: number): number

Returns 1 if the value is 0, otherwise 0.

Or

function Or(a: number, ...b: number[]): number

eix Added by addon.dll.

Returns the logical disjunction of two or more values.

Pow

function Pow(base: number, exponent: number): number

eix Added by addon.dll.

Returns the base raised to the exponent.

Random

function Random(a: number, b?: number): number

eix Added by addon.dll.

Random(max) is the game’s random function. Random(min, max) returns a random value in the range, or min if max is not above min. Replaces the game’s function of one number.

Round

function Round(value: number): number

eix Added by addon.dll.

Rounds half away from zero.

Differs from the original: Round is exact (the original gives 8388610 for 8388609 and 1 for 0.49999997)

Sin

function Sin(value: number): number

eix Added by addon.dll.

Returns the sine of a value in radians.

Sub

function Sub(a: number, b: number): number

Returns a minus b.

Text and console

Strings, and output to the console and the chat.

ConsoleFloat

function ConsoleFloat(value: number): void

Prints a number to the console. Synonym: #.

ConsoleString

function ConsoleString(...text: string[]): void

eix Added by addon.dll.

Joins the strings and prints them as one console line. Replaces the game’s command of one string.

FloatToStr

function FloatToStr(value: number, precision?: number): string

eix Added by addon.dll.

Converts a number to text. precision defaults to 3 and is clamped to 0 to 8; the fraction’s trailing zeros are removed.

Differs from the original: FloatToStr(100, 0) is 100 (the original removes the integer part’s zeros too, giving 1)

RandStr

function RandStr(text: string, ...more: string[]): string

eix Added by addon.dll.

Returns one of two or more strings at random.

Differs from the original: RandStr stops on a missing object (the original crashes)

Say

function Say(...text: string[]): void

eix Added by addon.dll.

Joins the strings and sends them to the multiplayer server chat.

SendEvent

function SendEvent(player: number, event: number): void

Debug command: broadcasts a number to the engine’s players.

SendStringEvent

function SendStringEvent(player: number, event: string): void

Sends a text command to the engine. say sNN Name starts an NPC line (the text say sNN of texts.res, with the sound from the unit’s Acks field); say_block does the same and blocks the speaker. Other commands: tutorial zt1, briefing, autosave, endofgame.

StrCat

function StrCat(text: string, ...more: string[]): string

eix Added by addon.dll.

Joins two or more strings.

StrHas

function StrHas(text: string, fragment: string): number

eix Added by addon.dll.

Returns 1 if the fragment occurs in the text, otherwise 0.

StrLen

function StrLen(text: string): number

eix Added by addon.dll.

Returns the length of a string in bytes.

StrLower

function StrLower(text: string): string

eix Added by addon.dll.

Converts a string to lowercase by the active code page.

StrMid

function StrMid(text: string, start: number, count: number): string

eix Added by addon.dll.

Returns part of a string, by the active code page. start counts bytes from 1, and from the end when negative; a count of 0 takes the rest of the string.

StrToFloat

function StrToFloat(text: string): number

eix Added by addon.dll.

Converts text to a number.

StrUpper

function StrUpper(text: string): string

eix Added by addon.dll.

Converts a string to uppercase by the active code page.

Groups

Sets of objects: diplomatic groups, game groups and the results of group functions.

AddObject

function AddObject(group: group, unit: object): void

Adds a unit to a game group, a global variable of the script.

GetObjectsOnMap

function GetObjectsOnMap(...class: number[]): group

eix Added by addon.dll.

Returns the group of the objects of the listed classes. -1 selects all.

GetPlayerUnits

function GetPlayerUnits(diplomacyGroup: number): group

Returns the group of the units of a diplomatic group.

GroupAdd

function GroupAdd(a: group, b: group): group

Returns the group of the objects that are in either group.

GroupCross

function GroupCross(a: group, b: group): group

Returns the group of the objects that are in both groups.

GroupHas

function GroupHas(group: group, unit: object): number

Returns 1 if the group contains the unit, otherwise 0.

GroupSee

function GroupSee(group: group): group

Returns the group of the objects that the members of the group see at this moment. The members see each other.

GroupSee - SpellAddon groupseetrans

function GroupSee(group: group, mode: number): group

eix SpellAddon option [gameplay] groupseetrans.

Accepts an extra numeric mode argument. The group traversal itself stays the game’s.

GroupSize

function GroupSize(group: group): number

Returns the number of units in the group.

GroupSub

function GroupSub(a: group, b: group): group

Returns the group of the objects of the first group that are not in the second.

PlayerSee

function PlayerSee(player: number): group

Returns the group of the units that the units of the player’s party see.

RandObj

function RandObj(group: group): object

eix Added by addon.dll.

Returns a random member of the group.

RemoveObject

function RemoveObject(group: group, unit: object): void

Removes a unit from a game group. Not usable from the console.

UnitSee

function UnitSee(unit: object): group

Returns the group of the units the unit sees. A unit does not see itself.

UnitSee - SpellAddon unitseetrans

function UnitSee(unit: object, mode: number): group

eix SpellAddon option [gameplay] unitseetrans.

Selects the visibility list: positive zero is the visible living units, a negative sign bit (negative zero included) the second list, any other value the nearby-units list.

UnitsNearPoint

function UnitsNearPoint(x: number, y: number, radius: number, filter: number): group

eix Added by addon.dll.

Returns the group of the objects within the radius that pass the filter. Filter bits: 1 units, 2 other objects, 4 living named objects.

Objects and positions

Finding objects, reading their positions and placing them on the map.

AddMob

function AddMob(file: string): void

Loads a .mob file from the MAPS folder; the name includes the extension.

AddMob - SpellAddon infiniteaddmobspawn

function AddMob(file: string): void

eix SpellAddon option [gameplay] infiniteaddmobspawn.

Can load the same file repeatedly, except case-sensitive names that begin with Auto, which still load once. Active for values 1 and up.

AddMobEx

function AddMobEx(file: string, skipScripts?: number): void

eix Added by addon.dll.

Adds the objects of a .mob file without a loading movie, and optionally skips its scripts.

AddUnitToServer

function AddUnitToServer(mapName: string, prototype: string, displayName: string, x: number, y: number, z: number): void

Places a unit of the given prototype on the map at (x, y, z). mapName is its internal name, case-sensitive; displayName is the name shown in info windows.

DistanceUnitPoint

function DistanceUnitPoint(unit: object, x: number, y: number): number

Returns the straight distance between an object and a point.

DistanceUnitUnit

function DistanceUnitUnit(unitA: object, unitB: object): number

Returns the straight distance between two objects.

GetBSZValue

function GetBSZValue(x: number, y: number): number

Returns the height of the surface at the point: like GetZValue, except that an unfrozen pool of water or lava gives the height of its surface, not of its bottom.

GetFutureX

function GetFutureX(object: object, ticks: number): number

Returns the x coordinate the object is predicted to have after the given time, in 1/15 s units. The shorter the time, the better the prediction.

GetFutureY

function GetFutureY(object: object, ticks: number): number

Returns the y coordinate the object is predicted to have after the given time, in 1/15 s units.

GetLeader

function GetLeader(): object

Returns the leader of the current party: Zak in single player, the player’s current character in multiplayer.

GetObject

function GetObject(id: number): object

Returns the object with the given ID. Does not work with ten-digit IDs: use GetObjectByID.

GetObjectByID

function GetObjectByID(id: string): object

Returns the object with the given ID, written as a string. Accepts ten-digit IDs.

GetObjectByName

function GetObjectByName(name: string): object

Returns the object with the given in-game name.

GetObjectID

function GetObjectID(object: object): number

Returns the ID of an object, a unit or any other object such as a chest.

GetObjectStringID

function GetObjectStringID(object: object): string

eix Added by addon.dll.

Returns the ID of an object as a decimal string.

GetObjectTypeID

function GetObjectTypeID(object: object): number

eix Added by addon.dll.

Returns the class ID of an object, or -1.

GetPrototype

function GetPrototype(unit: object): string

eix Added by addon.dll.

Returns the name of the unit’s prototype.

Differs from the original: stops on a missing object (the original crashes)

GetRace

function GetRace(unit: object): string

eix Added by addon.dll.

Returns the model name of the unit.

Differs from the original: stops on a missing object (the original crashes)

GetX

function GetX(object: object): number

Returns the x coordinate of an object.

GetY

function GetY(object: object): number

Returns the y coordinate of an object.

GetZ

function GetZ(object: object): number

Returns the z coordinate of an object.

GetZValue

function GetZValue(x: number, y: number): number

Returns the height of the solid ground at the point, whatever water or lava lies over it.

HideObject

function HideObject(object: object, hide: number): void

Hides the object if hide is nonzero, shows it if 0. Works in briefing zones only.

IsInArea

function IsInArea(area: number, x: number, y: number): number

Returns 1 if the point (x, y) lies in a part of the quest area, otherwise 0.

IsInSquare

function IsInSquare(x: number, y: number, x1: number, y1: number, x2: number, y2: number): number

Returns 1 if the point (x, y) lies in the rectangle (x1, y1)-(x2, y2), otherwise 0.

IsUnitInWater

function IsUnitInWater(unit: object): number

Returns 1 if the unit is in water, otherwise 0.

IsUnitVisible

function IsUnitVisible(unit: object): number

Returns 1 if the player can see the unit in the zone, otherwise 0. HideObject does not change the result.

NearestUnit

function NearestUnit(x: number, y: number, radius: number, filter: number): object

eix Added by addon.dll.

Returns the nearest object within the radius that passes the filter, or no object. Filter bits: 1 units, 2 other objects, 4 living named objects.

RemoveObjectFromServer

function RemoveObjectFromServer(object: object): void

Removes the object from the server. Same as RemoveUnitFromServer.

RemoveUnitFromServer

function RemoveUnitFromServer(unit: object): void

Removes the object from the server. Same as RemoveObjectFromServer.

SetCP

function SetCP(object: object, x: number, y: number, z: number): void

Moves any map object, not only a unit, to (x, y). A unit loses its orders, and the passability graph is recalculated, which is slower than SetCPFast.

SetCPFast

function SetCPFast(object: object, x: number, y: number, z: number): void

Like SetCP, but the AI passability graph is not recalculated.

UnitInSquare

function UnitInSquare(unit: object, x1: number, y1: number, x2: number, y2: number): number

Returns 1 if the unit stands in the rectangle (x1, y1)-(x2, y2), otherwise 0.

Units and statistics

Health, mana, attributes, skills and experience.

BlockUnit

function BlockUnit(unit: object, block: number): void

Blocks the player’s control of a unit if block is nonzero, gives it back if 0. A unit can be controlled from the moment it joins a party.

GetActions

function GetActions(unit: object): number

eix Added by addon.dll.

Returns the current action count of a unit.

GetActionsBase

function GetActionsBase(unit: object): number

eix Added by addon.dll.

Returns the base action count of a unit.

GetAIClass

function GetAIClass(unit: object): number

Returns what the unit is doing: 0 calm, 1 moving, 2 turning, 3 attacking, 4 casting, 5 using a skill, 6 following.

GetAIClass - SpellAddon getaiclasstrans

function GetAIClass(unit: object): number

eix SpellAddon option [gameplay] getaiclasstrans.

Reads the AI class field of the unit’s server controller.

GetArmor

function GetArmor(unit: object, kind: number, part: number): number

eix Added by addon.dll.

Returns the protection of a unit. kind 0 to 5 selects a body slot’s layers and part 0 to 6 a damage type; a negative kind uses the computed protection.

GetDexterity

function GetDexterity(unit: object): number

eix Added by addon.dll.

Returns the dexterity of a unit.

GetEncumbrance

function GetEncumbrance(unit: object): number

eix Added by addon.dll.

Returns the load a unit carries.

GetIntelligence

function GetIntelligence(unit: object): number

eix Added by addon.dll.

Returns the intelligence of a unit.

GetMaxEncumbrance

function GetMaxEncumbrance(unit: object): number

eix Added by addon.dll.

Returns the load a unit can carry.

GetPerk

function GetPerk(unit: object, name: string): number

eix Added by addon.dll.

Returns the level of a perk of a unit. Names: sword, axe, dagger, spear, hammer, bow, crossbow, fire, lightning, acid, illusion, divination, enchantments, healing, domination, infra, health, mana, vitality, spirit, quickness, lift, backstab, strength, dexterity, intelligence.

GetSkill

function GetSkill(unit: object, name: string): number

eix Added by addon.dll.

Returns a skill or attribute of a unit: science, melee, archery, elemental, sense or astral.

GetStrength

function GetStrength(unit: object): number

eix Added by addon.dll.

Returns the strength of a unit.

GetUnitExp

function GetUnitExp(unit: object, what?: string): number

eix Added by addon.dll.

Returns the experience of a unit: max (the default), spent, penalty (the experience not yet added) or real (experience plus penalty).

GiveDexterity

function GiveDexterity(unit: object, amount: number): void

Adds dexterity points to a unit. Zones only; works on enemies as well.

GiveExp

function GiveExp(player: number, amount: number): void

eix Added by addon.dll.

Awards experience to a player’s party.

GiveIntelligence

function GiveIntelligence(unit: object, amount: number): void

Adds intelligence points to a unit. Zones only; works on enemies as well.

GiveSkill

function GiveSkill(unit: object, skill: string, amount: number): void

Raises a skill (science, melee, archery, and so on) by amount points. The experience needed is added to the unit, not to the party, and spent on the skill, so health and stamina follow.

GiveStrength

function GiveStrength(unit: object, amount: number): void

Adds strength points to a unit. Zones only; works on enemies as well.

GiveUnitExp

function GiveUnitExp(unit: object, amount: number): void

eix Added by addon.dll.

Awards experience to one unit.

Differs from the original: GiveUnitExp stops on a missing object (the original crashes)

HP

function HP(unit: object): number

Returns the unit’s current health, with the effects of weakening and strengthening spells.

HP - SpellAddon hptrans

function HP(unit: object, index: number): number

eix SpellAddon option [gameplay] hptrans.

Reads a float by index from the unit’s vitals. Valid indices are 0 to 29; the fields are listed under indexed stat readers.

InflictDamage

function InflictDamage(unit: object, amount: number): void

Deals damage to the unit, ignoring its armor. Experience is granted for a kill.

IsAlive

function IsAlive(unit: object): number

eix Added by addon.dll.

Returns 1 if the unit is alive. Returns 0 for a missing object or a non-unit. Replaces the game’s function.

IsDead

function IsDead(unit: object): number

eix Added by addon.dll.

Returns 1 if the unit is dead. Returns 0 for a missing object or a non-unit. Replaces the game’s function.

IsUnitBlocked

function IsUnitBlocked(unit: object): number

Returns 1 if the player’s control of the unit is blocked, otherwise 0.

KillUnit

function KillUnit(unit: object): void

Kills the unit. The party gets no experience for it.

Mana

function Mana(unit: object): number

Returns the unit’s current mana.

Mana - SpellAddon mptrans

function Mana(unit: object, index: number): number

eix SpellAddon option [gameplay] mptrans.

Reads a float from the unit’s stats, indices 0 to 448; an invalid object, controller or index returns 0. The fields are listed under indexed stat readers. Also adds the index argument to HP, which keeps its reader unless hptrans is 1.

MaxHP

function MaxHP(unit: object): number

Returns the unit’s maximum health, with the effects of weakening and strengthening spells.

MaxHP - SpellAddon legendaryspellsystem

function MaxHP(unit: object, index: number): number

eix SpellAddon option [gameplay] legendaryspellsystem.

Reads a byte, starting at the skill-byte area of the unit, indices 0 to 631; outside it returns 0.

Differs from the original: stays inside the unit (the original reads any address)

MaxMana

function MaxMana(unit: object): number

Returns the unit’s maximum mana.

MaxMana - SpellAddon legendaryspellsystem

function MaxMana(x1: number, y1: number, z1: number, x2: number, y2: number, z2: number, extra: number): number

eix SpellAddon option [gameplay] legendaryspellsystem.

Takes seven numeric arguments, but the option supplies no working path-query implementation.

WasLooted

function WasLooted(unit: object): number

Returns whether the unit was robbed or its corpse searched: 0 if not. Stealing a quest item does not change the result.

WasLooted - SpellAddon waslootedtrans

function WasLooted(unit: object, bit?: number): number

eix SpellAddon option [gameplay] waslootedtrans.

Value 1: returns the full flag word. Value 2: WasLooted(unit, bit) returns the selected bit. Known bits: 15 moving, 19 knocked out, 20 looted, 24 stunned, 28 feebleminded.

Orders and behaviour

Orders to units, their stances, and the behaviours of the AI.

AlarmPosX

function AlarmPosX(alarm: number): number

Returns the x coordinate of the point set for the alarm number by InvokeAlarm.

AlarmPosY

function AlarmPosY(alarm: number): number

Returns the y coordinate of the point set for the alarm number by InvokeAlarm.

AlarmTime

function AlarmTime(alarm: number): number

Returns the time counter of the alarm number, in 1/15 s units. The counter runs while the alarm is the active one.

Attack

function Attack(unit: object, target: object): void

Orders the unit to attack the target.

Crawl

function Crawl(unit: object): void

Sets the unit’s stance to crouching.

Crawl - SpellAddon crawltrans

function Crawl(unit: object, slot: number): void

eix SpellAddon option [gameplay] crawltrans.

Requests a weapon slot instead of setting the crawl style.

Follow

function Follow(unit: object, target: object): void

Orders the unit to follow the target once. The order ends after a fight. See UMFollow.

Guard

function Guard(unit: object, x: number, y: number, radius: number): void

Orders the unit to guard the area of the given radius around (x, y), moving about in it.

Idle

function Idle(unit: object): void

Drops all behaviours and the current order of the unit: it stands and does nothing.

InvokeAlarm

function InvokeAlarm(alarm: number, x: number, y: number): void

Raises the alarm with the given number at the point (x, y). Only one alarm is active at a time: a new number replaces the previous one. Enemies with that alarm number in their .mob go to the point and guard it. InvokeAlarm(0, 0, 0) clears the alarm, and they return to their places.

IsAlarm

function IsAlarm(alarm: number): number

Returns 1 if the alarm number has been raised, otherwise 0.

Lie

function Lie(unit: object): void

Sets the unit’s stance to lying down.

MoveToObject

function MoveToObject(unit: object, target: object): void

Orders the unit to walk, in its current stance, to the place where the target stands at this moment. The unit does not follow the target.

MoveToObject - SpellAddon movetoobjecttrans

function MoveToObject(unit: object, target: object, skill?: number, unused?: number, run?: number, slot?: number): void

eix SpellAddon option [gameplay] movetoobjecttrans.

Value 1: MoveToObject(unit, target) performs thieving while walking. Value 2: runs instead. Value 3 and up: the four numbers after the target supply the thieving order.

MoveToPoint

function MoveToPoint(unit: object, x: number, y: number): void

Orders the unit to move to the point (x, y).

PlayAnimation

function PlayAnimation(unit: object, animation: string): void

Plays an animation from the unit’s .adb file (res/database.res/<mask name>.adb; the mask is in RaceModels.txt). The first letter of the name sets the kind: u plays once, then back to normal; c loops, and the unit ignores everything until the next order; s is one step of a movement. Families: uattack01..09, udeath01..31, cidle01..09, ucast01..04, uhit01..21, crun01..02, cwalk01..09, ccrawl01, ucross01..09, scross01..09, crest; humans also have ubriefing##, uspecial## and cspecial##. An unknown name does nothing.

ResetTarget

function ResetTarget(unit: object): void

eix Added by addon.dll.

Resets the unit’s perception and reapplies its alarm state. Resets all guard, patrol and aggression behaviours: call it before building a patrol or guard list. Replaces the game’s command.

Differs from the original: refuses a negative slot or a unit without a controller

Rest

function Rest(unit: object): void

Orders the unit to rest. It sits down when it can.

RotateTo

function RotateTo(unit: object, x: number, y: number): void

Turns the unit to face the point (x, y). Does nothing right after AddUnitToServer: the unit has to exist on the map first.

Run

function Run(unit: object): void

Sets the unit’s stance to running.

Run - SpellAddon runtrans

function Run(unit: object, style: number): void

eix SpellAddon option [gameplay] runtrans.

Sets the requested movement style instead of always selecting run.

Sentry

function Sentry(unit: object, x: number, y: number): void

Orders the unit to guard the point (x, y) and attack the enemies it sees. It returns to the point afterwards.

SetEnemy

function SetEnemy(unit: object, diplomacyGroup: number): void

Makes the diplomatic group hostile to the unit, as if one of its members had attacked it. Units only.

SetPlayerAggression

function SetPlayerAggression(a: number, b: number): void

Does nothing.

SetSpellAggression

function SetSpellAggression(unit: object, aggression: number): void

Does nothing.

Stand

function Stand(unit: object): void

Sets the unit’s stance to walking, as Walk.

Stand - SpellAddon standtrans

function Stand(unit: object): void

eix SpellAddon option [gameplay] standtrans.

Also revives the unit. A non-unit argument causes no revival.

Differs from the original: does nothing for an argument that is not a unit (the original crashes)

StartAnimation

function StartAnimation(unit: object, path: string): void

Not implemented in game.exe: does nothing. Use PlayAnimation.

UMAg

function UMAg(unit: object, preset: number): void

Replaces the unit’s fear, aggression and suspicion behaviours with a preset: 0 always afraid (flees), 1 afraid (fear without the “always” flag), 2 revenge plus fear. Each preset also adds suspicion and corpse watching.

UMAggression

function UMAggression(unit: object): void

Adds aggression: the unit attacks the enemies it sees.

UMClear

function UMClear(unit: object): void

Removes all independent behaviours of the unit. The current order stays.

UMCorpseWatcher

function UMCorpseWatcher(unit: object): void

Adds corpse watching: the unit reacts to dead bodies.

UMFear

function UMFear(unit: object, always: number): void

Makes the unit never attack and try to hide from enemies, the default of neutral beasts. If always is nonzero, the unit fears the player for good.

UMFollow

function UMFollow(unit: object, target: object): void

Orders the unit to follow the target for good. The order survives a fight. See Follow.

UMGuard

function UMGuard(unit: object, x: number, y: number, radius: number): void

Makes the unit guard the point (x, y) within the radius.

UMGuardEx

function UMGuardEx(unit: object, x: number, y: number, radius: number, delay: number): void

Makes the unit guard the point (x, y) within the radius, pausing for delay at random stopping points.

UMPatrol

function UMPatrol(unit: object): void

Adds a patrol behaviour. The command raises an error in the game. To make a unit patrol, call ResetTarget, then UMPatrolAddPoint.

UMPatrolAddPoint

function UMPatrolAddPoint(unit: object, x: number, y: number): void

Adds the patrol point (x, y). Does nothing when the unit has no patrol behaviour. Add UMPatrolAddPointLook after it to make the unit wait there.

UMPatrolAddPointLook

function UMPatrolAddPointLook(unit: object, x: number, y: number, delay: number): void

Sets the point (x, y) the unit looks at, and the time delay it stays, at the last patrol point.

UMPatrolClear

function UMPatrolClear(unit: object): void

Removes all patrol points of the unit. ResetTarget also resets the other behaviours.

UMPlayer

function UMPlayer(unit: object): void

Makes the unit obey the rules of a player’s unit, which gives sensible behaviour. Briefing zones use it for scripted NPCs. Clears the unit’s other behaviours and orders.

UMRevenge

function UMRevenge(unit: object): void

Gives the unit conditional fear: it runs away from everyone hostile and fights only when wounded or cornered.

UMSentry

function UMSentry(unit: object, x: number, y: number): void

Makes the unit guard the point (x, y) and attack the enemies it sees. It returns to the point afterwards.

UMStandard

function UMStandard(unit: object): void

Gives the unit the behaviour of a standard enemy: aggression, suspicion and corpse watching.

UMSuspection

function UMSuspection(unit: object): void

Adds suspicion: the unit goes to the nearest enemy it sees.

WaitEndAnimation

function WaitEndAnimation(): void

Not implemented in game.exe: does nothing.

Walk

function Walk(unit: object): void

Sets the unit’s stance to walking.

Spells

Casting spells and giving them to units.

Cast

function Cast(unit: object, x: number, y: number): void

Makes the unit cast its active area spell at the point (x, y). The spell must be one meant for a place, not for targets.

Cast - SpellAddon castscripttrans

function Cast(unit: object, x: number, y: number, spell: number, target?: object): void

eix SpellAddon option [gameplay] castscripttrans.

Value 1: Cast(unit, x, y, spell) uses the numbered spell. Value 2: with target, casts on the target if there is one, otherwise at the point.

Differs from the original: value 2 reads its target from the running command (the original: a stale value)

CastSpellPoint

function CastSpellPoint(spell: string, x1: number, y1: number, x2: number, y2: number): void

Casts a terrain spell by its name: the effect starts at (x1, y1) and lands at (x2, y2). Area spells only.

CastSpellPoint - SpellAddon castspellpointtrans

function CastSpellPoint(spell: string, x1: number, y1: number, x2: number, y2: number, caster: object): void

eix SpellAddon option [gameplay] castspellpointtrans.

Adds a caster unit to the named-spell command.

CastSpellUnit

function CastSpellUnit(spell: string, x: number, y: number, unit: object): void

Casts a spell on a unit. The visual effect starts at (x, y).

CastSpellUnit - SpellAddon castspellunittrans

function CastSpellUnit(spell: string, x: number, y: number, target: object, caster: object): void

eix SpellAddon option [gameplay] castspellunittrans.

Adds a caster unit to the named-spell command.

GiveUnitSpell

function GiveUnitSpell(unit: object, spell: string): void

Puts the spell into the unit’s set of active spells. Nothing happens when the set is full. Does not work in the shop; in a zone the spell is given, but the panel does not refresh.

GiveUnitSpell - SpellAddon giveunitspelltrans

function GiveUnitSpell(unit: object, name: string): void

eix SpellAddon option [gameplay] giveunitspelltrans.

Creates and adds a quick item instead of a spell, from the same unit and name arguments.

Players, parties and diplomacy

Players, their parties, and the relations between diplomatic groups.

AddUnitToParty

function AddUnitToParty(player: number, name: string, unit: string): void

Adds a localized character to a party; name is PartyName::NamePersRes.

AddUnitUnderControl

function AddUnitUnderControl(player: number, unit: object): void

Adds the unit’s icon to the player’s control panel. Diplomacy does not change.

CopyItems

function CopyItems(player: number, from: string, to: string): void

Copies the equipment (not the wagon, not the backpack) of a member of the current party to a member of another party of the same player. to is PartyName::Name.

CopyStats

function CopyStats(player: number, from: string, to: string): void

Copies the skills, abilities and parameters of a member of the current party to a member of another party of the same player. to is PartyName::Name.

CreateParty

function CreateParty(player: number, name: string): void

Creates a party for the player. A party of the same name is replaced. The name is case-sensitive.

GetDiplomacy

function GetDiplomacy(groupA: number, groupB: number): number

Returns what group A is to group B: 1 ally, 0 neutral, -1 enemy.

GetMercsNumber

function GetMercsNumber(player: number): number

Returns the number of mercenaries in the player’s current party, the leader not counted.

GetPlayer

function GetPlayer(unit: object): number

Returns the diplomatic group of the unit.

GetPlayer - SpellAddon getplayertrans

function GetPlayer(unit: object, index: number): number

eix SpellAddon option [gameplay] getplayertrans.

Reads a 32-bit integer at four times the index within the unit (0 to 310); returns 0 outside it.

Differs from the original: returns 0 outside the unit (the original reads any address)

GetPlayerOfUnit

function GetPlayerOfUnit(unit: object): number

eix Added by addon.dll.

Returns the number of the player the unit belongs to, or -1.

GetUnitOfPlayer

function GetUnitOfPlayer(player: number, index: number): object

Returns the unit at position index in the player’s current party, in the order the units joined.

IsEnemy

function IsEnemy(unit: object, diplomacyGroup: number): number

Returns 1 if the unit is an enemy of the diplomatic group, otherwise 0.

IsPlayer

function IsPlayer(unit: object): number

eix Added by addon.dll.

Returns 1 if the named unit belongs to a player, otherwise 0.

IsPlayerInDanger

function IsPlayerInDanger(player: number): number

Returns 1 if the player’s danger state is “in danger”, otherwise 0 (also for a player that does not exist). The state has three values: safe, an in-between value, and in danger; at the in-between value both this and IsPlayerInSafety return 0.

IsPlayerInSafety

function IsPlayerInSafety(player: number): number

Returns 1 if the player’s danger state is “safe”, otherwise 0 (also for a player that does not exist). Not the opposite of IsPlayerInDanger: at the state’s in-between value both return 0.

PlayerExists

function PlayerExists(player: number): number

eix Added by addon.dll.

Returns 1 if the player exists, otherwise 0.

RedeployParty

function RedeployParty(player: number): void

Finishes the change of the current party.

RemoveParty

function RemoveParty(player: number, name: string): void

Removes the party.

RemoveUnitFromControl

function RemoveUnitFromControl(player: number, unit: object): void

Removes the unit’s icon from the player’s control panel.

RemoveUnitFromParty

function RemoveUnitFromParty(player: number, name: string): void

Removes a character from its party. PartyName::Name reaches a party other than the current one.

SendCurrentParty

function SendCurrentParty(player: number): void

eix Added by addon.dll.

Resends the player’s party state.

Differs from the original: SendCurrentParty stops on a missing object (the original crashes)

SetCurrentParty

function SetCurrentParty(player: number, name: string): void

Makes the party the player’s current one.

SetDiplomacy

function SetDiplomacy(groupA: number, groupB: number, relation: number): void

Sets the relation between two diplomatic groups: 1 ally, 0 neutral, -1 enemy. The player’s party is always group 0.

SetPlayer

function SetPlayer(unit: object, diplomacyGroup: number): void

Assigns the unit to a diplomatic group.

SetPlayer - SpellAddon setplayertrans

function SetPlayer(unit: object, value: number, field: number): void

eix SpellAddon option [gameplay] setplayertrans.

Writes a rounded 32-bit integer at four times field within the unit (0 to 310). Fields 132 to 148 keep the game’s normal player-setting path; a write outside the unit is ignored.

Differs from the original: stores nothing outside the unit (the original writes any address)

Items and money

Items, quest items, wagons and money.

AddLoot

function AddLoot(player: number, from: string, to: string): void

Moves all items and gold from the wagon of a party to the wagon of another party of the same player.

CopyLoot

function CopyLoot(player: number, from: string, to: string): void

Copies the wagon (items and gold) of a party to the wagon of another party, which is emptied first. Do not confuse wagon and backpack: RedeployParty empties the sender’s backpack, and the new party’s one stays empty, though its items still show in the window.

EraseQuestItem

function EraseQuestItem(player: number, id: number): void

Removes a quest item from the player’s wagon.

FixItems

function FixItems(): void

Repairs every item of the current party: wagon, backpack, worn equipment and weapons.

GetLootItemsCount

function GetLootItemsCount(player: number): number

Returns how many item lines the current party has picked up in the zone, found, stolen or taken from enemies and corpses, quest items included. Money does not count, and identical items picked up one at a time each count.

GetMoney

function GetMoney(player: number): number

Returns the money of the player’s current party.

GiveItem

function GiveItem(player: number, item: string): void

Gives a regular item to the player’s backpack.

GiveMoney

function GiveMoney(player: number, amount: number): void

Adds money to the player’s party. Does not work in a shop.

GiveQuestItem

function GiveQuestItem(player: number, item: string): void

Gives a quest item to the player.

GiveQuestItem - SpellAddon legendaryspellsystem

function GiveQuestItem(unit: object, value: number): void

eix SpellAddon option [gameplay] legendaryspellsystem.

Writes byte 10 of the unit’s skill-byte area.

GiveUnitArmor

function GiveUnitArmor(unit: object, name: string, override: number): void

eix Added by addon.dll.

Creates an armor and equips the unit with it. override 1 also updates a non-named unit’s natural protection.

Differs from the original: stops on a missing object (the original crashes)

GiveUnitQuestItem

function GiveUnitQuestItem(unit: object, item: string): void

Puts a quest item into the unit’s backpack, such as a chest’s contents. For a member of the player’s party it goes to the party’s wagon.

GiveUnitWeapon

function GiveUnitWeapon(unit: object, name: string, override: number): void

eix Added by addon.dll.

Creates a weapon and equips the unit with it. override 1 also updates a non-named unit’s natural attack.

Differs from the original: stops on a missing object (the original crashes)

HaveItem

function HaveItem(player: number, id: number): number

Returns 1 if the player has the quest item in the wagon or the backpack, otherwise 0.

Loot

function Loot(player: number, unit: object): void

eix Added by addon.dll.

Loots the dead unit and removes it.

RemoveQuestItem

function RemoveQuestItem(unit: object, id: number): void

Removes a quest item from the unit’s backpack. Only items given by GiveUnitQuestItem or listed in the .mob file’s QuestItems are removed.

Quests

Dynamic quests of multiplayer zones.

QFinish

function QFinish(): void

Completes the quest started by QStart. In single player the quest’s experience is paid too.

QObjArea

function QObjArea(area: number): void

Quest objective: enter the area. The area is a virtual object made of all the parts with that ID, usually area 1.

QObjGetItem

function QObjGetItem(id: number): void

Quest objective: get the quest item with the ID. A macro for ordinary commands.

QObjKillGroup

function QObjKillGroup(group: string): void

Quest objective: kill the whole game group, given by the name of its global variable.

QObjKillUnit

function QObjKillUnit(unitName: string): void

Quest objective: kill the named unit. A macro for ordinary commands.

QObjSeeObject

function QObjSeeObject(objectName: string): void

Quest objective: see the named object, a chest and not a unit. A macro for ordinary commands.

QObjSeeUnit

function QObjSeeUnit(unitName: string): void

Quest objective: see the named unit. A macro for ordinary commands.

QObjUse

function QObjUse(lever: string, state: number): void

Quest objective: put the lever into the state. A macro for ordinary commands.

QStart

function QStart(name: string): void

Starts a dynamic multiplayer quest and opens its list of objectives, which ends with QFinish.

QuestComplete

function QuestComplete(player: number, name: string): void

Completes the quest and pays its experience, like QFinish but without the message in the info window.

Player variables

Named numeric variables that belong to a player.

GSDelVar

function GSDelVar(player: number, name: string): void

Deletes a variable of the player.

GSGetPlayerVar

function GSGetPlayerVar(player: number, name: string): number

eix Added by addon.dll.

Returns a numeric variable of a player.

GSGetVar

function GSGetVar(player: number, name: string): number

Returns the value of a variable of the player. Works differently in single and multiplayer.

Differs from the original: a name ini.<file>.<section>.<key> reads that value of <file>.ini in the mod or game folder, 0 when missing (INI variables)

GSGetVar - SpellAddon gsgetvar

function GSGetVar(player: number, name: string): number

eix SpellAddon option [speed] gsgetvar.

Returns the difficulty for names that begin with Diff, and the update period for names whose characters 4 to 7 (zero-based) are peed, such as SetSpeed.

Differs from the original: reads the variable’s name before freeing it

GSListPlayerVars

function GSListPlayerVars(player: number): void

eix Added by addon.dll.

Prints the variables of a player.

Differs from the original: stops on a missing object (the original crashes)

GSSetPlayerVar

function GSSetPlayerVar(player: number, name: string, value: number): void

eix Added by addon.dll.

Sets a numeric variable of a player.

Differs from the original: GSSetPlayerVar stops on a missing object (the original crashes)

GSSetVar

function GSSetVar(player: number, name: string, value: number): void

Sets a variable of the player. Works differently in single and multiplayer.

GSSetVar - SpellAddon gssetvar

function GSSetVar(player: number, name: string, value: number): void

eix SpellAddon option [speed] gssetvar.

Also sets the update period for names with peed at characters 4 to 7. The variable is still stored normally.

GSSetVarMax

function GSSetVarMax(player: number, name: string, value: number): void

Sets a variable of the player only when the new value is higher than the stored one.

World, effects and sound

Levers, areas, time, weather, lights, particles, sound, music, movies and the camera.

ActivateTrap

function ActivateTrap(trap: object, activate: number): void

Activates a trap on the map if activate is nonzero, deactivates it if 0. Traps attack only diplomatic group 0.

AddRectToArea

function AddRectToArea(area: number, x1: number, y1: number, x2: number, y2: number): void

Adds a rectangle, given by two opposite corners, to a quest area.

AddRoundToArea

function AddRoundToArea(area: number, x: number, y: number, radius: number): void

Adds a circle to a quest area.

AttachParticles

function AttachParticles(id: number, unit: object): void

Attaches the particle source to the unit. It follows the unit with the offset given in CreateParticleSource.

AttachParticleSource

function AttachParticleSource(id: number, unitId: number): void

Attaches the particle source to the unit with the ID unitId. It follows the unit with the offset given in CreateParticleSource.

CreateFX

function CreateFX(x: number, y: number, z: number, volume: number, radius: number, wav: string): void

Plays a WAV file once at (x, y, z), audible within the radius.

CreateFXSource

function CreateFXSource(id: number, x: number, y: number, z: number, volume: number, radius: number, wav: string): void

Creates a looping sound source at (x, y, z).

CreateLightning

function CreateLightning(id: number, x1: number, y1: number, z1: number, x2: number, y2: number, z2: number, intensity: number): void

Creates a lightning that stays, from (x1, y1, z1) to (x2, y2, z2). A positive intensity ends the bolt in a ball, as if it hit an object.

CreateParticleSource

function CreateParticleSource(id: number, x: number, y: number, z: number, radius: number, type: string): void

Creates a particle source of the given type at (x, y, z). A negative radius flips the source upside down.

CreatePointLight

function CreatePointLight(id: number, x: number, y: number, z: number, radius: number, r: number, g: number, b: number): void

Creates a point light of the given color at (x, y, z). A torch or a campfire at night is a light; a dim one darkens a dense forest by day.

CreateRandomizedFXSource

function CreateRandomizedFXSource(id: number, x1: number, x2: number, y1: number, y2: number, z1: number, z2: number, volume: number, radius: number, wav: string): void

Creates a looping sound source placed at random inside the box x1..x2, y1..y2, z1..z2. Remove it with DeleteFXSource.

DeleteArea

function DeleteArea(area: number): void

Deletes every part of the quest area.

DeleteFXSource

function DeleteFXSource(id: number): void

Deletes all sound sources with the ID.

DeleteLightning

function DeleteLightning(id: number): void

Deletes all lightnings with the ID.

DeleteParticleSource

function DeleteParticleSource(id: number): void

Deletes all particle sources with the ID.

DeletePointLight

function DeletePointLight(id: number): void

Deletes all point lights with the ID.

EnableLever

function EnableLever(lever: object, enable: number): void

Lets the player use a lever (1) or not (0). A usable lever shows the grabbing-hand cursor.

FixWorldTime

function FixWorldTime(time: number): void

Sets the time of day and stops the clock.

FixWorldTime - SpellAddon fixworldtimetrans

function FixWorldTime(time: number, speed: number): void

eix SpellAddon option [gameplay] fixworldtimetrans.

Adds an explicit time-speed argument.

GetLeverState

function GetLeverState(lever: object): number

Returns 1 if the lever is on, 0 if it is off.

GetWorldTime

function GetWorldTime(): number

Returns the time of day, in hours.

IsCameraPlaying

function IsCameraPlaying(): number

Returns 1 if a camera path file is playing now, otherwise 0.

IsNight

function IsNight(): number

Returns how dark it is, from 0 to 1, by the game’s hour of day: 0 from 8:00 to 20:00, rising evenly from 0 at 20:00 to 1 at 24:00, and 1 from 0:00 until 8:00, when it drops to 0 at once. The server updates it every tick.

MoveParticleSource

function MoveParticleSource(id: number, x: number, y: number, z: number, time: number, remove: number): void

Moves the particle source to (x, y, z) over the time, in 1/15 s units. If remove is nonzero, the source is deleted on arrival.

MovePointLight

function MovePointLight(id: number, x: number, y: number, z: number, time: number, remove: number): void

Moves the point light to (x, y, z) over the time, in 1/15 s units. If remove is nonzero, the light is deleted on arrival.

PlayCamera

function PlayCamera(name: string): void

Starts the automatic camera path in the file CAMERA\<name>.

PlayFX

function PlayFX(x: number, y: number, z: number, radius: number, wav: string): void

Not implemented in game.exe: does nothing.

PlayMovie

function PlayMovie(name: string): void

Plays the video MOVIES\<name>, the name with its extension.

PlayMusic

function PlayMusic(player: number, name: string): void

Plays the piece STREAM\<name> for the player, the name without its extension. The name 0 turns the zone’s music off, 1 on.

PlayMusic - SpellAddon playmusictrans

function PlayMusic(player: number, name: string): void

eix SpellAddon option [gameplay] playmusictrans.

Values 1 and 2: the name "0" or "1" re-selects the local map’s music theme and sets the music system’s level to the digit. The first character selects this behavior. Value 1 uses the map’s theme, value 2 its alternative name field. Other text, or no map or music system, uses normal handling.

Differs from the original: without a map or a music system, does what the game does (the original crashes)

RunWorldTime

function RunWorldTime(hour: number): void

Sets the time of day and lets the clock run at normal speed.

SetBackGroundColor

function SetBackGroundColor(r: number, g: number, b: number): void

Sets the color the screen is cleared to, which is the sky behind everything. Each value is 0 to 255.

SetCameraOrientation

function SetCameraOrientation(angle: number): void

Not implemented in game.exe: does nothing.

SetCameraPosition

function SetCameraPosition(x: number, y: number, z: number): void

Not implemented in game.exe: does nothing.

SetDirectionToObject

function SetDirectionToObject(unit: number): void

Not implemented in game.exe: does nothing.

SetParticleSourceSize

function SetParticleSourceSize(id: number, radius: number): void

Changes the radius of the particle source.

SetScience

function SetScience(lever: object, questItem: number, skill: number, hands: number, lockpick: number, crowbar: number, key: number): void

Sets how a lever can be switched: with the quest item, with that much Sleight of hand, and by hands, lockpick, crowbar or key (1 allowed, 0 not).

SetSunLight

function SetSunLight(sunR: number, sunG: number, sunB: number, ambientR: number, ambientG: number, ambientB: number, skyR: number, skyG: number, skyB: number): void

Not implemented in game.exe: does nothing.

SetWaterLevel

function SetWaterLevel(basin: number, level: number, delay: number): void

Sets the water level of a basin, relative to its bottom, after the delay. Does not change where the party can wade.

SetWeather

function SetWeather(name: string): void

eix Added by addon.dll.

Sets the weather: rain or snow, exact and lowercase. Any other text clears the weather.

SetWind

function SetWind(x: number, y: number, z: number, force: number): void

Makes a gust of wind blow toward the point (x, y, z) with the given force.

ShowBitmap

function ShowBitmap(id: number, bitmap: string): void

Not implemented in game.exe: does nothing.

ShowCredits

function ShowCredits(text: string): void

Does nothing.

SwitchLeverState

function SwitchLeverState(lever: object, state: number): void

Puts the lever into the off (0) or on (1) position. GetLeverState reads it.

SwitchLeverStateEx

function SwitchLeverStateEx(lever: object, state: number, delay: number): void

Like SwitchLeverState, with a delay before the animation.

Game and system

Zone changes, game modes, game end and files.

GameOver

function GameOver(): void

eix Added by addon.dll.

Ends player 0’s game with the default title and text, the text keys game_over and game_over_msg.

GameOverMSG

function GameOverMSG(title: string, text: string): void

eix Added by addon.dll.

Ends player 0’s game with the given text keys for title and text.

GodMode

function GodMode(player: number, mode: number): void

Sets the mode of the player’s current party: 0 normal, 1 invulnerable when running and mana is free, 2 like 1, and every hit kills.

IsFileExists

function IsFileExists(path: string): number

eix Added by addon.dll.

Tests whether a file exists under the mod folder. A path that climbs to a parent folder is refused.

GSGetIniNumber

function GSGetIniNumber(file: string, section: string, key: string, default: number): number

eix Added by addon.dll.

Reads a value from an INI file of the mod folder, at the call, as a number; default when the file, section or key is missing or the value is not a number. The file is a path below the mod folder, with / or \ as separators; an absolute path, a drive letter or a .. component is refused and gives default. The decimal point is . or ,. File, section and key are matched without regard to case. A missing value or a refusal is logged once per file, section and key.

GSGetIniString

function GSGetIniString(file: string, section: string, key: string, default: string): string

eix Added by addon.dll.

Reads a value from an INI file of the mod folder, at the call, as text; default when the file, section or key is missing. The path and matching rules are those of GSGetIniNumber. A key with an empty value gives an empty string.

KillUnit - SpellAddon killunittrans

function KillUnit(kind: number, strength: number): void

eix SpellAddon option [gameplay] killunittrans.

Sets the weather with two rounded numbers instead of killing a unit.

LeaveToZone

function LeaveToZone(player: number, zone: string, entry: number): void

Moves the player, every party and the wagon, to the zone, at its entry place entry (the numbers are in Res/texts.res/map.txt).

PauseGame

function PauseGame(value: number): void

eix Added by addon.dll.

Outside multiplayer, 0 requests a pause and any other value unpauses.

RecalcMercBriefings

function RecalcMercBriefings(): void

Single player: recalculates which dialogues the companions and candidates offer.

ShowAllUnits

function ShowAllUnits(value: number): void

eix Added by addon.dll.

0 draws all units; any other value restores normal visibility.

StartCameraSave

function StartCameraSave(file: string): void

eix Added by addon.dll.

Starts recording a camera path to a file.

StopCameraSave

function StopCameraSave(): void

eix Added by addon.dll.

Stops recording the camera path.

Lua

With [Addon] EnableLuaScripts set in the mod’s ai.reg, addon.dll runs the mod’s Lua script and calls it from nine patch groups. The other ai.reg settings are under addon and loader.

The last column of the table below gives eix’s difference from addon.dll 0.12.4.2, with what the original does in parentheses.

Host

Set [Addon] EnableLuaScripts nonzero and supply LuaMainScriptPath (default main.lua). The host is LuaJIT 2.1, with bit and jit loaded as globals; load ffi with require("ffi"). Module search paths include the mod root and its lua folder. Define only the callbacks the mod changes: once the main script has run, each group hooks the game where the script defines a callback, and the game’s own code runs for the rest. A callback defined later is not hooked. addon.log lists the callbacks found. A reported Lua error shows a message, logs its traceback, and exits. Pointers are passed as signed 32-bit integers; raw memory operations require valid game memory.

Behavioureix
LuaJIT builda newer LuaJIT 2.1 (the original: 2.1.0-beta3)
A callback the script does not definethe game’s own code runs there (the original hooks every site of the nine groups and stops with a Lua error at the first call)
A Lua errorthe message is shown as text (the original treats it as a printf format, so %d is consumed); the box is titled addon.dll; the message and its traceback also go to addon.log (the original logs nothing)
Lua state at exitnot closed at exit, since a fatal error box exits from inside a Lua call

Callbacks and functions

The Lua reference lists the callbacks the mod defines, with the patch group that calls each, and the functions eix supplies. Each entry has its LuaLS signature and its difference from addon.dll 0.12.4.2. The same declarations ship as the definitions file eix.lua for the Lua language server.

Editor setup

With the definitions file, an editor running the Lua language server (LuaLS) completes eix’s functions, shows their documentation on hover and checks the arguments of each call.

  1. Install the language server’s plugin: in VS Code, the Lua extension (sumneko.lua); other editors run lua-language-server.
  2. From the docs folder of the release package, put eix.lua in the game folder, beside game.exe, and .luarc.json in the mod folder (Mods\<mod>). Open the mod folder in the editor. The site has eix.lua too.

The .luarc.json names the definitions file relative to the mod folder:

{
  "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
  "runtime.version": "LuaJIT",
  "workspace.library": ["../../eix.lua"]
}

A mod folder outside the game’s Mods folder names the file by its full path instead. A mod that has a .luarc.json already takes the two settings into it. After an eix update, replace eix.lua with the one from the new release.

Lua reference

The Lua API of a mod’s main script: the functions addon.dll registers before the script runs, and the callbacks the script defines. Lua says how the host runs the script. The declarations below are annotations for the Lua language server (LuaLS); the same text is the definitions file eix.lua, in the docs folder of the release package: editor setup says how to give it to the server for completion and type checks.

Types

The one type of this API that is not a Lua built-in.

Pointer

---@alias Pointer integer

A game address, passed as the 32 bits of the address read as a signed integer: an address above 2 GB is negative. Raw memory functions need an address that holds valid game memory. A record or slot index is zero-based and must lie inside the table or the slots.

Functions supplied by eix

Global functions that addon.dll registers before the main script runs. A signed 32-bit integer argument is read as Lua’s luaL_checkinteger reads it: a number or a numeric string, truncated. A bad argument raises LuaJIT’s usual argument error.

Round

---@param value number
---@return number
function Round(value) end

Rounds half away from zero.

IDiv

---@param a integer
---@param b integer
---@return integer
function IDiv(a, b) end

Signed integer quotient. A zero divisor or an overflow ends the game.

IMod

---@param a integer
---@param b integer
---@return integer
function IMod(a, b) end

Signed integer remainder. A zero divisor or an overflow ends the game.

GetEffectValue

---@param unit Pointer
---@param effect integer
---@return number
function GetEffectValue(unit, effect) end

The strength of an effect on a unit.

GetPerkModifierForServer

---@param stats Pointer
---@param perk integer
---@return number
function GetPerkModifierForServer(stats, perk) end

The modifier of a perk, read from a unit’s stats on the server.

GetPerkModifierForClient

---@param params Pointer
---@param perk integer
---@return number
function GetPerkModifierForClient(params, perk) end

The modifier of a perk, read from a unit’s parameters on a client.

Heal

---@param stats Pointer
---@param vitals Pointer
---@param amount number
function Heal(stats, vitals, amount) end

Heals the body slots and recomputes the total health.

RegenMp

---@param vitals Pointer
---@param fraction number
function RegenMp(vitals, fraction) end

Adds the maximum mana times fraction, capped at the maximum mana.

DistributeHp

---@param stats Pointer
---@param maxHp number
function DistributeHp(stats, maxHp) end

Sets the body slots’ maxima from maxHp and clamps their current health.

CollectHp

---@param stats Pointer
---@param maxHp number
---@return number
function CollectHp(stats, maxHp) end

Computes the total health from the body slots.

GetPerkRecord

---@param index integer
---@return Pointer
function GetPerkRecord(index) end

The perk record at index, or 0 past the table.

GetWeaponRecord

---@param index integer
---@return Pointer
function GetWeaponRecord(index) end

The weapon record at index, or 0 past the table.

GetArmorRecord

---@param index integer
---@return Pointer
function GetArmorRecord(index) end

The armor record at index, or 0 past the table.

GetMaterialRecord

---@param index integer
---@return Pointer
function GetMaterialRecord(index) end

The material record at index, or 0 past the table.

GetQuickRecord

---@param index integer
---@return Pointer
function GetQuickRecord(index) end

The quick-item record at index, or 0 past the table.

GetSpellRecord

---@param index integer
---@return Pointer
function GetSpellRecord(index) end

The spell record at index, or 0 past the table.

GetRuneRecord

---@param index integer
---@return Pointer
function GetRuneRecord(index) end

The rune record at index, or 0 past the table.

GetPerksTableSize

---@return integer
function GetPerksTableSize() end

The number of perk records.

GetWeaponsTableSize

---@return integer
function GetWeaponsTableSize() end

The number of weapon records.

GetArmorsTableSize

---@return integer
function GetArmorsTableSize() end

The number of armor records.

GetMaterialsTableSize

---@return integer
function GetMaterialsTableSize() end

The number of material records.

GetQuicksTableSize

---@return integer
function GetQuicksTableSize() end

The number of quick-item records.

GetSpellsTableSize

---@return integer
function GetSpellsTableSize() end

The number of spell records.

GetRunesTableSize

---@return integer
function GetRunesTableSize() end

The number of rune records.

GetPerkIndex

---@param record Pointer
---@return integer
function GetPerkIndex(record) end

The index of a perk record, or -1 if it is not found.

GetSpellIndex

---@param record Pointer
---@return integer
function GetSpellIndex(record) end

The index of a spell record, or -1 if it is not found.

GetRuneIndex

---@param record Pointer
---@return integer
function GetRuneIndex(record) end

The index of a rune record, or -1 if it is not found.

GetClassId

---@param object Pointer
---@return integer
function GetClassId(object) end

The class ID of an object.

GetItemsWeight

---@param unit Pointer
---@return number
function GetItemsWeight(unit) end

The total weight of a unit’s equipped items.

GetItemShapeEntry

---@param item Pointer
---@return Pointer
function GetItemShapeEntry(item) end

The prototype record of an item.

GetWeapon

---@param unit Pointer
---@param slot integer
---@return Pointer
function GetWeapon(unit, slot) end

The weapon in a slot (0 to 3) of a unit, or 0 if the slot is empty or out of range.

GetArmor

---@param unit Pointer
---@param slot integer
---@return Pointer
function GetArmor(unit, slot) end

The armor in a slot (0 to 6) of a unit, or 0 if the slot is empty or out of range.

GetQuick

---@param unit Pointer
---@param slot integer
---@return Pointer
function GetQuick(unit, slot) end

The quick item in a slot (0 to 3) of a unit, or 0 if the slot is empty or out of range.

GetSpell

---@param unit Pointer
---@param slot integer
---@return Pointer
function GetSpell(unit, slot) end

The spell in a slot (0 to 7) of a unit, or 0 if the slot is empty or out of range.

ConsoleWriteLine

---@param text string
function ConsoleWriteLine(text) end

Writes a line to the console.

GetGSVar

---@param unit Pointer
---@param name string
---@return number? value `nil` if the unit has no player or the variable does not exist.
function GetGSVar(unit, name) end

Reads a variable of the unit’s player. Names are case-sensitive.

SetGSVar

---@param unit Pointer
---@param name string
---@param value number
function SetGSVar(unit, name, value) end

Writes a variable of the unit’s player. Names are case-sensitive.

UnitIsHost

---@param unit Pointer
---@return boolean
function UnitIsHost(unit) end

Tests whether the unit belongs to the hosting player.

TempDumpVars

---@param unit Pointer
function TempDumpVars(unit) end

Prints every variable of the unit’s player to the console.

FloatFromMem

---@param address Pointer
---@return number
function FloatFromMem(address) end

Reads a 32-bit float.

DoubleFromMem

---@param address Pointer
---@return number
function DoubleFromMem(address) end

Reads a 64-bit float.

Int8FromMem

---@param address Pointer
---@return integer
function Int8FromMem(address) end

Reads a signed 8-bit integer.

Int16FromMem

---@param address Pointer
---@return integer
function Int16FromMem(address) end

Reads a signed 16-bit integer.

Int32FromMem

---@param address Pointer
---@return integer
function Int32FromMem(address) end

Reads a signed 32-bit integer.

UInt8FromMem

---@param address Pointer
---@return integer
function UInt8FromMem(address) end

Reads an unsigned 8-bit integer.

UInt16FromMem

---@param address Pointer
---@return integer
function UInt16FromMem(address) end

Reads an unsigned 16-bit integer.

UInt32FromMem

---@param address Pointer
---@return integer
function UInt32FromMem(address) end

Reads a 32-bit integer. The result is the signed reading, as for Int32FromMem.

FloatToMem

---@param address Pointer
---@param value number
function FloatToMem(address, value) end

Writes a 32-bit float.

DoubleToMem

---@param address Pointer
---@param value number
function DoubleToMem(address, value) end

Writes a 64-bit float.

Int8ToMem

---@param address Pointer
---@param value integer
function Int8ToMem(address, value) end

Writes the low 8 bits of an integer.

Int16ToMem

---@param address Pointer
---@param value integer
function Int16ToMem(address, value) end

Writes the low 16 bits of an integer.

Int32ToMem

---@param address Pointer
---@param value integer
function Int32ToMem(address, value) end

Writes a 32-bit integer.

UInt8ToMem

---@param address Pointer
---@param value integer
function UInt8ToMem(address, value) end

Writes the low 8 bits of an integer.

UInt16ToMem

---@param address Pointer
---@param value integer
function UInt16ToMem(address, value) end

Writes the low 16 bits of an integer.

UInt32ToMem

---@param address Pointer
---@param value integer
function UInt32ToMem(address, value) end

Writes the 32 bits of an integer, converted as Int32ToMem converts it.

StrFromMem

---@param address Pointer
---@return string? text `nil` if the stored pointer is null.
function StrFromMem(address) end

Reads the string pointer stored at address, then the NUL-terminated text it points to.

GetIniValue

---@param file string
---@param section string
---@param key string
---@return string? value `nil` if the path is refused or the file, section or key is missing.
function GetIniValue(file, section, key) end

Reads a value from an INI file of the mod folder, at the call. File, section and key are matched without regard to case. The file is a path below the mod folder, with / or \ as separators; an absolute path, a drive letter or a .. component is refused with a warning in addon.log, as is a missing value (once per file, section and key). Convert a number with tonumber, which wants a . as the decimal point.

Callbacks supplied by the mod

Global functions that the main script defines, each called by the patch group named in its description. Every callback is optional: where the script defines none, the game’s own code runs. A callback that returns nothing updates the structures it receives through their addresses.

GetSkillPriceDiff

---@param from integer
---@param to integer
---@return integer cost
function GetSkillPriceDiff(from, to) end

Group LuaGetPerkPrice. The experience cost of a skill step.

GetPerkPrice

---@param stats Pointer
---@param perkRecord Pointer
---@return integer cost
function GetPerkPrice(stats, perkRecord) end

Group LuaGetPerkPrice. The experience cost of a perk.

GetModifiedDamageOnServer

---@param stats Pointer
---@param unit Pointer
---@param damage number
---@return number damage
function GetModifiedDamageOnServer(stats, unit, damage) end

Group LuaGetModifiedDamage. The damage a unit takes on the server, without the game’s floor of 1.

GetModifiedDamageOnClient

---@param stats Pointer
---@param unit Pointer
---@param damage number
---@return number damage
function GetModifiedDamageOnClient(stats, unit, damage) end

Group LuaGetModifiedDamage. The damage a unit takes on a client, without the game’s floor of 1.

CalcBaseHealth

---@param stats Pointer
---@return number health
function CalcBaseHealth(stats) end

Group LuaRecalcUnitStats. The base health of a unit.

CalcBaseMana

---@param stats Pointer
---@return number mana
function CalcBaseMana(stats) end

Group LuaRecalcUnitStats. The base mana of a unit.

GetModifiedHealth

---@param stats Pointer
---@param unit Pointer
---@param hp number
---@return number health
function GetModifiedHealth(stats, unit, hp) end

Group LuaRecalcUnitStats. The health of a unit after its effects.

RecalcUnitStats

---@param stats Pointer
---@param vitals Pointer
---@param unit Pointer
---@param effectMaxima Pointer
function RecalcUnitStats(stats, vitals, unit, effectMaxima) end

Group LuaRecalcUnitStats. Recomputes a unit’s statistics; effectMaxima points to 40 floats.

Differs from addon.dll 0.12.4.2: sets the alive bit in the stats’ flags (the original sets it in the build flags); an intelligence raise marks the unit changed (the original does not); a strength raise widens the unit by 0.2, as the game does (the original narrows it by mistake); a unit without vitals is skipped (the original crashes).

RecalcInternalHeroStats

---@param stats Pointer
---@param newLoad number
function RecalcInternalHeroStats(stats, newLoad) end

Group LuaRecalcUnitStats. Resets a hero’s statistics to their base values.

Differs from addon.dll 0.12.4.2: sets the alive bit in the stats’ flags (the original sets it in the build flags); an intelligence raise marks the unit changed (the original does not); a strength raise widens the unit by 0.2, as the game does (the original narrows it by mistake); a unit without vitals is skipped (the original crashes).

RegenerateHpAndMp

---@param vitals Pointer
---@param stats Pointer
---@param unit Pointer
---@param inCombat boolean
---@return boolean changed Whether something changed.
function RegenerateHpAndMp(vitals, stats, unit, inCombat) end

Group LuaRegenerateHpAndMp. Regenerates health and mana.

CalcRunningManaCost

---@param stats Pointer
---@param unit Pointer
---@param vitals Pointer
function CalcRunningManaCost(stats, unit, vitals) end

Group LuaCalcRunningManaCost. The mana a running unit pays; subtract it from the mana in the callback.

CalcAttackParamsOnServer

---@param stats Pointer
---@param unit Pointer
---@param params Pointer
---@param effectMaxima Pointer
function CalcAttackParamsOnServer(stats, unit, params, effectMaxima) end

Group LuaCalcAttackParams. Computes an attack’s parameters on the server; effectMaxima points to 40 floats.

Differs from addon.dll 0.12.4.2: a hit from another player’s unit is ignored unless SpellAddon’s enablekillsinmulti is on, as without the group (the original lets players’ units always hurt each other).

CalcAttackParamsOnClient

---@param unitParams Pointer
---@param unit Pointer
---@param params Pointer
---@param effectMaxima Pointer
function CalcAttackParamsOnClient(unitParams, unit, params, effectMaxima) end

Group LuaCalcAttackParams. Computes an attack’s parameters on a client; effectMaxima points to 40 floats.

Differs from addon.dll 0.12.4.2: a hit from another player’s unit is ignored unless SpellAddon’s enablekillsinmulti is on, as without the group (the original lets players’ units always hurt each other).

GetManaBonusOnServer

---@param stats Pointer
---@return number factor
function GetManaBonusOnServer(stats) end

Group LuaGetExpModifiers. The mana factor of a unit on the server.

GetManaBonusOnClient

---@param params Pointer
---@return number factor
function GetManaBonusOnClient(params) end

Group LuaGetExpModifiers. The mana factor of a unit on a client.

GetExpMultiplierOnServer

---@param stats Pointer
---@return number multiplier
function GetExpMultiplierOnServer(stats) end

Group LuaGetExpModifiers. The experience multiplier of a unit on the server.

GetExpMultiplierOnClient

---@param params Pointer
---@return number multiplier
function GetExpMultiplierOnClient(params) end

Group LuaGetExpModifiers. The experience multiplier of a unit on a client.

Differs from addon.dll 0.12.4.2: experience for a unit the client does not have shows unchanged, without a call (the original passes the script a pointer into nothing).

RecalcShopsPriceMultipliers

---@param multipliers Pointer
---@param isLmp boolean
function RecalcShopsPriceMultipliers(multipliers, isLmp) end

Group LuaRecalcShopsPriceMultipliers. Sets the price multipliers of the shops; multipliers points to nine floats per shop.

DistributeLmpExp

---@param exp number
---@param players integer
---@return number share
function DistributeLmpExp(exp, players) end

Group LuaLmpExpDistribution. The share of a kill’s experience for each player.

Differs from addon.dll 0.12.4.2: no value is left on the FPU stack per call (the original’s leftovers corrupt later results).

Addon and loader

dinput.dll loads addon.dll, which loads the selected mod, applies fixes and extensions, and writes addon.log. For installation and supported game builds, see the overview. SpellAddon has its own option reference, and mod scripts in Lua have the Lua reference; additional eix fixes are under fixes.

The last column of each table gives eix’s difference from addon.dll 0.12.4.2 and Addon Loader 1.1.0.0, with what the original does in parentheses. same means the original’s behavior; fixed: marks a difference that only fixes a defect of the original. A name in parentheses after a key is its patch group’s name in addon.log.

Configuration

Settings come from:

SourceContents
HKCU\Software\Gipat.Ru\EI_Starter\SettingsLauncher preferences; values below are DWORDs unless stated otherwise
Mod folder’s config.regMod paths, resource archives, menus and plugins
File named by config.reg’s [CONFIG] ai.regGameplay settings, experience, skill caps and Lua

The .reg files use the game’s registry format, not Windows Registry Editor syntax. A missing key takes its default, and so does a string where a number is expected or a single value where an array is expected. An integer is accepted where a float is expected, and a float where an integer is expected (truncated). A negative integer keeps its sign where a float is expected (the original reads -1 as 4294967296). Gameplay groups require a selected mod and a nonempty [CONFIG] ai.reg path. Configure multiplayer participants consistently for gameplay changes. Groups apply in a fixed order, and later groups can replace earlier changes. A failed byte check skips the whole group and logs why.

Launcher settings

ValueDefaultEffecteix
WideScreen1Adapt the interface to a wide resolution; 0 disables. VideoResolution is a WxH string under EI_Starter\EvilIslands\General Settingsfixed: an out-of-range scaling value wraps instead of crashing; a failed curtain window or text size is skipped (the original writes through null)
ObjectsDrawRadius100Object draw radius; 100 leaves the game’s valuesame
LandscapeDrawRadius100Terrain draw radius; 100 leaves the game’s valuesame
AntiCrash (FixCreateItem, DisableExitOnCrashReport)1Report invalid item creation and fatal game errors without the game’s forced exit; mod [Addon] AntiCrash overrides when presentsame
AutoRecover1Back up the hosting player’s party on a crash; 0 disablesfixed: with all 1000 backup names taken, nothing is backed up (the original writes into the drive’s root)
FullDump0Nonzero includes the process’s full memory in crash dumpssame
LogLevel, ConsoleLogLevel1, 3Minimum log/console severity: 0 debug, 1 information, 2 warning, 3 error, 4 offsame
Rate (SetDefaultRate)5000Only 2500 enables the group, which writes 2500, the value the game already holds; no visible effect on RU 1.06same
Console AutoExec (AutoExec)EmptyREG_MULTI_SZ console commands, executed before 2utoexec.cfg and the game’s autoexec.cfgsame
MQ client (MQClient)0Nonzero prevents writing files received from the serversame
MP Speed-control (MPSpeedControl)0Nonzero allows speed keys and clock buttons in multiplayersame
MP console (MPConsole)0Nonzero allows console commands in multiplayersame

eix’s own settings are in the mod folder’s eix.ini, not in the registry.

Settings\AddonPath is a string naming the selected mod folder. The loader searches for addon.dll using the starter root’s AddonPath (full DLL path), then Settings\AddonPath\addon.dll, then the starter root’s Path\addon.dll; the first existing file wins.

Mod configuration: config.reg

Paths under [CONFIG] and plugin paths are relative to the mod folder. An empty [CONFIG] path other than lights<X>.ini disables its override.

Section and keyDefaultEffecteix
[MOD] Title, AuthorEmptyMod identificationsame
[MOD] Multi, Single (MenuMultiDisable, MenuSingleDisable)1 each0 disables the respective main-menu entrysame
[MOD] GameSaves (SavesPath)1Nonzero puts saves and multiplayer heroes under the mod foldersame
[MOD] PreventZone1Animation0Nonzero skips the opening zone animationsame
[MOD] ForceShowTutorial0Nonzero prevents saved preferences and the options window from disabling tutorialssame
[MOD] ShowTutorialDefaultNoneThe “Show tutorial” option’s value while none is stored; the player’s later choice winseix’s own (fixes)
[MOD] AddonVersionEmptyRequired addon version as text. The game stops with a message when it is not a dotted version of at most four numbers, or names a version newer than 0.12.4.2same
[CONFIG] ai.reg (ConfigAiReg)EmptyReplace the game’s AI settings and enable the gameplay groupsfixed: a path of 260 bytes or more is refused and the game’s own ai.reg loads (the original ends the process)
[CONFIG] movie.ini, tutorial.ini (ConfigMovieIni, ConfigTutorialIni)EmptyReplace the respective configurationfixed: a path of 260 bytes or more fails the group (the original ends the process)
[CONFIG] music.reg, streamsn.reg (ConfigMusicReg, ConfigStreamSNReg)EmptyReplace music and streamed-sound configurationfixed: a path of 260 bytes or more fails the group (the original ends the process)
[CONFIG] lights<X>.ini (ConfigLightsIni)NoneReplace a matching lights file; use lowercase key names. The key’s presence enables it: an empty value names the mod folder itselfsame
[DEV] GameDebugFile (GameExeDebug)EmptyWrite game debug messages to this file, relative to the game working foldersame
[DEV] ChatHtmlFile (ChatLog)EmptyAppend server chat to this HTML file, relative to the game working folderfixed: names and messages are escaped, and the speakers’ colours show (the original writes them raw, and its colour classes are not valid CSS)
[DEV] PluginEmptyLoad a plugin DLL, a filename pattern, or a folder’s *.plg files; empty or missing loads the mod folder’s *.plg filesplugins are called in load order (the original: by module handle, which the system chooses)
[MPZone] Gipath, Ingos, Suslanger (MpZone)MissingReplace multiplayer zone lists; provide all three as strings or string arrays; duplicate names are rejected ignoring casesame
[RES] <archive> (ResOverride)NoneString or string array of replacement archive paths, in file order; a path starting with - suppresses the game’s archivefixed: archive paths of 260 bytes and more work (the original ends the process); an archive that fails to open is left out and logged

[RES] replacement keys: aiinfo.res, database.res, databaselmp.res, gfx.res, materials.res, outro.res, redress.res, sfx.res, speech.res, texts.res, textslmp.res, textures.res. figures.res and menus.res also add figure archives; textures.res, redress.res and menus.res add texture archives. The listed mod archives are searched before the game’s archive, in the supplied order.

Loose files in the mod folder override matching game files:

Mod fileEffecteix
Maps folder (Maps)Mapsfixed: no length limit (the original overruns its buffers at 260 bytes and more)
Movies folder (MoviesPatch)Moviesfixed: movie paths of 260 bytes and more work (the original ends the process)
Camera folder (CameraPatch)Camera pathsfixed: camera paths of 260 bytes and more work (the original overruns its buffer)
Stream folder (StreamPatch)Streamed soundfixed: stream paths of 260 bytes and more work (the original ends the process)
binkw32.dll (BinkPatch)Replace the game’s movie decoderfixed: a binkw32.dll without BinkSetVolume: the volume call does nothing, with a warning (the original crashes)

A Stream folder can also hold .flac tracks: FLAC music.

Gameplay: ai.reg

Unless stated otherwise, these [Addon] keys default to 0: 0 leaves the option off; any nonzero integer enables it.

KeyEffecteix
ShowAllQuests (MPShowAllQuests)Offer every multiplayer zone separately instead of grouping questssame
DisableRunawayKeep an action when its target moves out of reachsame
ArchersDefence (EnableArchersDefence)Retain defence while wielding a bow or crossbowsame
DistanceModifier (DisableDistanceModifier)Remove the distance penalty from attack rollssame
WeightBonus (DisableWeightBonus)Remove the weapon-weight bonus from attack rollssame
MagicRandom (RandomMagicDamage)Float, default -1 (off); nonnegative values scale spell damage by 1 + MagicRandom * u, with random u in [-1, 1)same
AllowMagicNegative (NegativeMagicEffect)Allow negative spell effects; choose the strongest overlapping effect by magnitudesame
DeagrPlayerOnLooseStop tracking an unseen enemy merely because it is on guard; clear a player’s target when sight of another player’s unit is lostfixed: releases the target reference it clears (the original leaks it)
AttackAnimSpeedMatch attack and cast animation speed to their action durationfixed: a cast of no time plays at normal speed, as an attack of no time does (the original’s speed is then infinite)
NewRunes (NewRuneCodes)Rune code xa adds its whole strength to cast time, with a minimum of one ticksame
UsePrototypeActionsForMobAttackUse the natural attack interval for non-named units, scaled by their action ratesame
UsePrototypeActionsForMobCastUse the natural attack interval for non-named units’ casts, scaled by their action ratesame
FastTeleportReduce the extra teleport delay from 30 ticks to onesame
DisableCrippledHLDisable crippling a hit location on a severing hitsame
UnlockExperimentalSpellsAllow Possession, Link and Charm through the spell-casting restrictionsame
LoadSpellAnimationFromSlotsFieldSelect cast animations from the spell record’s slots fieldsame
ImproveInfoWindowsShow spell damage to one decimal, cast time in seconds, and the unit rate percentagefixed: info windows keep their text alive while it is drawn (the original draws freed memory); same text shown
RemoveSpellConstructReqsRemove mana and skill requirements for spell construction and equippingsame
SendSpellEffectValueToPlayersAdd effect strengths to network state; enable on both host and clients because it changes the record formatsame
AddWeaponsFromDbToAllUnitsGive all units their prototype’s starting weapons and armor, not just heroessame
UnlockCorpsesAllow searching a corpse already owned or searched by another unitsame
AntiCrashDefault: launcher value; override the two AntiCrash groupssame
LogErrorsInFile, LogErrorsInConsole, LogErrorsInChatSend game-data error reports to errors.log, the console, or multiplayer chatsame
EnableShowingScriptErrorsEnable the script error displaysame
EnableLuaScriptsLoad the main Lua script and enable the nine Lua groupsa group hooks only where the script defines a callback; the game’s own code runs for the rest (the original hooks everything and stops with a Lua error at the first call of a missing callback)
LuaMainScriptPathString, default main.lua; path relative to the mod foldersame
UsePerkLimitEnables and disables nothingsame
Section and keyDefaultEffecteix
[AddonExp] ChargeMethod (ExpCalc)0Share awarded experience among heroes: 1 multiplies by hero count, 2 gives each hero the whole amount, all other values divide by countfixed: 2 gives the whole amount (the original adds the hero count to it)
[AddonExp] MulFactor (ExpCalc)1.0Multiply the resulting experience sharesame
[AddonExp] Limit (ExpLimit)-1Negative disables the cap; otherwise cap experience at this value times the hero’s intelligence factorfixed: no stray FPU pop on an empty stack
[AddonPerk] Melee, Archery, Elemental, Sense, Astral (PerkLimit)100 eachTraining caps for the five skills; any value other than 100 enables the cap group; Backstab is a perk, not a sixth skillsame
[DifficultyLevels] Absorption, Attack, DefenceEmptyFloat arrays of difficulty modifiers, one entry per levelsame

Lua experience modifiers replace the earlier experience-cap calculation. SpellAddon may replace overlapping gameplay rules when it applies later; select one implementation of a rule when configuring a mod.

Automatic groups

These need no individual switch. Mod-file overrides and the settings above supply the remaining group controls. MultipleLoadingMobs also needs a selected mod and a nonempty [CONFIG] ai.reg. SetEiPluginMode, PlayerNumbers, AddNewScriptCommands and PersistentGlobalVars run only when no incompatible EI_Plugin build (*.asi) is in the game folder; a mod whose AddonVersion is 0.10 or newer then stops the game with a message.

GroupEffecteix
AllowScriptArgsInLoopsMake enclosing script locals available inside For and ForIfsame
AllowScriptsInMainMenuRun the main-menu zone’s scriptsame
FixLeversActivateCorrect signed lock/key level comparison and locks that require no skillsame
FixSpellEffectsOverflowLimit accumulated effects, replace renewed effects, and expire effects on sleeping objectsfixed: a missing object in the sleep count-down is skipped (the original crashes); every renewed effect goes (the original keeps the second of two in a row)
FixProgressNamesUse the separate loading movies for the allods gipat2 and jigransame
AddNewConsoleCommands, ImproveConsoleAdd commands below, clipboard shortcuts, and suppress consecutive duplicate history linesfixed: execs without a file name does nothing (the original reads a stale argument); a paste over a selection puts the caret after the pasted text (the original puts it past the selection’s end, less one); a repeated line leaves no selection (the original may leave one)
NewStringEventsAdd gameover [title [text]]; defaults are text keys game_over and game_over_msgfixed: a failed allocation pushes no window (the original pushes null)
FixSpellSelfCastingCorrect offensive-spell classification and clicked-unit targetingsame
FixProcessLoginInitialize the joining player’s age fieldsame
FixMoveObjectReject negative or invalid map coordinates instead of indexing an invalid sectorthe refusal message goes to addon.log; coordinates round half to even, NaN prints NaN
FixAddMapObjectReport a missing unit prototype and use the fallback recordsame
FixPlaingBattleMusicFromAntimagicStop Antimagic alone from triggering combat musicsame
FixRepairPriceBound wear to 0..1 and include repair cost correctly when taking an item apartfixed: an item with no maximum condition has no wear (the original’s price is then not a number)
FixRemoveMapObjectReport an unknown object instead of crashing on removalsame
ExtendedServerNameEnlarge the multiplayer server-name field and wrap or shorten display textfixed: a name of 90 bytes or more is cut to 89 (the original ends the process)
NivalRegistryUse the starter’s game-settings registry branchsame
SetEiPluginModeDisable the EI plugin’s duplicate script commands; eix supplies their replacementssame
PlayerNumbersGive each new session player the next number after the highest existing playersame
AddNewScriptCommandsAdd the script interface below and log script-load errorssame
PersistentGlobalVarsPreserve party variables with the case-sensitive gvar. prefix across network resets and saved-party reloadssame
MultipleLoadingMobsWhen adding a mob to a populated zone, skip duplicate object IDs and assign unused IDs from 100 to anonymous objectsfixed: frees the entries it skips (the original leaks them); anonymous objects of one file each get their own ID (the original gives them one, so only the last appears)

Console and command line

The console supports Ctrl+A/C/X/V for selection, copy, cut and first-line paste. Enter the game’s cheat password before addon, execs, listgvar or drawall; nogui needs none.

Console commandEffect
addon infoShow addon compatibility version and current mod
addon debug log [level], addon debug console [level]Read or set verbosity: 0 debug, 1 information, 2 warning, 3 error, 4 off
execs <file>Load and run a script in the current zone; report a load error with its line
listgvarList the zone script’s globals, including strings, objects and groups
drawall <value>0 draws all objects; other values restore normal visibility
nogui <mode>0 shows panels and cursor, 1 hides panels, 2 hides panels and cursor

Run these from the game folder (CommandLine; aigraph also GraphGenPatches):

CommandEffect
game.exe addon helpPrint command help
game.exe addon versionPrint the addon compatibility version
game.exe addon runStart the game
game.exe addon check <path>...Check script text, .mob files, or directories of .mob files
game.exe addon aigraph single <mob> [mpr]Generate the single-player AI graph
game.exe addon aigraph multi <mob> [mpr]Generate the multiplayer AI graph

Place --moddir <name> before the command to select a mod under the starter’s Mods folder. Without an mpr argument, the map name is derived from the mob name, dropping -lmp. Script-check exit codes: 0 success, 2 unreadable file, 3 nothing checked, 4 script error or failed batch.

Game script interface

addon.dll adds functions and commands to the game’s script language, and replaces some of the game’s own. The script reference lists them with the rest of the language, tagged eix, with the differences from addon.dll 0.12.4.2.

Loader and start-up behavior

Only the differences; everything else behaves as the original.

Behavioureix
DirectInputCreateA, DirectInputCreateW, DirectInputCreateExfixed: a missing system dinput.dll or export: a message box and E_FAIL (the original returns success with no interface and the game crashes)
Game build checkchecks image base, size, time stamp and entry point (the original: the entry point alone); the box names the build found. The patches fit RU 1.06 byte for byte and would corrupt another build
Old-loader modenot supported: an old loader reaching addon.dll gets an error box and the game exits. eix ships both DLLs, so the mode is never reached
Unknown game.exe in addon.dlladdon.dll checks the build again, logs it and stops with a box (the original addon.dll checks none)
Patch writes are verifieda site that does not hold the expected bytes skips the whole group, logged with site, expected and found bytes (the original logs and writes anyway)
config.reg and ai.regfixed: a file with a valid header and a broken body reads as empty, with an addon.log error (the original reads garbage or crashes); a section with no keys reads as empty (the original’s lookup there crashes start-up)
addon.log: openingan addon.log that cannot be opened gets a message box, and the game goes on (the original runs silently without it)
addon.log: when a line is writtenfixed: each line is written at once (the original buffers, so a crash loses the last lines)
Exit of a commandfixed: ends with the same exit code and no box (the original stops on an R6025 - pure virtual function call box)
addon.log: what it recordsalso the eix build and commit, the Windows and Wine versions, the game.exe build, and the loaded modules (the original logs none of these)
Crash handler: a filter set laterfixed: a plugin that replaces the crash handler (Universal Mod’s um.dll) no longer disables it: eix’s handler runs the others first, then its own. A crash within a quarter of a second of the plugin setting its filter reaches only the plugin’s
Crash handler: the recordaddon.log gets the crash’s code, address, access, registers and return addresses, each address with the patch group, fix or SpellAddon feature that put code there, and the loaded modules (the original writes only the dump)
Crash handler: the boxasks for a report at eix’s issue tracker, naming addon.log and the dump (the original asks for the dump at its author’s address)
Tracernot reproduced: the original’s per-patch hit counter reports wrong names and traps every hit
When built-in SpellAddon runsapplies when the sound system starts, where the original module loads; a separate SpellAddon*.asi is not loaded
spelladdon.ini choicefixed: the mod folder’s file, else the game folder’s, else SpellAddon is off (the original reads AddonPath’s file unchecked, and with some registry setups a garbage path, so every key takes its default)
spelladdon.ini numeric parametersfixed: parammin, parammax, balance, exred, exgreen, exblue, hitdistmul and hitdistadd keep fractions (the original truncates to integers); framex and framey keep their integer reading
spelladdon.ini digit parserfixed: runconst, runconstex, pow and expforint without a decimal point read as whole numbers (the original divides by ten for each digit); points and commas keep their meaning
spelladdon.ini switcheskeeps the signed low byte, with a warning for an out-of-range value naming the key, written value and used value (the original truncates silently)
spelladdon.ini aliasesaccepts avrgarmors for avgarmors, and the gameplay-section spell AI, spell construction and multiplayer quest keys for their speed-section keys, with an info line; a canonical key wins, with a warning naming the ignored alias (the original ignores these aliases)
spelladdon.ini duplicate keysthe first value wins, with a warning naming the section, key, used and ignored values (the original keeps the first silently)
The fixed patchthe spell-name patch that the original always writes is written only when an INI is found
SpellAddonInfo.txtnot written (the original writes its documentation to the working folder)
Sites that do not holda feature whose sites do not hold the expected bytes, or whose original crashes, is logged and left out; the others apply (the original writes anyway, and a crash stops every later feature)

SpellAddon options

The built-in SpellAddon supports SPELLADDON.INI. It uses the mod-folder file first, then the game-folder file. It reads one file, without merging them. Without either file, SpellAddon is off. The built-in version takes precedence over separate SpellAddon*.asi files.

Loader and INI compatibility details: addon start-up behavior.

The last column of each feature table gives eix’s difference from SpellAddon 20.6 (SpellAddonXa.asi), with what the original does in parentheses. same means the original’s behavior; fixed: marks a difference that only fixes a defect of the original.

Configuration

All feature switches default to 0 (off), including when an INI file exists. Parameter defaults below apply only when their feature uses them; they are not the sample values from older guides. Restart the game after edits. An INI file also enables the spell-construction diagnostic correction, independently of its feature switches.

Section and key names are case-insensitive. Spaces around names are ignored; disablepausekey works without the historical trailing space. Duplicate keys use the first value and produce a warning. Canonical keys take precedence over aliases: [gameplay] avrgarmors aliases avgarmors; [gameplay] showallquestsinmulti, spellsaimod and spellsarealwaysconstructable alias their [speed] counterparts.

The tables give active values. Other values make no change unless marked skipped. A skipped value is logged and left out, and every other feature still applies. Switches retain a signed byte: use the listed values within -128..127; larger values wrap and warn. 1+ below means 1..127. Byte parameters retain the low byte (0..255), and other values warn; 32-bit parameters retain 32 bits. Floating-point parameters accept decimal text with a sign and an exponent; use a decimal point. runconst, runconstex, pow and expforint read only their first 21 characters, take a leading -, ignore any other character that is not a digit, point or comma, and also accept a decimal comma (the original ignores the sign too). Other numeric text holds at most 31 bytes; font text holds at most 47 bytes. Parameters have no general range validation. Limits stated below describe their use, not an extra validator.

Features apply in a fixed order, independent of INI order. Where two features change the same code, the later one builds on the earlier, or the earlier is skipped with a warning when the later replaces all it does; the tables call out those interactions. A conflicting or unexpected game patch causes the affected feature to be skipped and logged. If writing the merged patches fails, no feature applies. See the addon options for the other built-in settings.

Combat - [gameplay]

KeyActive values and effecteix
archersdefence1+: bows and crossbows retain defence.same
unbreakablebodyparts1+: prevent limb breaking; also use bodypartbreakmodifier for the limb-damage floor.same
enablekillsinmulti1: remove player damage immunity so players can hurt other players.fixed: god mode still holds (the original drops it too)
rangedweaponbackstabs1: allow ranged backstabs.fixed: with newrangedweapontypes, its change to how orders are carried out stays (the original’s newrangedweapontypes replaces it)
backstabsforall1: allow units without a player to backstab.same
newattackdefence1, 2: physical damage after armour uses attack minus defence divided by divmod, plus submod and the speed term; 1 also includes the distance/weight term. 3, 4: use max((attack - defence) * divmod + 1, submod); backstabs ignore defence. 4 also disables misses.same
damageboost1: scale weapon damage by strength/dexterity above 25 and spell effects by intelligence above 25, dividing by str, dex, int. 2: multiply by those parameters instead. 3: bypass the normal effect scaling and its client display adjustment. 4: boost after weapon-effect scaling and at cast completion, using multiplication. 5: boost after scaling and mark casts so only their spell effects get the intelligence boost.same
changeweightrangebonuses1+: use weight and range in hit calculations; 2 also bypasses the conditional range test.same
strengthstats1: set strength’s health, damage and carrying-capacity coefficients with strhp, strdmg, strweight.same
allowheroesarmors1: retain heroes’ natural armour.same
newrangedweapontypes1: weapon types 11 and 15 count as ranged, alongside bows (5) and crossbows (6), throughout combat checks.same
randomlessaimedhits1-4: choose a vulnerable body part using the last hit. 5-9: estimate damage from the weapon’s damage range and target resistance. 2, 6, 7, 9 compare the target’s reach minus the attacker’s reach with rangeddistance; other modes use attack reach. Highest score wins; bodypart is the fallback. 10 is skipped.10 skipped (calls code the original never wrote); 5 and 8 keep the game’s minimum of distance and reach
increasinghitchance1: after more than tohitcheck misses, add misses * tohitmul - tohitsub to the hit threshold. The defender holds the counter; a hit resets it.fixed: with newattackdefence 1, 2 or 4, both apply (the original undoes part of newattackdefence)
runawaydistcontrol1: attack reach becomes weapon range * hitdistmul + hitdistadd.fixed: keeps fractional hitdistmul and hitdistadd (the original truncates them to integers)
nightpower2: multiply attack and defence by nightmeleeadd - P * nightmeleemul + vision * P * nightmeleemul / 100, where P is night level (0..1). 1 is skipped.1 skipped (every attack and defence value becomes garbage); fixed: with damageboost=3, the unit panel keeps that feature’s change (the original drops it)
duels1: suppress diplomacy reactions to damage when player 0’s und<victim id> variable rounds to nonzero.same
acidattack1, 2: unfinished acid-effect variants; neither changes the visible effect.same
armorspell1 is skipped.1 skipped (corrupts the stats refresh)

Progression and movement

SectionKeyActive values and effecteix
skillsextendedskills1: replace the non-stealing skill cap with [skills] skills.fixed: uses [skills] skills as its cap (the original uses [stealing] stealing)
stealingextendedstealing1: replace the Sleight of Hand cap with [stealing] stealing.fixed: uses [stealing] stealing as its cap (the original uses [skills] skills)
rebalanceenginerewriteskillscounting1: next skill level costs pow ^ level * mul + add.fixed: pow without a decimal point reads as a whole number (the original divides by ten for each digit)
rebalanceenginerewriteherolevelcounting1: level is round(mullvla * log2(exp / divlvla + addlvla) + addlvlb) on server and client. 2 is skipped.2 skipped (returns a stale level)
rebalanceenginerewriterunabilitytime1: use runconst as the running stamina coefficient; also supply runconstex for newrunsystem=2.fixed: runconst and runconstex without a decimal point read as whole numbers (the original divides by ten for each digit)
rebalanceenginenewrunsystem1: drain a fixed amount instead of maximum stamina times the coefficient. 2: drain maximum stamina times the coefficient, plus runconstex. rewriterunabilitytime controls the coefficient; otherwise the game uses 1/150.fixed: runconstex without a decimal point reads as a whole number (the original divides by ten for each digit)
rebalanceengineuseonlymindamage1: use minimum weapon/unit damage instead of rolling within its range.same
rebalanceenginebackstabvisualbase1: show bsbase as the base backstab percentage; display only.same
rebalanceenginedifficultyreset1: apply difficulty scaling to all units, including heroes. 2: also add difficulty-table values instead of multiplying by them.same
rebalanceenginemeleesum_low1: non-hero server attack/defence add the first skill and dexterity minus 25. Skipped unless meleesum_1 or meleesum_2 is exactly 1; meleesum_1=1 satisfies this although that feature is itself skipped.1 skipped without meleesum_1 or meleesum_2 (the original’s game hangs at the first attack)
rebalanceenginemeleesum_11 is skipped.1 skipped (the original crashes at the first attack)
rebalanceenginemeleesum_21: client attack/defence add the first skill and dexterity minus 25, retaining the low byte: the client side of meleesum_low’s sum.same
animationsspeedrewriteactionsspeed1: set six animation factors from this section’s parameters; also adjust client animation timing.same
gameplaychangeexpforint1: use expforint as the experience multiplier per intelligence point.fixed: expforint without a decimal point reads as a whole number (the original divides by ten for each digit)
gameplayheroalwayscanrun1: remove the load-based restriction on running.same
gameplayautomaticruncontrol1: stop running at zero stamina.same
gameplayspeedboost1: multiply movement speed by ((action counter + speedadd) / 15) ^ speedbase.same
gameplayactionspatch1: actions become dexterity * actionsmul + actionsadd.same
gameplayactionsdexteritymovespeed1+: actions and dexterity contribute to movement speed through actmovemul and dexmovemul.same
gameplaycleveranimationsspeedup1: set casting animation speed from cast duration and send animation speed in unit network state; also adjust client animation timing. Peers need compatible settings.same
gameplaynhits1: use a fixed count of five hits for the special multi-hit animations.same
gameplaydiffchange1: ignore clicks on the difficulty spinner in game options.same
gameplaynewdifficultylevels1: add normal between easy and hard in the new-game choice, default to normal, and use a difficulty slider in options.fixed: the box grows for the new row and the explanation moves below it; a click on the hard box selects hard (the original’s overlaps and ignores the click)

Items, shops and camp - [gameplay]

KeyActive values and effecteix
unlockmoney1: keep money unencoded in memory.same
saveall1+: use the game’s persistent-zone save/load handling for every zone. Value 32 also participates in extendedmerc’s draw-range extension.same
extendedmerc1+: set the briefing mercenary limit to the low byte of maxcountofmerc. With value 16+, saveall=32 and maxcountofmerc>50, extend the client draw range from 100 to 4096.same
shopschanges1+: change trader templates’ price/count fields; the final template uses price/count factors 1.0, 0.2 and 0.1. shopsnewnormalchanges=1 supplies jigran for its shared factor.same
shopsnewnormalchanges1: set stock ranges for weapons, armour, wands, potions, prototypes and runes, plus four traders’ sell-price factors and jigran for shopschanges.same
copystatscut1: CopyStats skips portions of the stats copy, retaining the recipient’s racial traits rather than making a complete clone.same
liftchanges1: unfinished carry-weight check; no game-state change.same
liftdebuffs1: packed stats are scaled by 1.01 - q / (q + liftkey), q = load / maximum load, while load is nonzero.same
encumbrancemovespeed1: subtract r^3 * encumbrancemovemul from movement factor, r = load / (load + maximum load). With speedslowremaster=2, also apply its Speed/Slow contribution, defaultspeedmul, minimumspeedmul, and the overload factor.same
encumbranceattackspeed1: load slows attack animations using (animation factor - r^3) * encumbranceattackmul. 2: load reduces the stats’ attack rate using speed factor - r^3 * encumbranceattackmul. r is as above.fixed: 1 slows rewriteactionsspeed’s attack factor and 2 actionspatch‘s actions (the original uses the game’s values there, dropping those features’ changes)
improvedstealing1: separate theft from corpse loot. A living hero retains equipped armour/weapons; a previously robbed hero’s corpse yields those, without repeating gold or stolen items. An already robbed living unit yields nothing.same
dmgtypes1: list dmgtypescount damage types in camp panels.same
avgarmors1: show average rather than maximum armour, including Endurance’s bonus. Requires newperks>=1; otherwise skipped. The divisor follows dmgtypescount when dmgtypes is active.also accepts [gameplay] avrgarmors when absent (the original ignores it); 1 skipped without newperks (the original crashes entering the camp)
itemslightchanges1: replace eight item-highlight RGBA colours with light00 through light31.same
heroscanhideweapons1: press the selected weapon’s key again, or click its selected slot, to sheathe it.same
camp1+: SendStringEvent(0, "constr") enters camp. 2+: the camera-track key (Home by default) also enters when player 0’s Camp is nonzero, then sets Camp=2. With newoptions>=1, modes 2-12 omit Home; newoptions=1 adds the camp binding and ability button. Each of 3, 4 and 13+ adds only its own extra: 3 tracks/removes quest items; 4 limits quick quest-item slots by QuIs; 13+ restore zone music on return and retain Home.fixed: the Home key without player 0 does nothing (the original runs into the next action); value 3’s quest-item handling keeps the game’s own steps
newoptions1: add camp (action 60) and kick bindings (61, 62). Camp’s action requires camp>=2; actions 60/61 otherwise end safely, with no kick behavior added.fixed: actions 60 and 61 end the battle screen’s handling (the original jumps into garbage), unless camp defines 60
newperks1+: add Endurance (armour against all seven damage types) and Ghostliness (less visibility), and separate the magic-school skills. 2 also changes the school field used by spell descriptions.fixed: Ghostliness lowers loudness as well as visibility, and visibility stops at 0 (the original’s loudness change is overwritten, and visibility can go below 0)

For camp=3, quest-item creation sets player 0’s q<item id>=2; taking the item sets it to 0, except while camp entry strips items. Set QuIs to the active quest-item count when using camp=4.

Spells - [gameplay] unless specified

KeyActive values and effecteix
possessioncharmlink1: allow casting Possession, Charm and Link.same
possessionpatch1: Possession retains the caster’s sight and registers both caster and target for script visibility.same
batterycharge1+: reject spells containing modifier codes ic or it when placing them in wands. Invalid rune-table references are skipped.fixed: a modifier past the rune table is skipped (the original crashes)
nonstopmanaregen1+: continue stamina/mana regeneration through the state that normally stops it.same
npcsmana1: charge NPCs mana on cast completion. 2: require enough mana before casting. 3: both. 4: make both exemptions depend on AI class 16 instead of player ownership.same
splashdamageonair1: area spells can hit flying units. 2: only Invoke Lightning gains this ability, including AI targeting.same
newspells1-3: extend spell dispatch, enable Ensorcell (42), Inspiration (43), script spells (44-48), and the Fireball-based spell 49. 1 also marks spell 49’s caster; 1/2 load the autorun mob. 3 adds lasting-effect cases and Ensorcell’s damage type. See script variables below. New spell records/scripts must come from the mod.fixed: the original’s crashes left out: Charm on a spawned wolf, the effect types that crash the effect loop, Charm and Healing with a broken stack; legendaryspellsystem and changefont keep their data
lastspellsdamagetype1: use astralbeam, destroyray, lightningbeam, firearrow as damage-type IDs.same
[speed] spellsaimod1: extend the AI offensive/friendly spell table to 54 types.also accepts [gameplay] spellsaimod when absent (the original ignores it)
[speed] spellsarealwaysconstructable1: bypass level checks when assembling spells or giving them to heroes.also accepts [gameplay] spellsarealwaysconstructable when absent (the original ignores it)
offensivespells1: mark Stench and Shrink as friendly in missile hostility checks.same
inspirationintelligence1: item Inspiration waits until mana is below maximum, as Healing waits for missing health; limit repeated item casts within the tick. 2/3 are skipped.2 and 3 skipped (Healing items crash)
teleportboost1: teleport duration becomes round(900 / (effect strength / 2 + 22)) + 2. 2: use delay 8 instead of 30. 3: use delay 1.same
shrinkenlargerebalance1, 2: Shrink and Enlarge no longer change unit size. 1 also changes the scale multiplier used by the size calculation.same
hidefireworks1: omit Fireworks’ drawing setup.same
wallsfrequency1: wall spells deal damage every frequencyvalue ticks.same
wallspercentage1: per-type wall fractions are percentagevalue * period / 150. 2: use each configured fraction directly.same
controlmagic1: Stun-like spells, Weaken and Slow meet target resistance; the latter two use weaknessmultiplier/slownessmultiplier. 2, 5, 7: add the 22 configured spell bonuses. 3: add bonuses only for spell switches with odd values. Duration/intelligence formulas from older guides do not run. 4/6 are skipped.4 and 6 skipped (corrupt durations)
effectredraws1: show all configured b<spell> bonuses as value+bonus in item descriptions, and their sum in the HUD, independently of the controlmagic switches.same
stenchremaster1: Stench increases visibility, loudness and two further perception stats by its strength times stenchmul.same
regenerationremaster1: add Regeneration strength times regenerationmul to natural regeneration.same
speedslowremaster1, 2: multiply Speed minus Slow by speedslowmul. 2 also affects movement when encumbrancemovespeed=1.same
strengthweakrebalance1: Strengthen minus Weaken, times strengthweakmul, scales strength.same
linkremaster1: every 32 ticks, transfer mana from the proportionally fuller linked unit towards equal fill ratios, limited by spell strength. 2/3 are skipped.2 and 3 skipped (the original crashes)
allowcastsonbriefings1: allow casting in briefing zones.same
fakesrcrune1: modifier code f starts a spell from its target point instead of its caster.same
filtersarealwaysusing1, 2: apply target filters to every spell.same
tornadochanges1+: use tordmg for tornado damage. 2+: also remove the unit exclusion, so it can hurt every unit.same
changesounds1: use fireboom as the Fireball volume factor.same
vampirism1 is skipped.1 skipped (the original runs unfinished code)
maxmanatrans1 is skipped. Do not use it for the advertised path-query interface.1 skipped (breaks every spell type but one)
fhrunetrans1 is skipped.1 skipped (the original crashes and stops every later feature)

AI and perception - [gameplay]

KeyActive values and effecteix
rewritenpcvisionarc1+: set NPC sight-arc value to visionarc.skipped with a warning when removenpcsblock is 1 or 2, which removes the value it sets (the original writes it unused)
visionfix1: bypass two lost-sight exits; with aggrmod>5, bypass only the first shared exit.same
aggrmod1: record lost-sight state, without changing the loss calculation. 2: distance-dependent random loss using losehardcore. 4/5: distance-dependent loss based on clock ticks, losefactor1/2. 6/7: per-viewer aggression counter using losehardcore, losefactor1/2/3, difffactor, maxadd. follow affects all these modes; loseallyfast affects 6/7. 3 is skipped; 6/7 omit the faulty improve alarm hook and warn if requested.3 skipped (corrupts the AI’s state); 6 and 7 with improve nonzero leave out the alarm hook (it acts on the wrong unit), with a warning; 2, 4-7 handle a zero divisor
spellsprioritys1: replace seven AI target-rating constants with the priority parameters below.same
otherspellcastingai1: permit area spells even with allies nearby.same
npcsspellcastingtactics1: NPCs lacking mana switch to weapons and return to spells once mana recovers.same
removenpcsblock1, 2: remove NPC-specific stats, AI and naming branches. 1 also removes the NPC difficulty-scaling exception.same
everyoneishero1: broaden the shared hero-ID test to nonnegative IDs below two billion. 2: selected game systems treat all units as heroes. 3: as 2 but retain difficulty scaling for enemy heroes.same
allysseeseachothers1: sight treats all units as player-owned. 2/3: also use the player branch during visibility updates. 4: sight uses hero IDs from one billion to below two billion as the ownership test.fixed: 4: looting works with or without improvedstealing (the original crashes without it at the first scripted loot)
newplayersafetyconditions1: use safetymuldist in party-safety distance checks and maxvisibilitymul for the visibility factor.same
fearpatch1: use fearsafetydist for retreat distance and legsdamagedmul for the leg-damage cutoff.same
stupidenemies1: witnessing a murder no longer invokes the diplomacy attack reaction.same
hyperreactions1: react to seen units/corpses without the own-side-only restriction.same
hearingreactionspatch1: fix the unit/target used in reactions to sounds. 2: also offset and scatter the destination with runtosoundsmul and randsoundfactor.same
visioninweather1: use visionweathermul in bad weather.same
nightvisionincaves1+: dark zones always use night vision, without daytime growth.same
nightpowervisionpatch1: ramp night level between visionevening/visionnight and visionmorning/visionday, with slope visionmulpowbetween.same
newbigtables1: alter the rebuilt AI tables’ positions/bounds. Requires the corresponding rebuild switches; it supplies no extra entries. The caster lookup shifts beyond the written table, and the offensive bound grows beyond its 38 entries.same
allvisible1 is skipped.1 skipped (corrupts the options load)
sizerebalance1-3 are skipped.1-3 skipped (corrupts the unit)
unitsrunfromdangers1 is skipped.1 skipped (passes a point as a unit)
SectionKeyActive values and effecteix
aicastertablerebuildedaicastertable1: replace the 54 spell AI classes with keys 0 through 53 in this section.same
aioffendtablerebuildedaioffendtable1: replace the 38 offensive/friendly entries with keys 0 through 37 in this section. No effect while spellsaimod=1, whose own 54-entry table the game reads instead.same

Multiplayer and speed

SectionKeyActive values and effecteix
gameplayfullexpforall1: every recipient gets the full experience award; do not divide it among recipients.same
gameplaymanygameexe1: allow multiple game processes.same
gameplayenablecheatsinmulti1: allow the console’s restricted commands in multiplayer.same
gameplayextendedparams1: character creation uses attribute bounds 5..45 and required sum 20000. 2: use parammin, parammax, balance and bypass the sum check.fixed: keeps fractional parammin, parammax and balance (the original truncates them to integers)
gameplaylongnick1: remove the character-name edit field’s length checks.same
gameplaydisableexppenalty1+: add experience directly to totals, bypass pending experience in training/trade, and remove its loss on respawn.same
speedallowmpspeed1+: enable multiplayer speed keys and clock-button speed changes.same
speedrewritespeed1: clock-button normal/fast periods use normal and fast.same
speedrewritespeedkey1: keyboard normal/fast periods use normalkey and fastkey.same
speedrewriteotherspeed1: apply both following changes. 2: reset to normal1 on battle-screen close. 3: initial clock period uses normal2/fast2.same
speeddisablepause1: clock pause sets period pause instead of pausing.same
speeddisablepausekey1: pause key sets period pausekey instead of pausing.same
speedshowallquestsinmulti1: bypass the multiplayer restriction on selectable quest zones.also accepts [gameplay] showallquestsinmulti when absent (the original ignores it)
speedruntimervars1-3: publish a timer state, 0 during mission/zone/save loading and 1 when the game interface appears. 1/3 differ in battle-screen paths; 2 uses 3D-interface activation instead of briefing creation. Requires an external timer that reads this interface.same

Speed periods are milliseconds per update: smaller means faster. Speed and network-format changes must be compatible between multiplayer peers. bigmapspatch and cleveranimationsspeedup change network data handling.

Interface and world - [gameplay] unless specified

KeyActive values and effecteix
rewriteperssize1: use perssize for the hero preview scale.same
[fonts] changefont1+: use newfont1 through newfont4. 2 also loads the font file newfont5 from the INI folder for this process.fixed: with newspells on, value 1 keeps the second and third font names (the original’s newspells overwrites them)
infowindowschanges1: compact the unit-info layout.same
infowindowremake1: combine attack/defence with hearing on one line and add two panel rows.same
showtype1: show the movement-type line in unit information.same
showmanaforeveryone1: show mana bars for non-heroes too.same
meterscale1: use meter for the information ruler scale.same
allodsinfo1: display the adesc <allod> text from texts.res in a frame sized by framex/framey.same
exploredcolors1: set explored-zone colour with exred, exgreen, exblue.fixed: keeps fractional exred, exgreen and exblue (the original truncates them to integers)
lightsboost1: replace the light-strength constant with newlightsvalue.same
mainmenuweather1: set weather type 2 at strength 40. 2: skip menu weather. 0 leaves the game’s weather unchanged.same
drawvisiblepatch1: zone entry no longer resets the draw-visible setting.same
playingnewloadingscreens1: name loading movies after the zone. 2: also use gz1g in the special initial-zone movie selection. Requires matching movie assets.same
notrash1: share the speech resource path instead of requiring separate s and t folders; also change the client animation-mode timing test.same
musictimings1: wait minmusictime + random % (maxmusictime - minmusictime) between music tracks.fixed: with maxmusictime not above minmusictime, the pause is minmusictime (the original divides by zero at the first track change, or waits far too long)
newzonescpecificmusic1: zone gz12d2 uses music named gz12d2. 2: use the alternative music-name field in zones, briefings and camp and shorten music-mode setup. 3: as 2, but retain camp’s original field.same
disabledragon1: disable the quest-variable dragon response and the related flying-insect effect updates.same
noruinszoneanimation1: omit zone1’s built-in introduction script.same
dontmergegipathzones1: keep Gipat zones separate on the global map.same
newallodsacks1: add environment flags for zones named jigran, neloya, eyn, bebesh, kvatokh, nokks, umoir, yazes, yiul.same
newcounters1: narrow the regeneration counter to one byte.same
nogameoveronleadersdeath1: the leader’s death does not end the game; revival must be supplied by scripts.same
hiddenzonesexits1: subtract player 0’s Exits from exit count. 2+: replace exit count with Exits; absent/zero means no exits.same
segmentsonexitenter1: cap zone-transition segments at maxsegments, add addsegments, and use exitenteractivityradius for activity radius.same
bigmapspatch1: change network position scaling for larger maps; all peers need compatible encoding.same
ultrafixes1: skip network objects without the companion needed to send them.same
expirementalfxes1: change unit sorting. 2: mask loaded IDs to 16 bits and fix load-loop initialization. 3: combine 1/2. 4: mask IDs, change sorting, bypass the first load-section count check. 5: change sorting and zero load-loop initialization. 6: bypass three load-section checks. 7: bound load-loop iterations. 8: combine 1/7. 9: no change. 10: bound the loop, change unit sorting, add null guards in hostility/sight, and change drawing/removal paths.same
importantalerts1+: show the starter’s configured mod path in a modal box; without it, the fallback box has no useful path.same
versionalerts1: warn about unknown lowercase .asi files in the game folder. Known names: EI_Plugin.asi, mp3dec.asi, SpellAddonX.asi, SpellAddonXa.asi. Does not warn about the built-in SpellAddon.does not warn about the built-in SpellAddon itself

Script interfaces

These options change existing commands/functions, including their argument lists. Update the mod’s scripts together with its INI. Defaults are 0. Except where shown otherwise, the active value is 1 and the section is [gameplay]. Numbers used as integer indices, styles or IDs are rounded with the game’s floating-point rounding mode.

KeyInterface and effecteix
[speed] gsgetvarGSGetVar(player, name) returns difficulty for names beginning Diff, and update period when characters 4-7 (zero-based) are peed, such as SetSpeed.fixed: reads the variable’s name before freeing it
[speed] gssetvarGSSetVar(player, name, value) also sets update period for names with peed at characters 4-7. The named variable is still stored normally.same
[saves] savecontrol1, 2: player 0’s Save!=0 blocks escape-menu and keyboard saves. GSSetVar(0, "Save", 0) allows them.fixed: reads player 0’s “Save” without corrupting the stack
autorunmobfile1+: append loading of Maps/AutoRunSpellAddon123.mob to each zone’s start script. newspells=1/2 also enables this.skipped with a warning when newspells is 1 or 2 (the original writes the same change twice)
infiniteaddmobspawn1+: AddMob can load the same file repeatedly, except case-sensitive names beginning Auto, which still load once.same
standtransStand(unit) also revives it. A non-unit argument causes no revival.fixed: does nothing for an argument that is not a unit (the original crashes)
waslootedtrans1: WasLooted(unit) returns the full flag word. 2: WasLooted(unit, bit) returns the selected bit. Known bits: 15 moving, 19 knocked out, 20 looted, 24 stunned, 28 feebleminded.same
castscripttrans1: Cast(unit, x, y, spell) uses the numbered spell. 2: Cast(unit, x, y, spell, target) casts on target if present, otherwise at the point.fixed: 2 reads its target from the running command (the original: a stale value)
castspellunittransCastSpellUnit(spell, x, y, target, caster) adds a caster unit to the named-spell command.same
castspellpointtransCastSpellPoint(spell, x, y, x2, y2, caster) adds a caster unit to the named-spell command.same
giveunitspelltransGiveUnitSpell creates/adds a quick item instead of a spell, using the existing unit/name arguments.same
hptransHP(unit, index) reads a float by index from the unit’s vitals companion; valid indices 0..29. The useful fields are listed below.same
mptransMana(unit, index) reads a float from unit stats, indices 0..448; invalid objects/controllers/indices return 0. Also adds the index argument to HP, without changing its reader unless hptrans=1.same
legendaryspellsystemMaxHP(unit, index) reads a byte starting at the skill-byte area, indices 0..631; outside it returns 0. GiveQuestItem(unit, value) writes byte 10 of that area. Also changes MaxMana to seven numeric arguments, but supplies no working path-query implementation.fixed: MaxHP stays inside the unit (the original reads any address)
getplayertransGetPlayer(unit, index) reads a 32-bit integer at four times the index within the unit (0..310); returns 0 outside it.fixed: returns 0 outside the unit (the original reads any address)
setplayertransSetPlayer(unit, value, field) writes a rounded 32-bit integer at four times field within the unit (0..310). Fields 132..148 retain the game’s normal player-setting path; out-of-unit writes are ignored.fixed: stores nothing outside the unit (the original writes any address)
groupseetransGroupSee(group, mode) accepts an extra numeric mode argument; group traversal itself remains the game’s.same
unitseetransUnitSee(unit, mode) selects the visibility list: positive zero = visible living units; negative sign bit (including negative zero) = the second list; other values = the nearby-units list.same
getaiclasstransGetAIClass(unit) reads the server controller’s AI-class field.same
runtransRun(unit, style) sets the requested movement style instead of always selecting run.same
crawltransCrawl(unit, slot) requests a weapon slot instead of setting crawl style.same
killunittransKillUnit(type, strength) sets weather with two rounded numbers instead of killing a unit.same
fixworldtimetransFixWorldTime(time, speed) adds an explicit time-speed argument.same
movetoobjecttrans1: MoveToObject(unit, target) performs thieving while walking. 2: run instead. 3+: MoveToObject(unit, target, skill, unused, run, slot) supplies four numbers for the thieving order.same
playmusictrans1, 2: PlayMusic(player, "0") or "1" re-selects the local map’s music theme and sets the music system’s level to the digit. The first character selects this behavior. 1 uses the map’s theme, 2 its alternative name field. Other text, or no map/music system, uses normal handling.fixed: without a map or a music system, does what the game does (the original crashes)
notifydeaths1: a death sets player 0’s variable named by the victim ID to 1. 2: set 2 for a hero killer, otherwise 1.same
notifylootings1: increase the corpse-ID variable by 20 for a hero looter, 10 otherwise. 2: a hero looter’s ID goes into the corpse-ID variable. 3: the corpse ID goes into the hero looter’s ID variable. Variables belong to player 0.same
genocideincvars1, 3: player 0’s Hits counts hero hits on non-heroes. 2 is skipped; 1/3 are skipped with genocidedipvars set.2 skipped, and 1 or 3 with genocidedipvars=2 (reads garbage as objects); 1 or 3 skipped with a warning when genocidedipvars is 1, 3, 4 or 5, which replaces its change (the original writes it unused)
genocidedipvars1: Hits holds the last non-hero victim’s side when hit by a hero. 2: Kils holds that side on a hero kill. 3: both. 4: Hits records any ownerless victim’s side; Kils records ownerless victims killed by owned units. 5: both require an owned attacker and ownerless victim. Replaces genocideincvars.same
removefrozenvarZone 6 ignores its frozen/unique-zone restriction on entry.same
freemultiplayervarsSkip the built-in multiplayer variables in trader creation and journal refresh.same
setspellaggressiontransNo change for any value; the command itself remains inactive.same
[tech] scriptchecking1-7: append script errors to SAlog.txt. 2, 4-7 also log mob loads. 1/2 do not record errors in the script. 3/4/6/7 return nonzero from error handling. 5 permits eight script-load errors to pass. 8+ are skipped.8 and up skipped (calls code the original never wrote); 1 and 2 write the line where the original crashes at the first script error
[tech] drawunitsidsPrefix the unit-info name with its ID; truncate to 255 bytes. Safe with no selected unit.fixed: works without a unit (the original crashes); lines cut at 255 bytes

Loot and death notifications and genocide take IDs of at least one billion as heroes and lower IDs as non-heroes (the original’s death and genocide tests take IDs strictly above one billion).

With newspells=1..3, spell types 44-48 publish S0e (strength), S1d (duration), S2t (type), S3x/S4y (point) on the first server player; scripts can read them with GSGetVar. They give the caster hostile mask 29 and side round(strength * 32). Mode 1 also gives spell 49’s caster mask/side 29. These hooks expose script inputs; they do not supply a summoning script or mod assets.

Indexed stat readers

HP indices: 10 current HP, 11 maximum HP; 13 current mana, 14 maximum mana, 15 mana regeneration. Resistance uses base/current pairs: 16/17 piercing, 18/19 slashing, 20/21 crushing, 22/23 fire, 24/25 acid, 26/27 lightning, 28/29 general.

Mana reads the underlying float fields. Do not treat packed flags or skill bytes as ordinary numeric stats.

IndicesMana(unit, index) field
1, 2Total and spent experience.
3/4, 5/6, 7/8Natural/total strength, dexterity, intelligence.
9/10, 11/12Natural/total movement and action coefficients.
13-16Run, walk, crawl, lie speeds.
17Vision arc.
20/21Current/maximum load.
28-32Natural reach, attack, attack spread, defence, speed.
33/34/35Natural damage, damage spread, attack interval.
36-42Natural damage weights, in damage-type order 0..6.
44/45Base/current sight range.
56/57, 62/63Base/current visibility and loudness.
125/126/127/128Head HP/max HP/HP share/damage scale.
186/187/188/189Body HP/max HP/HP share/damage scale.
247/248/249/250, 308/309/310/311Left/right arm HP/max HP/HP share/damage scale.
369/370/371/372, 430/431/432/433Left/right leg HP/max HP/HP share/damage scale.
71 + 61*p + 18*l + tArmour absorption: part p = 0..5, layer l = 0..2, damage type t = 0..6.
447, 448Fixed maximum HP and pending experience.

Parameters

Each row gives the default and the feature that uses it. F = floating-point; I = 32-bit integer; B = byte. Unless stated otherwise, these are [gameplay] keys. Bounds and spell-ID lists below describe meaningful values; values outside them can select unrelated fields or unsupported game data.

Progression, speed and presentation

SectionKey(s)Default(s)TypeUse
skillsskills0Bextendedskills: non-stealing skill cap.
stealingstealing0Bextendedstealing: stealing cap.
rebalanceenginepow, mul, add1.1, 4.5, 0.5Frewriteskillscounting: exponential base, multiplier, addition.
rebalanceenginemullvla, addlvla, divlvla, addlvlb4.5, 1.5, 245.5, 0.5Frewriteherolevelcounting: level formula.
rebalanceenginerunconst, runconstex0.006666666666, 0.5Frewriterunabilitytime/newrunsystem: drain coefficient and extra drain.
rebalanceenginebsbase300Ibackstabvisualbase: displayed percent.
speednormal, fast55, 27Irewritespeed: clock periods.
speednormalkey, fastkey55, 27Irewritespeedkey: keyboard periods; the game’s own are 55 and 27 (the original’s built-in normalkey is 5).
speednormal155Irewriteotherspeed 1/2: close/reset period.
speednormal2, fast255, 27Brewriteotherspeed 1/3: initial clock periods.
speedpause, pausekey0, 0Idisablepause/disablepausekey: replacement periods.
animationsspeedattackspeed, castspeed, hitspeed, deathspeed, specialspeed, idlespeedAll 1Frewriteactionsspeed: attack, cast, hit reaction, death, special, idle animation factors.
gameplayexpforint1.0245Fchangeexpforint: experience factor per intelligence point.
gameplayactionsmul, actionsadd0.2, 10.0Factionspatch: dexterity multiplier and action addition.
gameplayactmovemul, dexmovemul0.5, 0.01Factionsdexteritymovespeed: actions/dexterity contributions.
gameplayspeedbase, speedadd0.3, 5.0Fspeedboost: exponent and counter addition.
gameplayparammin, parammax, balance15.0, 35.0, 75.0Fextendedparams=2: attribute bounds and displayed balance; sum enforcement is bypassed.
gameplayperssize1.2Frewriteperssize: hero preview scale.
gameplayframex, framey75.0, 175.0Fallodsinfo: frame width/height; integer parts also size text rectangles.
gameplaymeter15.0Fmeterscale: ruler factor.
gameplaynewlightsvalue0.001Flightsboost: light strength.
gameplayexred, exgreen, exblueAll 0.5Fexploredcolors: RGB components; normally 0..1.
fontsnewfont1, newfont2, newfont3, newfont4All Times New RomanTextchangefont: four interface font families.
fontsnewfont5Times New RomanTextchangefont=2: font-file name relative to the INI folder, not a family name.

Combat, shops and load

Key(s)Default(s)TypeUse
divmod, submod100, 1Fnewattackdefence: divisor/base for modes 1/2; multiplier/floor for 3/4.
str, dex, int100.0012, 100.0013, 100.0014Fdamageboost: strength/dexterity/intelligence scaling; divisors in mode 1, multipliers in 2/4/5, unused in 3.
strhp, strdmg, strweight0.005, 0.003, 12.0Fstrengthstats: strength coefficients.
weight, range20.0, 10.0Fchangeweightrangebonuses: weapon-weight and distance constants.
bodypartbreakmodifier4Funbreakablebodyparts: replace the limb-damage floor constant (normally -5).
headmod, legmod, handmod3, 1, 1Frandomlessaimedhits: head/leg/arm score weights.
criticalmod2Frandomlessaimedhits: boost a head score reaching full head HP, and leg scores at ranged reach.
healthdowntoignore10.25Frandomlessaimedhits: ignore non-body parts when current HP times this value is below maximum HP.
rangeddistance5.65Frandomlessaimedhits: reach threshold for boosting leg scores.
bodypart1Irandomlessaimedhits: fallback part; 0 head, 1 body, 2/3 arms, 4/5 legs.
checkhero1Irandomlessaimedhits 5-9: nonzero makes player-owned attackers use bodypart instead of the chooser.
tohitcheck1Bincreasinghitchance: miss count must exceed this.
tohitsub, tohitmul0.0, 5.5Fincreasinghitchance: subtraction and counter multiplier.
hitdistmul, hitdistadd2.0, 5.0Frunawaydistcontrol: reach multiplier/addition.
nightmeleeadd, nightmeleemul1.0, 1.0Fnightpower=2: night-factor base and slope.
maxcountofmerc27Iextendedmerc: limit uses the low byte; full value controls the >50 draw-range condition (with extendedmerc 16+ and saveall=32).
minsweapons, maxsweapons1, 10Bshopsnewnormalchanges: weapon stock range.
minsarmors, maxsarmors1, 10Bshopsnewnormalchanges: armour stock range.
minswands, maxswands1, 10Bshopsnewnormalchanges: wand stock range.
minspotions, maxspotions20, 65Bshopsnewnormalchanges: potion stock range.
minsprototypes, maxsprototypes1, 10Bshopsnewnormalchanges: prototype stock range.
minsrunes, maxsrunes20, 56Bshopsnewnormalchanges: rune stock range. Use stock counts 0..127 because the game sign-extends these bytes.
ingos, gipatz, gipatb, suslanger, jigranAll 0.5Fshopsnewnormalchanges: sell-price factors for Ingos, Gipat healer, Babur, Suslanger and Jigran. shopschanges also reads jigran when shopsnewnormalchanges=1.
liftkey1.0Fliftdebuffs: load-ratio divisor term.
encumbrancemovemul, encumbranceattackmul1.0, 1.0Fencumbrancemovespeed, encumbranceattackspeed: load penalties for movement and attack.
speedslowmovemul, defaultspeedmul, minimumspeedmulAll 1.0Fencumbrancemovespeed with speedslowremaster=2: effect multiplier, overall multiplier, minimum factor.
dmgtypescount6Idmgtypes: displayed count; the game has seven damage types (0..6).
light00 through light31All 1Fitemslightchanges: eight RGBA groups in order; 00-03 first colour, 04-07 second, through 28-31 eighth. Normally 0..1.

AI, spells and zones

Key(s)Default(s)TypeUse
visionarc-2Frewritenpcvisionarc: NPC sight-arc value.
visiondependence1.05Faggrmod 2/4/5/6/7: NPC vision-distance factor.
aggrrangemulmodifier4Faggrmod=1 stores it but does not use it.
losehardcore100000Iaggrmod=2: random modulus. 6/7: counter limit uses only 16 bits (default becomes 34464).
losefactor1, losefactor215, 5Iaggrmod 4/5: time divisor multiplier and right-shift count. 6/7: distance multiplier and level-divisor addition.
losefactor3, difffactor, maxadd5, 2, 2Iaggrmod 6/7: distance addition, difficulty addition, quotient threshold. These and losefactor1/2 use signed low bytes in 6/7.
follow0INonzero lets units continue towards a lost enemy’s position in active aggrmod modes.
improve0INonzero requests the faulty alarm hook in aggrmod 6/7; eix omits it and warns.
loseallyfast0INonzero in aggrmod 6/7 bypasses the counter for units not classed as enemies.
sametarget, magicattack, damagemult, magicdamagemult, savedamagemult, rangecost, kickpriceAll 10.0Fspellsprioritys: same-target bias, magic bias, weapon/spell/healing priorities, casting-distance cost, quick-kill target factor.
safetymuldist, maxvisibilitymul2.0, 3.0Fnewplayersafetyconditions: safety distance and visibility factors.
fearsafetydist, legsdamagedmul10.0, 0.6Ffearpatch: retreat distance and damaged-leg threshold.
runtosoundsmul, randsoundfactor1.0, 1.0Fhearingreactionspatch=2: extrapolation from sound position and random scatter factor.
visionweathermul0.9Fvisioninweather: bad-weather vision factor.
visionevening, visionnight, visionmorning, visionday18.0, 22.0, 4.0, 8.0Fnightpowervisionpatch: hours for dusk/full night/dawn/full day.
visionmulpowbetween0.250Fnightpowervisionpatch: night-level ramp slope per hour.
astralbeam, destroyray, firearrow, lightningbeam5, 3, 3, 5Blastspellsdamagetype: 0 piercing, 1 slashing, 2 crushing, 3 fire, 4 acid, 5 lightning, 6 general.
slownessmultiplier, weaknessmultiplier12, 0.5Fcontrolmagic=1: resistance scaling for Slow and Weaken.
durcoeff, effcoeff, lvldurcoeffAll 0.03FRead but no active duration/effect formula consumes them; lvldurcoeff is stored by some controlmagic modes.
mul3, pow31.0, 1.0FRead but unused by the built-in features.
frequencyvalue0Bwallsfrequency: ticks per damage event; set a meaningful positive period.
firepercentagevalue, lightningpercentagevalue, acidpercentagevalueAll 0.1Fwallspercentage: fire, lightning and acid wall fractions.
stenchmul, regenerationmul, speedslowmul, strengthweakmulAll 0.1FRespective Stench, Regeneration, Speed/Slow and Strengthen/Weaken remasters.
tordmg5.0Ftornadochanges: damage per tornado hit.
fireboom5.0Fchangesounds: Fireball volume factor.
minmusictime, maxmusictime120000, 180000Imusictimings: minimum and exclusive upper delay in milliseconds.
maxsegments, addsegments1000, 0Isegmentsonexitenter: segment cap and addition.
exitenteractivityradius70.0Fsegmentsonexitenter: transition activity radius.

Per-spell bonuses - [gameplay]

Each switch defaults to 0 (B); each bonus defaults to 0.0 (F). controlmagic=3 applies the bonus only when the switch is odd. Modes 2/5/7 apply all bonuses. effectredraws=1 displays all bonuses regardless of these switches.

SpellSwitchBonus
Protection from Fireprot_firebprot_fire
Protection from Lightningprot_electrobprot_electro
Protection from Acidprot_acidbprot_acid
Eagle Sighteagle_sightbeagle_sight
Night Visioninfravisionbinfravision
Detect Lifedetect_lifebdetect_life
Invisibilityinvisibilitybinvisibility
Silencesilencebsilence
Lichdomlichdomblichdom
Stenchstenchbstench
Stunstunbstun
Antimagicantimagicbantimagic
Strengthenstrengthbstrength
Weakenweakbweak
Regenerationregenerationbregeneration
Feeblemindfeeblemindbfeeblemind
Speedspeedbspeed
Slowslowbslow
Enlargeenlargebenlarge
Shrinkshrinkbshrink
Necronecrobnecro
Destroydestroybdestroy

Spell AI table entries

SectionKeysDefaultAccepted meaning
aicastertable0 through 53All 8B; 0 missile, 1 area attack, 2 lasting area attack, 3 out-of-combat buff, 4 combat buff, 5 unused, 6 healing, 7 debuff, 8 unused. Used with rebuildedaicastertable=1; with newbigtables=1, key n no longer classifies spell type n.
aioffendtable0 through 37All 8B; 0 harmful, 1 friendly. Set meaningful entries before enabling rebuildedaioffendtable=1; the default 8 is not a normal classification.

Read-only placeholders - [gameplay]

These switches default to 0 and have no feature of their own. Changing them has no effect in the built-in version.

Key(s)Status
enemysmanaRead only; use npcsmana for the implemented mana rules.
newspells_fireraze, newspells_inspircharm, newspells_spellsmachine, newspells_invislichdRead only; newspells controls the implemented spell extensions.
hideobjecttrans, stableaggrmod, helloworld, castspellpatchRead only; no command/AI change.
newallodsinmulti, autocastsRead only; no allod-selection or automatic-casting feature.
walktrans, lietrans, resttrans, getleadertrans, inflictdamagetrans, isunitblockedtrans, setplayeraggressiontransRead only; the named script interfaces retain their existing behavior.

Fixes and features

eix’s own changes to game.exe RU 1.06, beside the originals’ features: fixes of defects that the original addon.dll and SpellAddon leave, and features. Each is set in eix.ini.

Fixes

Code: crates/addon/src/fixes/, one module per fix, applied in table order after the patch groups.

  • A fix changes nothing a player sees, except the mod setting below. The changes a player sees are game options, switched in the same section.
  • No fix writes a place a SpellAddon feature patches, or changes what a multiplayer peer computes.
  • addon.log lists each fix under fixes: applied, failed with the reason, or off. With Enabled 0 one line says no fix applied, and only the mod setting is listed.

The switches are in eix.ini’s [EngineFixes] section.

A mod’s config.reg can set [MOD] ShowTutorialDefault: the “Show tutorial” option’s value while the game has none stored, as on a first start. The game stores the option at exit, so the player’s later choice wins. Without the key the game’s default (on) stands. Enabled 0 does not turn it off.

FixBeforeAfter
HeapPaddingheap overruns crash under Wine (lobby, shops, looting)blocks padded (about 14 % more heap)
TimerResolutionframe times snap to 15.6 ms on Windows 10 2004+1 ms timer
CastTargetGonea cast whose target leaves the world crashes the serverthe order ends
KillScriptOutsideKillScript outside a script writes through null (AntiCrash on)reported, game goes on
LowercaseTableslow per-character lower-case conversion in parsing and lookupscached; zone loads about 30 % faster
ViewDepthInituninitialised depth can crash the draw sort on a first zone loaddepth starts at 0
MappedFileMessagea failed-open message over 210 bytes overflows its bufferpath cut at 210
PresentFailurea screen lock or UAC prompt in full screen ends the gameflip waits, frame skipped
FullScreenFallbacka refused full-screen mode crashesnext larger mode, game in its top left corner
ScreenshotRowPaddingscreenshots at some widths read past their bufferbuffer sized right
BriefingTexta guest joining during a briefing crashes the hostdialogue goes on
UnitPanelTextthe unit panel stalls the frame for its textdrawn in memory, copied on change
StreamWaita music change spins the main threadthe wait sleeps
QuestSortquests without a #quest entry crash or hang the journalsorted first
ExitIndexa zone with unloaded exits crashes the briefing screenempty exit, warning
FatalErrorExita fatal error with AntiCrash off crashes on shutdownclean exit, same code
UnknownScreenan unknown game state from the host crashes the clientlogged, ignored
DiplomacySidesa side outside 0..31 reads and writes past the tableshostile, unchanged, warning
MissingPlayerModea party member with cleared modes crashes the party checkskipped
NoAudioDeviceno audio device: movies crashruns silent, warning
EmptyMusicThemean empty theme track list divides by zeroplays nothing, warning
ZoneModeText% in a theme name read as a formatprinted as text
StreamFadeLocka fade races the stream threadtakes its lock
PointLightOverflowthe 257th script light overruns the tablerefused, warning
EmptyItemStripa missing constructor record crashes a camp or shop stripempty list, warning
UnregisteredLighta light removed twice or moved before added corrupts memoryignored, warning
FindHandleLeakeach music track leaks a file search handleclosed
LoadFailureLeaksa failed read and two failed texture loads leakfreed
TruncatedScripta script ending mid-statement crashes or hangs the game“Unexpected end of file”
VertexLighting3DfpFPU.dll lights vertices in slow x87 codeSSE, four at a time, same colours bit for bit, also with Universal Mod’s um-engine.dll

Features

AutoPause and encumbrance are off by default, FLAC music and INI variables on; the game options each have their own default.

Game options

Window, display and start-up changes. Each has its own key in eix.ini’s [EngineFixes]; UnitShadowResolution, KeepRunningOnFocusLoss and ClipCursorToWindow are off by default, the others on. The focus options change the window only, never full screen. Enabled 0 there turns all of them off. addon.log lists each under fixes.

OptionBeforeAfter
KeepWindowOnFocusLossin a window, the game minimises on focus lossstays on screen, paused
KeepRunningOnFocusLossin a window, the game pauses (tick, sound, input) and minimises on focus loss; a multiplayer client falls out of stepkeeps running and playing, not minimised
ClipCursorToWindowin a window, the cursor leaves itconfined to the client area while the window has the focus; let go on focus loss, minimising and closing
UnitShadowResolutionunit shadows are 64 by 64 pixels128 or 256: a sharper edge, more video memory and work per shadowed unit; rendering only
SmoothCamerapanning stutters: a tick’s camera step lands in one framespread over frames, same speed
PlayerListOnJoina client may see an empty player list until players changerefreshed when its player arrives
SkipDiscCheckthe starter’s game.exe asks for the game discstarts without it, as GOG’s exe
UnitPanelSpellsthe unit panel’s values page shows no spells of a unit outside the partyeach spell below the values: its icon and “Effect:” with its value, as the spell windows show it; a page taller than the panel gets the game’s scroll bar (arrows, thumb, and the mouse wheel over the page instead of the camera’s zoom); single player and the host only
SpellsNeedAcceptedSkillsthe camp’s spell constructor counts skills bought on the skills page before the purchase is accepted, so undoing the purchase keeps the spells made with themcounts each hero’s skills as last accepted

KeepRunningOnFocusLoss covers KeepWindowOnFocusLoss in a window: with both on, the game never sees the focus loss, so nothing pauses and nothing minimises. With KeepRunningOnFocusLoss 0 the second still keeps the window on the screen, paused. Minimising the window yourself stays the game’s: it pauses, and resumes when the window is restored.

AutoPause

In single player, the game pauses when a party member’s health drops low, an enemy comes into view, an enemy turns on the party, or an enemy in view dies; each event has its own setting. A network game never pauses. addon.log names what paused the game.

  • Health: a party member’s health drops below Health or HealthPercent; with both set, either limit pauses the game. It pauses once per drop: the member’s health must rise to the limit again before the next drop pauses the game. A member that is already low when it comes into view (a zone loads, a new member joins) does not pause the game.
  • An enemy is a living unit without a player that is hostile to the party’s side. The party’s view is what its members see, as the script PlayerSee gives it.
  • EnemySighted and EnemyAggro pause once per encounter: the next pause comes after no enemy is left in view, or aggressive, and one appears again. Enemies already in view, or aggressive, when a game loads pause it at once.

Encumbrance

The load a hero carries slows running, and makes running cost more stamina; above the hero’s maximum load it slows walking too. Load and maximum load are the encumbrance figures of the hero panel; their ratio is the encumbrance below (1.0 at the maximum). The rates are settings.

  • Running speed is 100% - RunSlope x encumbrance: with the defaults 80% at the maximum load, 60% at twice it.
  • Walking, sneaking and crawling keep the game’s speed up to the maximum load, then lose WalkSlope per extra maximum load: 80% at twice the maximum.
  • Turning while walking or standing slows with the running speed: the game turns a walking or standing unit at its running pace.
  • Running drains the stamina the game’s run system takes, divided by the load’s running speed: at 80%, 1.25 times as much. Wounded legs slow the hero without raising the drain. RunDrain scales the extra: 0 keeps the game’s drain, 50 halves the extra.
  • No load stops a hero from running; wounded legs still slow every movement and stop running, as in the game.
  • The speeds follow the load when the game recomputes a hero’s statistics (regeneration, damage, healing, effects), not at the moment an item is equipped.
  • With SpellAddon’s encumbrancemovespeed also on, both slow movement; addon.log warns. SpellAddon’s heroalwayscanrun changes the same byte as this feature. Its run settings (newrunsystem, runconst) decide the drain that RunDrain scales.

FLAC music

eix plays a music or streamed-sound track from Stream\<name>.flac when there is one, in place of <name>.mp3: the mod’s Stream folder first, then the game’s. The order is the mod’s .flac, the mod’s .mp3, the game’s .flac, the game’s .mp3.

  • The track keeps its .mp3 name in the game: music.reg names and streamsn.reg sections (<name>.mp3) stay.
  • Mono or stereo, any sample rate, up to 32 bits a sample (played as 16 bits). Packs can mix FLAC and MP3 tracks.
  • addon.log names each FLAC file played. A file that does not decode plays silence, with a warning.
  • Briefing speech (speech.res) stays MP3.
  • On by default; eix.ini’s [Music] Flac=0 turns it off.

INI variables

A script’s GSGetVar(0, "ini.<file>.<section>.<key>") reads key in [section] of <file>.ini as a number, so a mod’s map scripts can take settings from an INI file. Mod settings for map scripts shows the use.

  • The file comes from the mod’s folder (Mods\<mod>), else the game folder. <file> is a plain file name: a name with \, / or : reads the game’s variable.
  • Each file is read once, at the first variable that names it: a change applies at the next game start.
  • A missing file, section or key, or a value that is not a number, reads 0; addon.log notes each such name once. The decimal point is . or ,; names ignore case. The key is the rest of the name, dots included.
  • Nothing is stored: saves hold no INI values, and multiplayer clients are sent none. Each machine reads its own file.
  • Without eix the game reads 0 for such a name, as for any variable never set.
  • Always on; other variable names keep the game’s own lookup.

eix.ini

eix’s own settings, in eix.ini in the mod’s folder (Mods\<mod>, beside its config.reg). Without the file, or with no mod selected, every key takes its default. The package’s copy holds every key at its default.

addon.log names the file read, and each value left out with the reason.

EngineFixes

The switches of the fixes and the game options:

KeyValue
Enabled0: all fixes off; else on (default 1)
KeepWindowOnFocusLoss, SmoothCamera, PlayerListOnJoin, SkipDiscCheck, UnitPanelSpells, SpellsNeedAcceptedSkills0: the game’s behaviour for that option; else on (default 1)
UnitShadowResolution64 (default): the game’s; 128 or 256: sharper unit shadows; another value is logged and stays 64
KeepRunningOnFocusLoss, ClipCursorToWindow0 (default): off; else on

AutoPause

The events that pause single player (AutoPause), each off by default:

KeyValue
HealthHit points; 0 (default): off
HealthPercentA percentage of the member’s maximum hit points, 1 to 100; 0 (default): off
EnemySighted1: an enemy comes into the party’s view while none was in view
EnemyAggro1: an enemy turns on a party member while no enemy was aggressive to the party
EnemyDead1: an enemy in the party’s view dies, whoever killed it

A negative Health, or a HealthPercent above 100, is logged and is off.

[AutoPause]
Health=0
HealthPercent=25
EnemySighted=1
EnemyAggro=0
EnemyDead=1

Encumbrance

The rates of encumbrance, off by default. Encumbrance is load over maximum load (1.0 at the maximum).

KeyValue
Enabled1: on; 0 (default): the game’s movement
RunSlopeRunning speed lost per maximum load, in percent, 0 to 100 (default 20)
WalkSlopeWalking speed lost per maximum load above the maximum, in percent, 0 to 100 (default 20)
MinSpeedThe lowest speed either reaches, in percent, 10 to 100 (default 50)
RunDrainHow much of the extra running drain applies, in percent, 0 to 200 (default 100)

A value out of its range is logged and takes its default.

[Encumbrance]
Enabled=1
RunSlope=20
WalkSlope=20
MinSpeed=50
RunDrain=100

Music

KeyValue
Flac1 (default): a track plays from its .flac when there is one (FLAC music); 0: only the game’s own stream code runs

Development

Build

Install podman, then:

cargo xtask container
  • Runs cargo xtask check in the image from build/Containerfile: format, clippy (host and Windows), tests (Windows ones under Wine), release build, docs, dependency checks, workflow checks, and the public-text check (cargo xtask public).
  • It also checks the generated reference pages (see Documentation site) and builds the site.
  • Output: target/container/i686-pc-windows-gnu/release/{dinput,addon}.dll.
  • Release package: cargo xtask container dist writes target/container/dist/eix-<version>.zip (the DLLs, dist/spelladdon.ini, dist/eix.ini, the README, the licence, the changelog, the third-party notices and docs/) and the release notes.
  • Without podman: the toolchain from rust-toolchain.toml, i686-w64-mingw32-gcc on PATH, Wine, and the tools build/Containerfile installs, then cargo xtask check.
  • Tests that need the game’s files are ignored by the gate. Run them with EIX_GAME_DIR set to the game folder as a Windows path (under Wine, Z:\...): cargo test -p eix-addon --target i686-pc-windows-gnu -- --ignored. They check every expected byte of the fixes and SpellAddon’s features against game.exe, and the vertex lighting against 3dfpfpu.dll.

Documentation site

The site is an mdBook book: book.toml at the repository root, docs/ as its source, docs/SUMMARY.md as its table of contents. Every page of docs/ is listed there.

Two pages are generated, and each has one data file that holds every signature and description:

PageData file
docs/scripting.mddocs/scripting.yml: the game’s script functions and commands, with their source and eix’s differences
docs/lua-reference.mddocs/eix.lua: the Lua API as LuaLS annotations; it ships as it is, for the Lua language server
  • Edit the data file, then run cargo xtask docs and commit both. Never edit a generated page: cargo xtask check fails when a page differs from a fresh generation.
  • cargo xtask check also fails on a broken link between pages and on a page missing from docs/SUMMARY.md, then builds the book into target/book. The build image carries mdBook, pinned in build/install-mdbook.sh.
  • Tests compare the data files with the code: every function and callback that addon.dll registers appears in docs/scripting.yml or docs/eix.lua with the same name and signature. Run the test that compares the game’s own entries with the keyword table of game.exe with EIX_GAME_DIR set, as for the other game tests: cargo test -p eix-docs -- --ignored.
  • Build the book by hand with mdbook build and open target/book/index.html.
  • Publishing: .github/workflows/pages.yml builds the book and deploys it to GitHub Pages on every push to main. The repository’s Pages source must be set to GitHub Actions once (Settings, Pages, Build and deployment).

Continuous integration

  • .github/workflows/ci.yml: the gate (cargo xtask container) on every push to main and every pull request. The build image is cached under a hash of build/ and rust-toolchain.toml, and stored by pushes to main only; EIX_IMAGE_KEY makes xtask use a loaded image carrying that key instead of building one.
  • .github/workflows/pages.yml: the documentation site, as above.
  • .github/workflows/release.yml: on a v* tag, the gate, the package, and a draft GitHub release.

Release

  1. Set the version in Cargo.toml and add its section to CHANGELOG.md.
  2. Push a tag v<version>. The release workflow runs the gate, builds the package, and drafts a GitHub release with it and the changelog section.
  3. Check the draft and publish it.

Keep each release’s package: an address inside addon.dll in a crash record is read against that exact file, which keeps its symbols.

Layout

PathContent
crates/coreplatform-free logic: registry format, options, group conditions, spelladdon.ini keys
crates/docsthe reference pages generated from docs/scripting.yml and docs/eix.lua, and the checks of those files
crates/sitesgame.exe addresses, one named constant each, per exe build
crates/enginegame.exe access: memory, hooks, typed game structures, Win32
crates/luathe Lua host (LuaJIT)
crates/spelladdonthe built-in SpellAddon
crates/loaderdinput.dll
crates/addonaddon.dll: patch groups, command line, engine fixes
xtaskproject tasks
buildthe build image and the installers of its tools
distthe release package’s spelladdon.ini and licence-notice settings