Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Game systemsSpielsysteme

Kiscule — The Script Layer of Wolfenstein II

SolvedGelöst Original page in the GitHub wikiOriginalseite im GitHub-Wiki

In plain wordsIn einfachen Worten

Kiscule is the mission scripting system of Wolfenstein II (TNC): triggers, enemy waves, doors and audio are built as node graphs, like boxes connected by wires. The researchers decoded the file format completely and can read and write all 2,408 scripts back byte-for-byte identical. Since WolfSDK 0.2.0 the Studio has a script and mission editor built on this, which saves edits as mods. Whether the game runs edited or added nodes is not yet confirmed in the game.

  • 2,408 scripts with 70,274 nodes in 205 node types; every byte is explained.
  • Round-trip: 2,408 of 2,408 scripts written back identical, including compression.
  • All 205 node types match classes in the game program; 113 more exist but are never used.
  • 30 of 2,408 original scripts contain small defects, such as 43 connections that point nowhere.
  • All 2,443 map references to scripts resolve.
  • The Studio editor shows 3,010 scripts, can change parameters and add or remove nodes and connections, and passes 98 of 98 offline checks; not yet confirmed in game.

Kiscule ist das Missions-Skriptsystem von Wolfenstein II (TNC): Auslöser, Gegnerwellen, Türen und Audio sind als Knotengraphen gebaut, wie Kästchen, die mit Drähten verbunden sind. Die Forscher haben das Dateiformat vollständig entschlüsselt und können alle 2.408 Skripte byte-genau lesen und zurückschreiben. Seit WolfSDK 0.2.0 hat das Studio darauf aufbauend einen Skript- und Missionseditor, der Änderungen als Mods speichert. Ob das Spiel bearbeitete oder neue Knoten ausführt, ist im Spiel noch nicht bestätigt.

  • 2.408 Skripte mit 70.274 Knoten in 205 Knotentypen; jedes Byte ist erklärt.
  • Roundtrip: 2.408 von 2.408 Skripten identisch zurückgeschrieben, inklusive Kompression.
  • Alle 205 Knotentypen entsprechen Klassen im Spielprogramm; 113 weitere existieren, werden aber nie benutzt.
  • 30 von 2.408 Original-Skripten haben kleine Fehler, etwa 43 Verbindungen ins Leere.
  • Alle 2.443 Verweise von Karten auf Skripte lassen sich auflösen.
  • Der Studio-Editor zeigt 3.010 Skripte, ändert Parameter, fügt Knoten und Verbindungen hinzu oder entfernt sie und besteht 98 von 98 Offline-Prüfungen; im Spiel noch nicht bestätigt.

Technical detailsTechnische Details

Der technische Teil ist auf Englisch – so, wie er im Wiki steht. Unterstrichene Begriffe haben eine Erklärung auf Deutsch.

Kiscule is what drives the missions in Part 2: triggers, enemy waves, intermission sequences, doors, and audio. It is the id-tech-6 equivalent to the .script files from Part 1 — but not a text interpreter, rather a node graph, as drawn by an editor.

All numbers here are measured from the unmodified Retail installation under <Steam library>\steamapps\common\Wolfenstein.II.The.New.Colossus\base\ (read-only only). Reproducible with:

python tools/kiscule.py --dump        # Unpack 2408 scripts into cache
python tools/kiscule.py --roundtrip   # Write back bit-exactly, all 2408
python tools/kiscule.py --verify      # Counter-tests plus negative tests
python tools/kiscule.py --maplink     # Map <-> Script <-> Entity-Name
python tools/kiscule.py --signatures  # 205 node types with signature
python tools/kiscule.py --signature idKisculeNodeDelay
python tools/kiscule.py --graph gp_kiscule_devstarts

Underlying infrastructure: wolfsdk/idcl.py (Archive), wolfsdk/oodle.py (Kraken via game DLL), tools/extract_tnc.py (Unpack). --dump requires roughly one minute initially; thereafter everything works out of %TEMP%\kiscule-cache (13 MiB raw, 186 MiB text).


0. Executive Summary

