galaxy-code-organization

star 5

File structure, include order, and modular layout for Galaxy script projects. Use when splitting a monolithic script into multiple files, establishing the MapScript.galaxy bootstrap chain, organizing UI into sub-files, or setting up Enums/GlobalVariables/Header/MapInit. Covers which files are auto-generated by the editor and must never be edited (MapScript.galaxy, LibHASH.galaxy, LibHASH_h.galaxy). Do not use for language syntax questions (use galaxy-language-fundamentals).

KimPlaybit By KimPlaybit schedule Updated 3/24/2026

name: galaxy-code-organization description: File structure, include order, and modular layout for Galaxy script projects. Use when splitting a monolithic script into multiple files, establishing the MapScript.galaxy bootstrap chain, organizing UI into sub-files, or setting up Enums/GlobalVariables/Header/MapInit. Covers which files are auto-generated by the editor and must never be edited (MapScript.galaxy, LibHASH.galaxy, LibHASH_h.galaxy). Do not use for language syntax questions (use galaxy-language-fundamentals).

Galaxy Code Organization & File Splitting

Galaxy scripts in a SC2 map can be split across many .galaxy files using include. This keeps code maintainable and modular. The SC2-IngameDevTools mod is the #1 primary reference for file structure and module layout.

Key References

Resource URL
SC2-IngameDevTools Script/ folder (PRIMARY — #1 example) https://github.com/abrahamYG/SC2-IngameDevTools/tree/main/DevToolsIngame.SC2Mod/Script
SSF scripts (secondary style reference) https://github.com/Cristall/SC2-SwarmSpecialForces/tree/main/SwarmSpecialForces.SC2Map/scripts
Alcyone Frontlines scripts https://github.com/KimPlaybit/Alcyone_Frontlines/tree/master/ProximaFrontlines.SC2Mod/scripts
Native function reference https://mapster.talv.space/galaxy/reference
GalaxyScript guide https://s2editor-guides.readthedocs.io/New_Tutorials/03_Trigger_Editor/058_GalaxyScript/
SC2 editor guides index https://s2editor-guides.readthedocs.io
SC2Mapster wiki https://sc2mapster.wiki.gg/

SC2-IngameDevTools Handler-Module Layout (PRIMARY PATTERN)

The SC2-IngameDevTools project organises each feature as a self-contained handler file in Script/. There is one main coordinator, a shared utilities folder, and a _h.galaxy header for forward declarations.

Folder & file structure

Script/
├── DevToolsMain.galaxy          ← coordinator: includes all handlers, calls all _Init()
├── debug.galaxy                 ← global debug helpers: print(), console(), err()
├── debug_h.galaxy               ← forward declarations only (header pattern)
├── split_string.galaxy          ← utility (string split)
├── ItemList.galaxy              ← shared data structure (aggregator)
├── ItemListListBoxFormat.galaxy ← formatting helpers for ItemList
├── AbilityHandler.galaxy        ← feature handler (one per module)
├── AbilityOrderHandler.galaxy
├── ActorMessageHandler.galaxy
├── BehaviorHandler.galaxy
├── CameraHandler.galaxy
├── CameraShakeHandler.galaxy
├── CatalogLinkHandler.galaxy
├── CatalogValueHandler.galaxy
├── CheatHandler.galaxy
├── DataEditorHandler.galaxy
├── DataTableHandler.galaxy
├── DoodadHandler.galaxy
├── EffectHandler.galaxy
├── FogHandler.galaxy
├── LightingHandler.galaxy
├── PlayerHandler.galaxy
├── PortraitHandler.galaxy
├── RaceHandler.galaxy
├── SkinHandler.galaxy
├── SoundtrackHandler.galaxy
├── UnitHandler.galaxy
├── UpgradeHandler.galaxy
├── UserDataHandler.galaxy
├── WeaponHandler.galaxy
├── FreeCamHandler.galaxy
├── ItemList/
│   ├── index.galaxy             ← the actual ItemList implementation
│   └── Listbox.galaxy           ← listbox sub-feature
└── DevTools/
    ├── helpers.galaxy           ← shared helper functions (spawn point, movement tracker)
    ├── helpers_h.galaxy         ← forward declarations for helpers
    ├── ChatCommand.galaxy       ← chat command subsystem
    └── ChatCommand/
        └── Commands.galaxy        ← registered chat commands

Main coordinator (DevToolsMain.galaxy)

include "Script/debug"
include "Script/DevTools/helpers"
include "Script/ActorMessageHandler"
include "Script/BehaviorHandler"
include "Script/EffectHandler"
include "Script/UnitHandler"
include "Script/UpgradeHandler"
include "Script/WeaponHandler"
include "Script/AbilityHandler"
include "Script/CatalogValueHandler"
include "Script/CatalogLinkHandler"
include "Script/LightingHandler"
include "Script/DataTableHandler"
include "Script/DataEditorHandler"
include "Script/CameraHandler"
include "Script/FogHandler"
include "Script/DevTools/ChatCommand"
include "Script/DevTools/ChatCommand/Commands"

void DevToolsMain_Init() {
    DevTools_ChatCommand_Init();
    helpersInit();
    ActorMessageHandler_Init();
    CatalogValueHandler_Init();
    UnitHandler_Init();
    BehaviorHandler_Init();
    EffectHandler_Init();
    LightingHandler_Init();
    // ... every handler's _Init() called here
    DevTools_ChatCommand_Commands_Init();
}

Entry point wiring (Lib7C0075CB.galaxy — editor-generated lib file)

include "TriggerLibs/natives"
include "Lib7C0075CB_h"
include "TriggerLibs/NativeLib"
include "Script/DevToolsMain"

void TestMap_main() {
    DevToolsMain_Init();
}
void lib7C0075CB_InitCustomScript() {
    TestMap_main();
}
bool lib7C0075CB_InitLib_completed = false;
void lib7C0075CB_InitLib() {
    if (lib7C0075CB_InitLib_completed) { return; }
    lib7C0075CB_InitLib_completed = true;
    lib7C0075CB_InitCustomScript();
}

Header file pattern (debug_h.galaxy)

// ONLY forward declarations — no implementations
void print(string s);
void printT(text t);
void console(string s);
void err(string s);

Single handler module anatomy (BehaviorHandler.galaxy)

// 1. Include shared utilities
include "Script/debug_h"
include "Script/ItemList"

// 2. Path constants with UPPER_SNAKE_CASE
static const string CONTAINERDLG_PATH =
    "UIContainer/ConsoleUIContainer/CatalogManager/BehaviorManager";

// 3. Module struct + global instance
ItemListContainerStruct BehaviorContainer;

// 4. File-private state
static string ItemList;
static ListBoxFilterStruct ListBoxFilter;

// 5. Trigger event functions (bool a, bool b signature)
bool BehaviorListBoxFilterQuery(bool a, bool b) { ... }
bool BehaviorListBoxSelectionChanged(bool a, bool b) { ... }
bool BehaviorContainerSendHandler(bool a, bool b) { ... }

// 6. One public Init function
void BehaviorHandler_Init() {
    playergroup pg = PlayerGroupAll();
    ItemList = "BehaviorList";
    ItemListContainer_InitStandard(BehaviorContainer, CONTAINERDLG_PATH,
        "BehaviorContainerSendHandler");         // trigger registered by STRING name
    ItemListInitFromCatalog(ItemList, c_gameCatalogBehavior, ItemListCatalogFilter);
    ItemList_FilterListInitStandard(ListBoxFilter, "BehaviorListBox",
        BehaviorListBoxSetActive, ItemListItemTextValue,
        CONTAINERDLG_PATH+"/NavList");
    ItemList_FilterListRebuild(ItemList, ListBoxFilter, "", pg);
}

Real-World Include Orders

SSF full include order (scripts/main.galaxy) — secondary reference

include "scripts/Lib/Starcode"       // third-party lib first
include "scripts/Enums"              // pure constants
include "scripts/GlobalVariables"    // structs, global vars, typedefs
include "scripts/secrets"
include "scripts/Header"             // forward declarations
include "scripts/Utilities"          // shared helpers
include "scripts/AchievementsTemplates"
include "scripts/Achievements"
include "scripts/UI/UI-Achievements" // UI panels first (alphabetical)
include "scripts/UI/UI-CustomDefeatFrame"
include "scripts/UI/UI-HeroPanel"
include "scripts/UI/UI-Objectives"
include "scripts/UI/UI-Options"
include "scripts/UI/UI-PlayerBoard"
include "scripts/UI/UI-Popup"
include "scripts/UI/UI-Speedruns"
include "scripts/UI/UI-Stats"
include "scripts/UI/UI-Votekick"
include "scripts/UI/UI-HivePanel"
include "scripts/UI/UI-Revive"
include "scripts/UI/UI-Main"         // UI coordinator last
include "scripts/Bank"
include "scripts/Enemy"
include "scripts/PartTerran"         // content parts
include "scripts/PartProtoss"
include "scripts/PartZerg"
include "scripts/Parts"              // part coordinator last
include "scripts/Hive"
include "scripts/HeroAbilities"
include "scripts/Player"
include "scripts/Collectibles"
include "scripts/HeroSelection"
include "scripts/Tutorial"
include "scripts/Debug"
include "scripts/MapInit"            // map init always last

void main(){
    TriggerAddEventMapInit(TriggerCreate("MapInit_Main"));
}

Alcyone Frontlines include order (scripts/main.galaxy)

include "scripts/GlobalVariables"
include "scripts/MapInit"
include "scripts/Bank"
include "scripts/Player"
include "scripts/Spawner"
include "scripts/Jungle"
include "scripts/UI"
include "scripts/Buildings"
include "scripts/AI"
include "scripts/HeroSelection"
include "scripts/HeroLevelUp"

⚠️ Auto-Generated Files — NEVER Edit

The SC2 editor auto-generates certain .galaxy files. Do not write code in these files — the editor will overwrite any manual changes.

File Why it is auto-generated
MapScript.galaxy The editor rebuilds this on every save. It wires trigger libraries and calls InitMap().
LibHASH.galaxy (e.g. Lib5A1C9904.galaxy) The library wrapper file for an .SC2Mod. The editor generates it from the mod's trigger data. It wraps calls into scripts/main.galaxy via include.
LibHASH_h.galaxy The auto-generated header for the library wrapper.

What these files contain (reference only)

MapScript.galaxy — bootstraps the engine:

include "TriggerLibs/NativeLib"
include "LibHASH"             // pulls in the mod library if present
void InitLibs() { libNtve_InitVariables(); libHASH_InitLib(); }
include "scripts/main"        // custom scripts (if standalone map)
void InitCustomScript() { main(); }
void InitMap() { InitLibs(); InitCustomScript(); }

LibHASH.galaxy — a thin editor-generated wrapper that:

  • calls libNtve_InitVariables() as its library init
  • includes scripts/main once
  • exposes public functions as libHASH_gf_* wrappers
  • registers a LoadTriggers trigger that calls ProximaInitTriggers() (or equivalent)
  • defines libHASH_InitLib() / libHASH_InitTriggers()

All actual logic lives in scripts/ — that is where you write code.


Bootstrap Chain

MapScript.galaxy is the map's entry point — it is generated by the editor and should contain minimal code:

include "TriggerLibs/NativeLib"
include "TriggerLibs/LibertyLib"

void InitLibs() { libNtve_InitLib(); libLbty_InitLib(); }

include "scripts/main"          // <-- pulls in all custom scripts

void InitCustomScript() { main(); }
void InitMap() { InitLibs(); InitCustomScript(); }

scripts/main.galaxy is the coordinator — it lists every include in dependency order and defines main():

include "scripts/Lib/Starcode"
include "scripts/Enums"
include "scripts/GlobalVariables"
include "scripts/Header"
include "scripts/Utilities"
// ... all other files ...
include "scripts/MapInit"

void main() {
    TriggerAddEventMapInit(TriggerCreate("MapInit_Main"));
}

main() only registers a single map-init trigger. All actual initialization happens inside that trigger function in MapInit.galaxy.


Include Syntax & Rules

include "scripts/Utilities"          // relative to map root, no .galaxy extension
include "scripts/UI/UI-Main"         // subdirectories are supported
include "scripts/Lib/Starcode"       // third-party / library code in Lib/

Critical rules:

  • Paths are relative to the map root (where MapScript.galaxy lives), not to the including file.
  • Do not add .galaxy extension — the engine appends it.
  • Order matters: a file can only call functions/use types declared in files included before it.
  • No circular includes. Each file is included exactly once.
  • There is no header guard mechanism — avoid including the same file twice.

File Roles & Naming Conventions

File Role
MapScript.galaxy Auto-generated by editor. NEVER edit. Entry point that wires libraries and calls InitMap().
scripts/main.galaxy Coordinator: all includes + void main().
scripts/Enums.galaxy Pure const int constants, grouped by category with comment headers.
scripts/GlobalVariables.galaxy All global const, static, struct definitions, and typedef funcref.
scripts/Header.galaxy Forward declarations for every cross-file function.
scripts/Utilities.galaxy Shared helper functions used by many other files.
scripts/MapInit.galaxy Map initialization logic (alliances, player groups, startup sequence).
scripts/Enemy.galaxy Enemy spawning and behavior.
scripts/Bank.galaxy Bank read/write logic.
scripts/Player.galaxy Player stats, XP, upgrades.
scripts/HeroSelection.galaxy Hero selection UI and unlock logic.
scripts/HeroAbilities.galaxy Hero ability implementations.
scripts/Parts.galaxy Coordinator for multi-part content (calls into Part* files).
scripts/PartTerran.galaxy Content specific to the Terran part.
scripts/PartProtoss.galaxy Content specific to the Protoss part.
scripts/PartZerg.galaxy Content specific to the Zerg part.
scripts/Debug.galaxy Debug helpers, override functions.
scripts/UI/UI-Main.galaxy Initializes all UI subsystems.
scripts/UI/UI-*.galaxy One file per UI panel/system.
scripts/Lib/Starcode.galaxy Third-party library.

Enums.galaxy — Pure Constants File

All game-wide integer constants go in one file:

// parts
const int c_Part_Terran  = 0;
const int c_Part_Protoss = 1;
const int c_Part_Zerg    = 2;

// game modes
const int c_GameMode_Normal  = 0;
const int c_GameMode_Endless = 1;

// bitflags
const int c_BossFightState_Alive   = 1 << 0;
const int c_BossFightState_Enraged = 1 << 1;
const int c_BossFightState_Dead    = 1 << 2;

// boss IDs
const int c_BossFightID_Flamer  = 0;
const int c_BossFightID_Crusher = 1;

Naming convention: c_CategoryName_IdentifierName
Grouping: Use // category name comment headers to separate groups.
Bitflags: Use 1 << n pattern.

Include Enums.galaxy before GlobalVariables.galaxy so struct fields can reference these constants for array sizes.


GlobalVariables.galaxy — Structs & Global State

Declare all structs, global variables, and typedef funcref here:

// global consts
const int gv_MaxAmountPlayers = 6;
const int gv_MaxAmountParts   = 3;

// funcref blueprints
bool blueprint_BossAbility(unitgroup ug, unit boss);
typedef funcref<blueprint_BossAbility> Blueprint_BossAbility;

// structs
struct PlayerStruct {
    bool activeFlag;
    bank bankfile;
    unit heroUnit;
    int points;
    int[gv_MaxAmountParts] wins;   // array sized by const
    int heroUnlocked;              // stored as bitflag
};

// global arrays
PlayerStruct[gv_MaxAmountPlayers + 1] gv_PlayerStats;

Naming convention: gv_SystemName_VariableName for globals, gv_VariableName for simple globals.
Static locals that are cross-trigger: Declare as static at file scope in the file that owns them.


Header.galaxy — Forward Declarations

Galaxy requires functions to be declared before they are called. When file A calls a function defined in file B (included later), use a forward declaration in Header.galaxy:

// Header.galaxy — comment groups match their source file

// UI-PlayerBoard.galaxy
void PlayerBoard_UpdatePlayer(int playerID);

// Bank.galaxy
void Bank_Save_ForcedAll(int playerID);
void Bank_Save_RequestSave(int playerID);

// Parts.galaxy
void Part_PartFinished();
void Part_InitVariables();

// Player.galaxy
void Player_AddExp(int playerID, fixed amount);

Include Header.galaxy after GlobalVariables.galaxy so declarations can reference struct types.


UI Subdirectory Pattern

Each UI panel gets its own file. A coordinator file (UI-Main.galaxy) initializes all of them:

// scripts/UI/UI-Main.galaxy
void SSFCustomUI_Init() {
    gv_UI_MasterFrame = DialogControlHookupStandard(c_triggerControlTypePanel, "UIContainer/...");
    StatsInterface_Init();
    Votekick_Init();
    Options_Init();
    HeroPanel_Init();
    PlayerBoard_Init();
    // ...
}

In main.galaxy, include individual UI files before UI-Main.galaxy:

include "scripts/UI/UI-HeroPanel"
include "scripts/UI/UI-PlayerBoard"
include "scripts/UI/UI-Options"
// ... all panels first ...
include "scripts/UI/UI-Main"     // coordinator last, so it can call the others

Parts Pattern — Per-Content Modularity

When content is split by campaign mission / chapter / faction, use a coordinator + per-part files:

// In main.galaxy — include individual parts BEFORE coordinator
include "scripts/PartTerran"
include "scripts/PartProtoss"
include "scripts/PartZerg"
include "scripts/Parts"        // coordinator included last

Each Part*.galaxy owns its content and exposes a TriggerCreate() function:

// PartTerran.galaxy
void PartTerran_AreaJunker_Second_Open() { ... }
void PartTerran_TriggerCreate() {
    TriggerAddEventXxx(TriggerCreate("PartTerran_SomeHandler"), ...);
}

Parts.galaxy coordinates between parts (handles part transitions, shared state):

// Parts.galaxy
static const int observer_AmountWaypoints = 11;
static point[observer_AmountWaypoints] observer_Waypoints;

void Part_InitVariables() { ... }
void Part_PartFinished() {
    PartTerran_TriggerCreate();   // calls into part files
}

Lib Subdirectory — Third-Party Code

Put external/library code in scripts/Lib/:

include "scripts/Lib/Starcode"    // third-party utility library

This keeps library code clearly separated from your own code.


Function Naming Convention

All functions follow a SystemName_ActionName pattern:

  • MapInit_ActivePlayers() — belongs to MapInit system
  • Bank_Save_RequestSave(int playerID) — belongs to Bank system, Save subsystem
  • PlayerBoard_UpdatePlayer(int playerID) — belongs to PlayerBoard UI
  • Utility_IsNumber(string input) — belongs to Utilities
  • PartTerran_AreaJunker_Second_Open() — Part + Area + Action

This makes it immediately obvious which file a function lives in.


Recommended File Include Order in main.galaxy

// 1. Third-party libraries (no dependencies)
include "scripts/Lib/Starcode"

// 2. Pure constants (no dependencies)
include "scripts/Enums"

// 3. Global state (depends on Enums for array sizes)
include "scripts/GlobalVariables"

// 4. Secret/config (if any)
include "scripts/secrets"

// 5. Forward declarations (depends on GlobalVariables for types)
include "scripts/Header"

// 6. Shared utilities (may depend on GlobalVariables)
include "scripts/Utilities"

// 7. Templates/data-only files
include "scripts/AchievementsTemplates"

// 8. Feature files alphabetical (depend on headers + globals)
include "scripts/Achievements"
include "scripts/Bank"
include "scripts/Enemy"

// 9. UI files — panels first, coordinator last
include "scripts/UI/UI-HeroPanel"
include "scripts/UI/UI-PlayerBoard"
include "scripts/UI/UI-Main"

// 10. Part files — individual parts first, coordinator last
include "scripts/PartTerran"
include "scripts/PartProtoss"
include "scripts/PartZerg"
include "scripts/Parts"

// 11. High-level systems that depend on parts
include "scripts/Hive"
include "scripts/HeroAbilities"
include "scripts/Player"
include "scripts/Collectibles"
include "scripts/HeroSelection"
include "scripts/Tutorial"

// 12. Debug and init last
include "scripts/Debug"
include "scripts/MapInit"

static Variables for Trigger Parameters

When a trigger needs to pass data to its handler function (since trigger functions take no parameters), use static file-scope variables as a parameter register:

// In Utilities.galaxy
static int Utility_DelayedTextTagDestroyer_ParamTextTag;
static trigger Utility_DelayedTextTagDestroyer_Trigger;

void Utility_DelayedTextTagCreate(text inText, color inColor, point position, playergroup pg, fixed offset) {
    Utility_DelayedTextTagDestroyer_ParamTextTag = TextTagCreate(...);
    TriggerExecute(Utility_DelayedTextTagDestroyer_Trigger, false, false);
}

bool Utility_DelayedTextTagDestroyer(bool testCond, bool runActions) {
    int textTag = Utility_DelayedTextTagDestroyer_ParamTextTag;  // capture immediately
    Wait(3.5, c_timeGame);
    TextTagDestroy(textTag);
    return true;
}

Capture the static into a local variable at the top of the handler before any Wait() calls.


Common Error: "Scri: Script load failed: Function not found"

If the SC2 editor or test map throws:

Scri: Script load failed: Function not found

and the function clearly exists in your .galaxy files, the most common cause is file encoding.

Fix: Save the offending .galaxy file as UTF-8 without BOM (not "UTF-8 with BOM").

In VS Code:

  1. Open the file.
  2. Click the encoding indicator in the bottom-right status bar (e.g. UTF-8 with BOM).
  3. Select "Save with Encoding" -> choose "UTF-8" (no BOM).

The SC2 engine cannot parse files saved with a BOM (EF BB BF) prefix, which causes it to fail to locate any functions defined in that file, even if the code itself is correct.

Install via CLI
npx skills add https://github.com/KimPlaybit/Starcraft-2-Editor-Skills --skill galaxy-code-organization
Repository Details
star Stars 5
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator