-
-
Notifications
You must be signed in to change notification settings - Fork 3
Hero Classes
Give a playable class the body of any creature: its model, skin and portraits, with a new name and description on the character selection screen. The class keeps everything else.
Verified on Sacred Gold Steam, build 2.0.2.28 (2006-10-13). All eight class slots were replaced and played live on 2026-09-16.
- The short version
- What changes and what stays
- CM.replace
- Choosing a body
- Bodies tested in the game
- Limits
- Undoing a class mod
- How it works
-- custom/lua/classes/wilbur.lua
local C = require "classes"
local CM = require "classmod"
CM.replace(C.DWARF, {
name = "Wilbur",
info = "Wilbur sets out to make his own way through Ancaria.",
body = 83, -- creature type 83, WILBUR.GRN
parts = false, -- no Dwarf goggles on the new body
})
return {}Start the game and go to character selection: the Dwarf slot is Wilbur. A class
mod is a runtime mod (return {}), so it lives in a folder that mirrors no game
file; the SDK's own class mods are in sdk/custom/lua/classes/.
If the first start after adding or changing a class mod still shows the old
class, restart once more. The game can read Items.pak and global.res before
the bake has written them.
| Changes | Stays the class's own |
|---|---|
| Class name, also the label on character selection | Stats and attributes |
| Description on character selection | Skills and combat arts |
| Body: model and skin | Voice |
| Portraits: the party panel, and the talk, trade and forge windows | Intro and start quests |
| The figure on character selection: drawn with the body's skin, without the class's items | Every class check in the game (the Dwarf's guns and forge, the Daemon's forms, the Vampiress's day and night) |
CM.replace(class, spec)
| Field | Meaning |
|---|---|
class |
The slot: C.SERAPHIM, C.GLADIATOR, C.BATTLEMAGE, C.DARKELF, C.WOODELF, C.VAMPIRESS, C.DWARF, C.DAEMON (from require "classes"), or the key as a string ("DWARF"). |
spec.name |
Class name and character selection label. |
spec.info |
Character selection description. |
spec.body |
Creature type whose body the hero wears. For a class with two hero types, a table per type: the Vampiress is type 6 by day and 7 by night, body = { [6] = 699, [7] = 224 }. |
spec.parts |
false leaves out the item the class always wears in an empty slot: the Battle Mage's cowl, the Seraphim's and Vampiress's hair, the Gladiator's belt, the Dwarf's goggles. Heroes of that class created before may keep theirs as a plain item. |
spec.portrait |
false keeps the class's own portraits. Default: the body's. |
spec.armor |
true keeps drawing worn armor meshes on the body. Default: hidden, because armor made for the hero's model rarely fits another. |
spec.preview_items |
true keeps the items on the character selection figure. Default: removed. |
Hero types per slot: Seraphim 1, Gladiator 2, Battle Mage 3, Dark Elf 4, Wood Elf 5, Vampiress 6 and 7, Dwarf 8, Daemon 9.
sdk/custom/lua/lib/npc.lua lists all 474 creature types. In
M.WILBUR = add(83, "5300", "Wilbur", "") the first number is the type, and the
constant can be used directly: body = require("npc").WILBUR.
Not every model has every animation a hero plays. Where the body has none for a motion slot, the SDK fills it with the body's closest motion, so a body without a cast or a special attack plays something similar. Some creatures move oddly as a result. Humanoid bodies fit best.
Which slot to use for a new character, from the class-check count in the game: the Seraphim slot is the most general (the only body with both cast and special attack animations, and full riding animations), the Gladiator suits pure melee and the Battle Mage a pure caster. The Vampiress, Dwarf and Daemon carry the most class-specific behaviour (forms, guns, flight), and it stays with the slot.
These eight are the mods in sdk/custom/lua/classes/:
| Slot | Becomes | body |
|---|---|---|
| Seraphim | Amazon | 316 |
| Gladiator | Prince Valor | 322 |
| Battle Mage | Alcalata the Wise | 271 |
| Dark Elf | Shalinor the Dark Elf | 326 |
| Wood Elf | Ice Elf Priestess | 141 |
| Vampiress | Baroness Vilya | 699 by day, 224 by night |
| Dwarf | Wilbur | 83 |
| Daemon | Anducar | 182 |
A file in custom/lua/classes/ with the same name as one of them replaces the
SDK's copy. Give each slot one mod.
- New heroes only. A save of the replaced class carries that class's skills, combat arts and quest state; do not load it with the mod installed.
-
Armor is not drawn on the new body unless
armor = true. It is still equipped and still protects. Weapons, rings and wings are drawn. -
Portraits need a case in the game. A creature type without its own small
portrait keeps the default one; the log says
small portrait <- type N ... skipped. In the tested set that is Shalinor (326) in the party panel. - This machine only. Class mods change files and code on the machine they run on; multiplayer with them was not tested.
Delete the mod file, custom/pak/Items.pak and custom/scripts/us/global.res,
then start the game.
- The body swap is written into
Items.pak, and a stale copy keeps serving the old body with no class mod left. - The name and description are written into
global.res, and the bake builds the nextglobal.reson top of the existing one, so a removed mod's strings stay. Without the file the bake starts again from the game's ownglobal.resand adds the strings of the mods that are still there. - The code patches are applied at every start by the mod itself, so they go away with it.
| Part | Mechanism |
|---|---|
| Name, description |
global.res strings through text.lua, at bake time |
| Body | The slot's hero TYPE record in pak/Items.pak (model file, texture, models.pak index) is rewritten into custom/pak/Items.pak, which fs_override serves instead of the game's file |
| Missing motions |
sacred.model_slots fills the body's empty motion slots in the model manager at runtime |
| Portraits | The game's two portrait switches (party panel, talk windows) get the body type's case copied into the hero's cell (sacred.patch_u32_copy) |
| Character selection figure | The preview draws the body with the TYPE record's skin, and its items are removed (sacred.patch_bytes, sacred.patch_u32) |
| Default part | The class's case in the part switch points at "none" (sacred.patch_u32) |
| Worn armor | Not drawn on the hero type (sacred.wear_hide) |
Every code patch runs only after the build check has matched Steam 2.0.2.28, and
only over the exact bytes it expects; anything else is skipped and logged.
lib/classmod.lua holds the addresses and the Items.pak layout.
Getting started
Authoring
- Quests and Dialog Authoring
- Native Quests
- Runtime NPCs
- Hero Classes
- Dialog Text
- Dialog Nodes (catalogue)
The engine's own verbs
Reference