Question Answer Evidence
Container format? 16-Byte header + Kraken frame, followed by plaintext --roundtrip: 2408/2408
What percentage explained? 100 %. No byte unexplained Parser → Emitter reproduces all 195,090,346 bytes of text and all 13,293,044 bytes of archive payload data bit-exactly
Node types? 205 used, all 205 with RTTI class in the EXE. 113 additional classes exist but do not appear in Retail docs/ghidra/rtti_tnc.json
Signatures? Fully derived from data itself — each node carries its connections and parameters with it --signatures
Graph? Edges stand at both ends, 178,516 of 178,559 ends mirrored --verify
Write back? Yes, bit-exactly — even the Kraken re-compression matches Retail --roundtrip
Map ↔ Script? 2443/2443 references resolve, 98.90% of Entity names reside in the same map --maplink

What a mod author can concretely do with this is detailed in Section 8.


1. The Container: Two Layers, Both Active

extkisclule is one of three types that appear binary in the archive but are not. Behind the archive compression lies another layer:

0x00  i64  usize   >= 0: Length after unpacking. < 0: Raw text, |usize| long
0x08  u64  csize   always len(payload) - 16
0x10  ...          Kraken frame (starts with 0x8C) or just the raw text

Measured against all 2408 unique entries: 2393 Kraken, 15 raw.

The 15 "negative" ones are not an exception. d9ffffff… = −39 simply means: 39 bytes, uncompressed. And these 39 bytes are

\n\tedit = {\r\n\t\tkiscule = {\r\n\t\t}\r\n\t}\r\n}\0

— a empty Kiscule. Kraken does not make 39 bytes smaller, so the packer stores them raw. The rule raw if compressed >= raw reproduces exactly these 15 and no others (--roundtrip, 2408/2408).

The re-compression matches Retail bit-exactly. Kraken, Level 4 (Normal), via the game's own oo2core_5_win64.dll: 2408 of 2408 frames are byte-identical to what is in the archive. This allows rebuilding an unmodified script without any deviation — the prerequisite for a desired change being the only change.

Compression ratio across all scripts: 13,293,044 → 195,090,346 bytes, so 14.7 : 1.


2. The Text: A Line Grammar, Three Cases

The unpacked content is Decl plaintext, machine-generated, and therefore strictly regular. Each line is exactly one of three, indentation is always exactly one tab per nesting depth:

<tabs>key = {          Block start
<tabs>}                Block end  (at the depth of block contents minus one)
<tabs>key = value;      Scalar

Five value types, and no others: int, float, "string", true/false, NULL. Counted across all scripts: 3,026,083 int, 1,684,238 string, 682,207 bool, 12,820 float, 10,119 NULL.

Structure: The file begins with a newline, the root block has no own { line (its content is at depth 1, the closing } at depth 0), and at the very end stands a NUL byte. Line endings are either LF (1461 files) or CRLF (947) — never mixed.

