Skip to content

Hero Classes

Borys Stelmakh edited this page Sep 16, 2026 · 1 revision

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.

Contents

The short version

-- 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.

What changes and what stays

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

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.

Choosing a body

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.

Bodies tested in the game

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.

Limits

  • 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.

Undoing a class mod

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 next global.res on top of the existing one, so a removed mod's strings stay. Without the file the bake starts again from the game's own global.res and 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.

How it works

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.

Clone this wiki locally