name: galaxy-debug-data-catalog description: Debug output, Data Table key-value storage, Catalog runtime field access, UserData, and asset preloading in Galaxy script. Use when reading or writing catalog fields at runtime, storing cross-trigger state in the Data Table, printing debug output, or preloading models and sounds. Do not use for bank save/load (use galaxy-game-systems).
Galaxy Scripting – Debug, Data Table & Catalog
Key References
Debug Output
// Print to the in-game debug window (F7 to open in test mode)
TriggerDebugOutput(c_triggerDebugOutputCode, StringToText("my value: " + IntToString(lv_n)), true);
// Enable/disable debug output globally
TriggerDebugOutputEnable(c_triggerDebugOutputCode, true);
// Open or close the debug window for a player
TriggerDebugWindowOpen(lv_player, true);
// Convenience debug functions (Template / unclassified)
DebugString(lv_player, "lv_unit type: " + UnitGetType(lv_unit));
DebugInt(lv_player, "lv_i", lv_i);
DebugFixed(lv_player, "lv_dist", lv_dist);
DebugUnit(lv_player, lv_unit);
DebugPoint(lv_player, lv_point);
// Debug message type constants
c_triggerDebugOutputCode // code path output
c_triggerDebugOutputError // error highlighting
c_triggerDebugOutputMessage // general message
Data Table (Global Key-Value Store)
The Data Table stores arbitrary values by string name, accessible from any trigger. It survives the scope of individual functions.
⭐ SC2-IngameDevTools DataTable patterns (PRIMARY — use these)
The SC2-IngameDevTools codebase uses the DataTable extensively as a backing store for its ItemList abstraction, chat command parameters, per-player state, and catalog lookups. Key patterns:
// Namespaced keys prevent collisions — use "Module.Feature.Key" format
DataTableSetInt(true, "DataTableListPage."+IntToString(player), page);
int page = DataTableGetInt(true, "DataTableListPage."+IntToString(player));
// Store catalog type integer by catalog name string
DataTableSetInt(true, "Catalog.Unit", c_gameCatalogUnit);
DataTableSetInt(true, "Catalog.Ability", c_gameCatalogAbil);
// Retrieve: int catalog = DataTableGetInt(true, "Catalog."+catalogName);
// Per-player listbox selection state (DataTable as a UI state cache)
string key = listId+".Selected["+IntToString(player)+"]:"+IntToString(listItem);
DataTableSetString(true, key, ItemListGetVal(itemList, index));
if (DataTableValueExists(true, key)) {
string val = DataTableGetString(true, key);
}
DataTableValueRemove(true, key);
// Trigger event param passing via DataTable (scoped to trigger execution context)
string paramKey = TriggerEventParamName("DevTools_ChatCommand.Exec."+cmd, "_player");
DataTableSetInt(false, paramKey, player);
int p = DataTableGetInt(false, TriggerEventParamName(EventGenericName(), "_player"));
// ItemList backing store (the full ItemList implementation in ItemList/index.galaxy)
// ItemList is a string key; all data goes into the DataTable with structured keys:
// "ItemList::[listId][index]" → the value stored at that slot
// "ItemList::[listId].Count" → current item count
// "ItemList[listId].Active.Player1" → current selection per player
Global (map-wide) data table
// Save values — always pass true for the global (map-wide) scope
DataTableSetInt(true, "myKey", 42);
DataTableSetFixed(true, "position_x", 16.5);
DataTableSetBool(true, "isPhase2", true);
DataTableSetUnit(true, "heroUnit", lv_hero);
DataTableSetString(true, "lastEvent", "SpawnWave");
DataTableSetPoint(true, "spawnPt", lv_point);
// Load values
int lv_n = DataTableGetInt(true, "myKey");
fixed lv_x = DataTableGetFixed(true, "position_x");
bool lv_b = DataTableGetBool(true, "isPhase2");
unit lv_u = DataTableGetUnit(true, "heroUnit");
string lv_s = DataTableGetString(true, "lastEvent");
point lv_p = DataTableGetPoint(true, "spawnPt");
// Check existence
bool lv_has = DataTableValueExists(c_dataTableScopeGlobal, "myKey");
// Remove
DataTableValueRemove(c_dataTableScopeGlobal, "myKey");
// Scope constants
c_dataTableScopeGlobal // map-wide
c_dataTableScopeLocal // trigger-local
Instance Data Tables
Instance tables let you create multiple independent tables (like a dictionary per unit):
DataTableInstanceCreate();
int lv_dt = DataTableInstanceLastCreated();
DataTableInstanceSetInt(lv_dt, "kills", 0);
DataTableInstanceSetUnit(lv_dt, "owner", lv_unit);
int lv_k = DataTableInstanceGetInt(lv_dt, "kills");
unit lv_o = DataTableInstanceGetUnit(lv_dt, "owner");
DataTableInstanceClear(lv_dt);
Catalog (Runtime Data Field Access)
The Catalog lets you read and modify game data fields at runtime — damage, range, cost, etc. — from within triggers.
Reading catalog values
// Get the string value of a data field
string lv_val = CatalogFieldValueGet(
c_gameCatalogUnit, // which catalog
"Marine", // entry name
"LifeMax", // field name
lv_player // player context
);
// Get as integer or fixed
int lv_int = CatalogFieldValueGetAsInt(c_gameCatalogUnit, "Marine", "LifeMax", lv_player);
fixed lv_real = libNtve_gf_CatalogFieldValueGetAsReal(c_gameCatalogWeapon, "C-14Rifle", "Range", lv_player);
// Catalog constants
c_gameCatalogUnit
c_gameCatalogWeapon
c_gameCatalogAbil
c_gameCatalogEffect
c_gameCatalogBehavior
c_gameCatalogUpgrade
c_gameCatalogModel
c_gameCatalogSound
c_gameCatalogActor
Writing catalog values
// Set a field for a player (overrides for that player)
bool lv_ok = CatalogFieldValueSet(
c_gameCatalogWeapon, "C-14Rifle", "Range",
lv_player, "8" // value as string
);
// Modify relative to current value
CatalogFieldValueModify(
c_gameCatalogUnit, "Marine", "LifeMax",
lv_player, c_upgradeOperAdd, "25" // add 25 HP
);
// Set as real
libNtve_gf_CatalogFieldValueSetAsReal(
c_gameCatalogWeapon, "C-14Rifle", "Range",
lv_player, 9.0
);
Array fields
// Field array element — for fields like Weapons[0].Range
int lv_count = CatalogFieldValueCount(c_gameCatalogUnit, "Marine", "Weapons", lv_player);
string lv_wpn = CatalogFieldValueGet(c_gameCatalogUnit, "Marine", "Weapons[0]", lv_player);
Catalog references
// Read a link reference (e.g., which weapon a unit uses)
string lv_ref = CatalogReferenceGet(c_gameCatalogUnit, "Marine", "Weapons[0]", lv_player);
// get as int
int lv_ri = CatalogReferenceGetAsInt(c_gameCatalogUnit, "Marine", "Weapons[0]", lv_player);
User Data
User data allows embedding custom typed values directly in game data entries (configured in the editor's data module).
// Load a user data value by category and field
string lv_val = UserDataGetString("MyCategory", lv_entryName, "MyField", 0, lv_player);
int lv_i = UserDataGetInt("MyCategory", lv_entryName, "MyField", 0, lv_player);
fixed lv_f = UserDataGetFixed("MyCategory", lv_entryName, "MyField", 0, lv_player);
text lv_t = UserDataGetText("MyCategory", lv_entryName, "MyField", 0, lv_player);
color lv_c = UserDataGetColor("MyCategory", lv_entryName, "MyField", 0, lv_player);
Preloading Assets
Preload before they're needed to avoid hitches:
PreloadAsset("Assets\\Textures\\MyHero.dds");
PreloadModel("Assets\\Units\\Hero\\Hero.m3");
PreloadImage("Assets\\UI\\HeroPortrait.dds");
PreloadLayout("Assets\\UI\\MyDialog.SC2Layout");
// Preload model animations — native function (NOT a libNtve helper):
ModelAnimationLoad("Assets\\Units\\Hero\\Hero.m3", "Assets\\Units\\Hero\\Hero_Attack.m3a");
ModelAnimationLoadOverriding("Assets\\Units\\Hero\\Hero.m3", "Assets\\Override\\Hero_Walk.m3a");
ModelAnimationUnload("Assets\\Units\\Hero\\Hero.m3", "Assets\\Units\\Hero\\Hero_Attack.m3a");
// (modelPath, animPath) — animPath is the .m3a animation file
Trigger Debugger
The Trigger Debugger is the primary runtime analysis tool for Galaxy scripts.
Full documentation: Trigger Debugger guide
Launching
| Method | How |
|---|---|
| In-game cheat | Type trigdebug in chat during a map test |
| Editor setting | File → Preferences → Test Document → Show Trigger Debugging Window |
| Galaxy code | TriggerDebugWindowOpen(lv_player, true); |
| Galaxy breakpoint | Add the keyword Breakpoint; in Galaxy code — debugger opens and jumps to that line when executed |
The game must be in Windowed mode for the debug window to appear.
Recommended initial config
Right-click the bottom subview → uncheck all "Show" options except Show Errors and Show User Output. This dramatically reduces performance overhead from the debugger.
Key tabs
| Tab | Best used for |
|---|---|
| Variables | Browse all global variables and their current values at runtime. Sort by Total Memory Size to find memory hogs. Complex types (arrays, structs) show "Double Click to Expand". |
| Triggers | List of all triggers, with fire count, fail count, run count, average/total run time. Sort by Total Time to find background lag; sort by Average Run Time to find latency spikes. |
| Threads | All active Wait-based threads. Shows which Wait type (Real vs Game) each thread is currently blocked on. Right-click → View Script to jump to the function. |
| Queue | Triggers currently using TriggerQueueEnter()/TriggerQueueExit(). |
| Trigger Profiling | Detailed profiling of threaded triggers. Shows Self-Only Time (pure Galaxy ops, excluding sub-function call time) vs Self+Children Time (total including sub-calls). Enable Show Natives and Show SubCalls for full call-stack detail. |
| Function Profiling | Per-function call stats, run count, average/worst/total run time. |
| Activity | Visual graph of fired events (green), checked conditions (yellow), executed triggers (red) vs time. Useful for spotting latency spikes. |
| Script Code | Full source view with breakpoint support, locals inspection, callstack, and Watch list. |
Setting breakpoints in Script Code tab
- Right-click in the Script Code tab → Add/Remove Breakpoint on the desired line.
- When execution hits the breakpoint, the game pauses and the debugger gains focus.
- Use the dropdown menus in the subviews to switch between Globals, Locals, Watch, Callstack, or Breakpoints views.
- The Locals view is the most useful — it shows every local variable and event parameter in the currently paused function.
Profiling workflow (from Optimizing Code guide)
- Run the map with the debugger open.
- In Triggers Tab, sort by
Total Time— this reveals which trigger causes the most cumulative lag. - In Trigger Profiling Tab, enable
Show NativesandShow SubCalls— this exposes exactly which native functions inside the trigger are the hottest. - Optimize the inner-loop first: even a small improvement in a hot inner loop compounds multiplicatively.
Note: Unit Custom Values are not tracked in the Variables Tab. If you use custom values for debugging, build your own debug UI.
Data Table: Performance & Scope Guidelines
(Expanded from the Data Tables guide)
Search time comparison
| Structure | Search Time | Notes |
|---|---|---|
| Array | Linear O(n) | Making it 10× bigger increases search time 10×. |
| Data Table | Constant O(1) | Search time does not grow with table size. |
Use arrays for small, typed, well-defined collections. Use Data Tables when the collection is large, dynamic, or mixed-type.
Scope rules
| Scope | Created by | Lifetime | Access |
|---|---|---|---|
Global (c_dataTableScopeGlobal) |
Always exists | Entire game session | Accessible from any trigger |
Local (c_dataTableScopeLocal) |
First DataTableSet* call inside a trigger |
Cleared when the trigger exits | Only the current trigger |
Memory leak risk: The Global data table is permanent. Entries added but never removed will accumulate. Always call DataTableValueRemove(c_dataTableScopeGlobal, "key") or DataTableClear(c_dataTableScopeGlobal) when data is no longer needed. Local tables are cleaned up automatically when their trigger finishes.