Measured: 9,243,027 lines across 2408 files, 0 unparsable.

    edit = {
        kiscule = {
            version = 6;
            nodes = {
                num = 2;
                item[0] = {
                    name = "idKisculeNodeBeginGame";
                    id = 1276314557;
                    nodePos = { x = 180; y = 220; }
                    parameters = {
                        class = "idKisculeGraphNodeParametersBeginGame";
                        object = { ignoreInitialFlag = true; }
                    }
                    connectionPoints = {
                        num = 2;
                        item[0] = {
                            name = "OnBeginGame";
                            id = 821853744;
                            connections = { num = 0; }
                            type = "OutputNotification";
                            properties = 8;
                            visible = true;
                        }
                        …

(above condensed for readability; in the file each field stands on its own line)

Four blocks at top level, in this order, in every script: version, nodes, comments, variables.

  • version is 6 in 2329 scripts and 5 in 64. A structural difference between 5 and 6 is not visible in the data: same blocks, same fields, same node types. What the version means remains open. (In the 15 empty scripts version is missing entirely.)
  • comments are editor notes: name, id, pos, size, parameters (with fillColor, borderThickness, insideText …). 12,068 pieces. Purely cosmetic, but they stand in the file and belong to the roundtrip.
  • variables are named graph variables: 829 pieces, all with fields name, global, isCrossMap, defaultValue, value. Classes: idKisculeGraphVariableBool 433, …Int 229, …EntityReference 69, …Time 50, …Float 34, …ListEntityReference 12, …EntityDef 2. isCrossMap is the interesting switch: a variable that survives across map changes.

3. The Graph

3.1 Nodes

item[i] = {
    name = "idKisculeNodeDelay";     <- the node type
    id = 1276314557;                 <- unique within the file
    nodePos = { x; y; }              <- editor layout only
    parameters = { class; object; }  <- or  parameters = NULL;
    connectionPoints = { num; item[…]; }
}

70,274 nodes in 205 types, median 19 nodes per script, maximum 532 (civilians_kiscule_soldier_approach, 1.5 MiB text). 15 scripts are empty.

parameters = NULL; occurs — nodes without their own parameter object.

3.2 Connections

item[j] = {
    name = "Trigger";
    id = 315942819;                  <- unique within the file
    connections = { num; item[…]; }
    type = "InputRegular";
    properties = 8;
    visible = true;
}

Six type values. The numbers in this section come from a sample of every fourth script (602 of 2408, 155,538 connections) — the roundtrip and graph numbers in section 7 are instead full surveys.

type Count (sample 1/4) Direction
OutputVariable 41,501 out
OutputRegular 38,439 out
InputVariable 33,097 in
InputRegular 29,448 in
OutputNotification 13,049 out
InputEvent 4 in

Regular is flow control (a pulse), Variable is data flow (a value), Notification is an event output. Every parameter of a node appears additionally as InputVariable + OutputVariable of the same name — so a value can either stand inside the node or come from outside.

properties is a bitmask. Observed values: 8, 0, 24, 25, 9, 40, 1, 13, 16. What is measured:

  • Bit 16 (0x10) is set only on list connections. Of the 65,159 tested connections with a matching parameter: 9,280 carry Bit 16 and are lists; 55,487 do not carry it and are not — not a single exception. The reverse is not entirely true: 392 list connections do not carry Bit 16. Thus, the implication holds in only one direction.
  • Bit 8 (0x8) is the standard case for outputs and for InputRegular; InputVariable rarely carries it (27,691 × properties = 0).
  • The meaning of Bits 1, 4, and 32 is undetermined. They appear in 2,583 out of 155,538 connections (values 1, 9, 13, 25, 40). properties = 40 (8|32) exists only on OutputNotification.

The visible flag toggles display in the editor (142,645 true, 12,893 false) and correlates with nothing in the execution flow.

3.3 Edges

connections = {
    num = 1;
    item[0] = {
        name = "";
        id = 672289692;
        endNodeID = 1700766265;
        endConnectionPointID = 2126630981;
    }
}

Every edge exists at both ends. Of the 178,559 connection endpoints, 178,516 have a counterpart — that is 89,279 edges.

A guarantee that does not hold and which one easily derives incorrectly: the id of an edge is the same at both ends — in 149,620 cases, but in 28,896 it is not. Anyone who pairs edges via their id builds a parser that errs on every sixth case. The defining relationship is (endNodeID, endConnectionPointID), not the id.

43 connection endpoints in 29 Retail files point to nothing — to node or connection IDs that do not exist in the file. This is an error in the game data, not a parser error: --verify lists them individually. Affected are among others c01p1_gameplay_kiscule_choice, gp_kiscule_button1/3, c11p1_light_kiscule_1_*. A mod tool must account for this.

This is compounded by a file with duplicate connection IDs: sub_gp_c02p3/kiscules/gp023_kiscule_peanutbutter_event.decl — there, an EntityReference node carries its four connections twice, each pair with the same id. Of the eight, only one hangs from an edge. Also a defect in the game data. Together this makes 30 conspicuous files out of 2,408 (1.25%); the remaining 2,378 are consistent in every respect.


4. The 205 Node Types

One does not need to guess a node type's signature: every node carries it. --signatures aggregates across all 70,274 occurrences and provides per-type the parameters (path, value type, whether always present) and the connections that appear in every instance.

$ python tools/kiscule.py --signature idKisculeNodeDelay
idKisculeNodeDelay  (4103 nodes in 1197 scripts)
  parameters.class = idKisculeNodeDelayParameters
  Parameter:
    delay                                      int                    always
    resetOnStart                               bool                   always
  Connections (17 fixed):
    out Out                              OutputRegular        properties=8
    in  Start                            InputRegular         properties=8
    out Start                            OutputRegular        properties=8
    in  Pause                            InputRegular         properties=8
    …
    in  delay                            InputVariable        properties=0
    out delay                            OutputVariable       properties=8

The twenty most frequent (columns: nodes, scripts, fixed inputs/outputs, parameter fields; * = the number of connections depends on the individual node):

Node Type Node Scripts In Out Par
EntityReference 13,850 2,202 2 2 1*
GetCustomEvent 8,020 1,488 3 6 4*
Delay 4,103 1,197 7 10 2
ActionTriggerFx 3,532 538 3 3 5
ActionSoundVoiceOver2 3,499 271 3 5 9*
Gate 3,195 924 6 9 2
SendCustomEvent 2,582 744 2 3 2
ActionBoolControl 1,979 963 3 5 1
ActionShowHide 1,837 1,174 11 11 7
RandomTime 1,808 510 2 3 2
ActionSoundPostEvent 1,493 374 3 4 5*
PlayerEnt 1,473 794 0 1 0
Light 1,417 507 7 8 6
Log 1,325 268 4 4 3
NamedVariableBool 1,206 172 2 3 1
TriggerNotification 1,100 452 4 9 6
ListEntityReference 1,021 445 2 2 2
ActionMidnightControl 958 647 8 18 2*
OpMidnightParametersControl 958 647 0 2 4*
VolumeNotification 857 458 4 11 6

(The prefixes idKisculeNode are omitted in the table. Full list: --signatures.)

Roughly sorted by name, five groups emerge:

  • Notification — Event inputs from the game world: TriggerNotification, DamageNotification, UseNotification, VolumeNotification, AiKilledNotification, PickupNotification, AlarmNotification.
  • Action — anything that does something: ActionDamage, ActionTeleport, ActionAISpawn, ActionShowHide, ActionAutosave, ActionNextMap, ActionGiveItems, ActionMission.
  • Flow / Op — Logic: Gate, Relay, Counter, Delay, Timer, DynamicSwitch, CompareFloat, FlowLayerCheck, FlowFlagCheck.
  • Variable / Cast — Values: Bool, Int, Float, Str, Time, NamedVariable*, EntityToString, FloatsToVector.
  • Reference — Pointers to the map: EntityReference, ListEntityReference, PlayerEnt, ActiveAIList.

Cross-check against the EXE

docs/ghidra/rtti_tnc.json contains 683 RTTI classes with "Kiscule" in the name, of which 315 idKisculeNode* (excluding the …Parameters suffixes).

  • All 205 node types used in Retail have an RTTI class — 0 Unknowns. This is a prediction derived from two independent sources (game data, EXE) yielding the same result.
  • Three used types do not start with idKisculeNode*, but are instead named idKisculeLeverNotification, idKisculePlayerNotification, and idKisculePlayerControlledNotification.
  • 113 node classes exist in the EXE but appear in no Retail scripts. Among them: LogicGateAnd, LogicGateNot, LogicGateXor, TestBools, CompareBool, CompareString, ClampInt, ClampFloat, MultiplyInt, DivideInt, SquareRootFloat, ActionGiveUpgrades, ActionPlayerPerk, ActionPlayerManipulate, ActionShowScreen, ActionSetOrientation, HudTimer, VaultCheck, DoorNotification, NamedVariableStr, ListInt, ListString, ParameterListOperatorAddItem*. Fully verifiable against the RTTI list via --signatures. Whether the engine loads such a node when it is inserted remains untested — the game has not been run.

5. A Script, Readable

python tools/kiscule.py --graph "*wooden_shipping_crate_loot_01_8364217*"

# …/c05p2_gameplay_district_kiscule_wooden_shipping_crate_loot_01_8364217.decl
# (version 6, 8 Nodes, 0 Variables, 0 Comments)

[1004261757] EntityReference   @780,740
    . value = "c05p2_gameplay_district_func_separable_part_tablet_1_8364217"
    -> value                        -> ActionWorldMarker[1998712095].entity
    -> value                        -> DamageNotification[150361742].listenedEntities

[2136463365] GetCustomEvent   @460,180
    . eventName = "sumerian_show"
    . global = true
    . enabled = true
    . maxNotificationCount = -1
    -> Received                     -> ActionShowHide[655736182].Show

[655736182] ActionShowHide   @820,200
    . showEntities.num = 0
    . hideEntities.num = 0
    . detachAnyAttachedObjectOnHide = false
    <- Show                         <- GetCustomEvent[2136463365].Received
    -> Show                         -> ActionWorldMarker[1998712095].Show
    <- showEntities                 <- ListEntityReference[1163848210].value

[150361742] DamageNotification   @840,640
    . enabled = true
    . maxNotificationCount = 1
    . waitingTime = 0
    . filterDamageType = "DAMAGETYPE_HEALTH DAMAGETYPE_EMP DAMAGETYPE_ELECTRIC …"
    -> Damaged                      -> SendCustomEvent[695051612].Send
    <- listenedEntities             <- EntityReference[1004261757].value

[695051612] SendCustomEvent   @1140,620
    . eventName = "tablet_destroyed"
    . global = true
    <- Send                         <- DamageNotification[150361742].Damaged
    -> Send                         -> ActionWorldMarker[1998712095].Hide

Read: A box (…part_tablet_1) becomes visible as soon as the global event sumerian_show occurs. If it takes damage — once per damage type — then tablet_destroyed is sent globally and the world marker is hidden. This is a collectible item.

--unconnected additionally displays free connections.


6. Map ↔ Script: The Rosetta Stone Holds

The connection is open. In the map:

entityDef c01p1_vo_kiscule {
    inherit = "kiscule";
    class = "idKisculeEntity";
    edit = {
        externalKisculeDecl = "maps/game/wolf/c01/c01p1_snd_vo/kiscules/c01p1_vo_kiscule.decl";
    }
}

python tools/kiscule.py --maplink checks both directions across all 29 maps:

29 Maps
externalKisculeDecl resolved: 2443/2443
EntityReference found in same map: 13520/13670 (98.90%)
  • 2443 of 2443 references point to a script that exists in the archive. (2443 is exactly the number of kiscule entities from docs/moddingflaeche-tnc.md — two independent counts, same number.)
  • 98.90% of idKisculeNodeEntityReference values are in the same map as entityDef <name> {. Thus, the entity name is the common key between map and script: whoever renames an entity in the map breaks the script, and whoever wants to know which script an object interacts with searches for its name.
  • The 150 failures are distributed across 29 maps; the top offender is volume_fire_damage_437646057 (21×). Whether these are runtime-spawned entities or map debris from the editor is undetermined.

7. Control Tests

python tools/kiscule.py --roundtrip and --verify, both across all 2408:

Text Roundtrip bit-exact:      2408/2408  (195090346 Byte Text)
Container Roundtrip bit-exact: 2408/2408  (13293044 Byte Payload)
Lines through model:           9243027

== Control Tests over 2408 Scripts ==
  clean: 2378   with broken references (Retail Defect): 30   Exception: 0
  Nodes: 70274 in 205 Types   Edge Ends: 178559 (= 89279 Edges)
  Edge Ends with Match: 178516   of which same edge-id: 149620, others: 28896

-- Prediction against Retail --
  ok   scripts            2408 (expected 2408)
  ok   nodes              70274 (expected 70274)
  ok   types              205 (expected 205)
  ok   edge_ends          178559 (expected 178559)
  ok   mirrored           178516 (expected 178516)
  ok   broken files       30 (expected 30)

The numbers in RETAIL (above in tools/kiscule.py) are hard-wired: a full sweep that deviates from this causes --verify to fail with return value 1. Whoever breaks the parser notices it here.

The predictions that had to be correct:

Claim Independent Recalculation Result
nodes.num is the node count item[0…num-1] must all exist, and no further ones may exist — in every num block of the file, including connectionPoints, connections, lists 2408/2408
70,274 nodes in 205 types survey_tnc.py --maps counts via regex over raw bytes, kiscule.py over the parsed tree identical
every node type is a real class RTTI from NewColossus_x64vk.exe 205/205
map references are resolvable externalKisculeDecl against archive contents 2443/2443
edges exist at both ends Mirror test over (endNodeID, endConnectionPointID) 178,516/178,559

All six lines are hard-coded as RETAIL in tools/kiscule.py and recalculated on every full sweep.

The Negative Test. A parser that reports everything green proves nothing. --verify therefore deliberately breaks one assurance and demands that the check succeed:

-- Negative test: tests must fail with corrupted data -- ok num-counter falsified KisculeError ok Edge bent into void Edge … points to unknown node 424242 ok NUL removed from end KisculeError ok Indent shifted KisculeError ok csize bent in header KisculeError ok byte flipped divergent

If any of these six fail, --verify returns exit code 1.

What is not tested: whether the game loads a rewritten script. The game was not started. Bit-exactness here means: identical bytes to Retail — not "accepted by the engine".


8. What a Mod Author Can Do With It

Safe, because it's just numbers (--set, one line):

python tools/kiscule.py --set gp_kiscule_devstarts \
  "edit.kiscule.nodes.item[8].parameters.object.delay=500" --out payload.bin

--set changes exactly one value and rebuilds the container. Without change, the result is byte-identical to Retail — verified. With change, Text and Container differ precisely at the intended location (calculated: 1 divergent byte for a single-character change).

Useful knobs, all in parameters.object:

Node Type Field What it does
Delay delay Wait time in ms — tempo of script chains
RandomTime min, max Scatter of repetitions
DamageNotification maxNotificationCount, waitingTime, filterDamageType what an object reacts to
GetCustomEvent enabled, maxNotificationCount disable or unlock a trigger
ActionBoolControl Bool switch flip script branches
ActionGiveItems Equipment lists what an event outputs
ActionAISpawn Spawn parameters enemy waves
ActionAutosave — save points
variables[].object.defaultValue starting value of a graph variable

Likely feasible but untested: bending edges. Bending an edge means adjusting endNodeID/endConnectionPointID at both ends — the mirror check in --verify catches it if you forget one. Adding nodes additionally requires a free id (32 bits, unique within the file) and matching connections; the signature table says what those are. Whether the engine accepts this: unverified. (Update 2026-10-04: the Studio editor now does both, checked structurally, not in game; see section 10.)

The path to the archive is not part of this tool. --set --out delivers the finished user data; getting it into .resources is the job of wolfsdk/patch.py / tncpatch.py, and patch precedence (which archive wins) remains open in the project — see docs/moddingflaeche-tnc.md, section 7. (Update 2026-10-04: the Studio saves script edits as mods, and the loader writes them into every copy of the script, so the precedence question no longer decides what a mod changes; see Patch Precedence sections 8–9.)


9. What Remains Open

  • version = 5 vs 6. Scripts carrying version 5: 64; scripts carrying version 6: 2329. No difference is visible in the structure. What distinguishes them for the engine: unknown.
  • properties-Bits 1, 4, and 32. Bit 16 (List) is set; bit 8 is the default case — the remaining bits appear in 2583 of 155,538 connections and are unassigned.
  • The id of an edge. It is often, but not always, the same at both ends (149,620 : 28,896). What else it means: unknown. It is unnecessary for reading the graph.
  • 43 dead edges in 29 Retail files, plus one file with duplicate connection IDs. Whether the engine silently skips them or if something is broken in the game there: unobservable without starting the game.
  • 84 scripts are duplicated in the archive, all 84 with different content. (--dump reports this.) Which version wins depends on patch precedence and is immeasurable — the same open question as for Decls. (Update: for mods it no longer matters. Since WolfSDK 0.2.0 the loader edits the newest patch copy and writes the result into every copy.)
  • 150 Entity references without a target in maps (1.10%).
  • The 113 unused node classes. They exist in the EXE. Whether a script can load them: untested.
  • g_kisculeRuntimeTypeChecks ("If 1, Kiscule will log all its runtime check verification", WINDOWS/DEBUG only) would be the direct way to let the engine talk about its own scripts via them. Not used — the game was not started.

10. The WolfSDK Studio Kiscule Script and Mission Editor

The Studio's Scripts tab (WolfSDK 0.2.0, added 2026-10-04) shows the 3,010 Kiscule scripts as node graphs, with all 205 node types and a mission filter. Edits are operations on one script (wolfsdk/kiscule.py, apply_ops):

  • set: a parameter; setting a list's num resizes the list (section 11);
  • add_node: a node of a catalogued type, built like a retail node of that type;
  • remove_node: the node and every edge end that touches it;
  • add_edge, remove_edge: output → input, event or variable at both ends.

Every edge is written at both ends, as in retail. After the ops the text is parsed again and must not hold a broken edge end the original did not. A save becomes a mod: the extkisclule entry as a whole file, packed exactly as retail packs it. Scripts only in a DLC archive, and entries in archive mode 4, are refused.

Not yet confirmed in game.

Keywords: Kiscule editor, script editor, mission editor, Studio Scripts tab, node graph, edit script, Skript-Editor, Missionen bearbeiten

11. Resizing Lists in the Kiscule Editor

Some node types grow or lose ports with a list. The rules (kiscule.LISTS) are measured over every retail node of these types:

Node type and list Ports when the list changes
DynamicSwitch weigths (engine spelling) one Case %d output per item; outputCount follows the list (211/211)
InputListener conditions Cond %d Passed and Cond %d Stopped per item (12/12)
ActionMission objectives fixed: the same 24 ports for 0 to 14 objectives (582/582)
ActionSoundPostEvent entities fixed (1864/1864)

A new item starts as a retail item of the type; a new mission objective starts as NULL. Shrinking a list drops the cut ports and their edges at both ends. A list holds up to 256 items.

Not resizable: FlowFlagCheck, FlowProgressionCheck, MidnightNotification and OpMidnightParametersControl name their ports after the items' contents.

Keywords: resize list, add objective, remove objective, DynamicSwitch case, InputListener, ports, Missionsziel hinzufügen, Liste

12. Saving Kiscule Edits: Accumulating Saves and the 409 Refusal

Which copy of the script a save edits:

  • A new mod: the game's copy, as the loader reads it from the archives (since 0.2.0 the newest patch copy, Patch Precedence section 8).
  • A Studio mod that already changes this script: that mod's own copy. Edits therefore accumulate over several saves into one mod, and the Studio shows the script as it is in the mod.
  • Refused (HTTP 409): a save based on the game's copy while an applied mod changes that script. The archives then hold that mod's version, and its changes would end up in yours. Reset the mods in the loader first. A save onto a Studio mod's own copy is not refused: that copy was made from a clean game copy.

Known limit: two saves racing into the same mod folder keep only one of the edits.

Keywords: save script, accumulate edits, 409, conflict, applied mod, reset mods first, speichern, Konflikt

13. Kiscule Editor Checks and In-Game Status

tools/verify_studio_kiscule.py: 98 of 98 checks pass, offline only (the game is not started). It checks:

  • every installed script re-packs byte-identical;
  • one save per operation kind, each changing exactly that one thing;
  • invalid requests get an English error and write nothing;
  • the list rules of section 11 on every retail node of those types, with the opposite rules refuted;
  • accumulating saves and the 409 refusal.

Not yet confirmed in game: whether the engine runs edited or added nodes. The first edited scripts meant for the game are the 12 dev start scripts of the Dev Menu Map Loading mod (TNC Dev Menu), also not yet confirmed in game.

Keywords: verify_studio_kiscule, tests, checks, does it work in game, Kiscule status, Prüfung, getestet