mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 11:03:51 +00:00
Novel open replacement for Blizzard's Lock.dbc. The 18th
open format added to the editor. Closes the cross-reference
gap from WGOT.entry.lockId — until now that field pointed
to a format that didn't exist yet.
A lock is a multi-channel security check. Each lock has up
to 5 independent channels; a player can open the lock by
satisfying ANY ONE channel:
• Item — requires a specific key item (WIT cross-ref)
• Lockpick — requires the lockpicking skill at minimum rank
(rogue / engineering profession)
• Spell — requires casting a specific spell
• Damage — can be forced open with attack damage
Cross-references with previously-added formats:
WGOT.entry.lockId -> WLCK.entry.lockId
WLCK.channel.targetId (Item) -> WIT.entry.itemId
WLCK.channel.targetId (Lockpick) -> future WSKL skillId
WLCK.channel.targetId (Spell) -> future WSPL spellId
The starter and dungeon presets' lockIds (1 and 2)
deliberately match WGOT.makeDungeon's iron-door lockId=1
and bandit-strongbox lockId=2, so the demo content stack
already wires together: WSPN spawn -> WGOT object template
-> WLCK lock template -> WIT key items.
Format:
• magic "WLCK", version 1, little-endian
• per lock: lockId / name / flags / 5 fixed channel slots
• per channel: kind / skillRequired / targetId
• all 5 slots written even when unused (kind=None +
zeroed fields), keeping the per-entry size constant for
fast random access
Enums:
• ChannelKind: None / Item / Lockpick / Spell / Damage
• Flags: DestructOnOpen / RespawnOnKey / TrapOnFail
API: WoweeLockLoader::save / load / exists / findById;
presets makeStarter (Iron Door + Wooden Chest), makeDungeon
(matches WGOT cross-references; light/heavy lockpicks +
boss-key-only seal), makeProfessions (4-tier rogue lockpick
progression at ranks 1/100/175/250).
CLI added (5 flags, 521 documented total now):
--gen-locks / --gen-locks-dungeon / --gen-locks-professions
--info-wlck / --validate-wlck
Validator catches: lockId=0 + duplicates, all-None channels
(lock can never open), Item/Spell/Lockpick channels with
targetId=0 (no resource referenced), unknown channel kind,
skillRequired set on non-Lockpick channel (silently ignored
at runtime — flag as warning).
116 lines
4.3 KiB
C++
116 lines
4.3 KiB
C++
#pragma once
|
||
|
||
#include <cstdint>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace wowee {
|
||
namespace pipeline {
|
||
|
||
// Wowee Open Lock Template (.wlck) — novel replacement for
|
||
// Blizzard's Lock.dbc. The 18th open format added to the
|
||
// editor. Closes the cross-reference gap from WGOT.entry.lockId
|
||
// (and the future WIT lockbox subset) — until now those
|
||
// fields pointed to a format that didn't exist yet.
|
||
//
|
||
// A lock is a multi-channel security check. Each lock has up
|
||
// to 5 independent channels; a player can open the lock by
|
||
// satisfying ANY ONE channel. Channels can be:
|
||
// • Item — requires a specific key item (WIT cross-ref)
|
||
// • Lockpick — requires the lockpicking skill at a minimum
|
||
// rank (rogue / engineering profession)
|
||
// • Spell — requires casting a specific spell
|
||
// • Damage — can be forced open with attack damage
|
||
//
|
||
// Cross-references with previously-added formats:
|
||
// WGOT.entry.lockId → WLCK.entry.lockId
|
||
// WLCK.channel.targetId (kind=Item) → WIT.entry.itemId
|
||
// WLCK.channel.targetId (kind=Skill) → future WSKL skillId
|
||
// WLCK.channel.targetId (kind=Spell) → future WSPL spellId
|
||
//
|
||
// Binary layout (little-endian):
|
||
// magic[4] = "WLCK"
|
||
// version (uint32) = current 1
|
||
// nameLen + name (catalog label)
|
||
// entryCount (uint32)
|
||
// entries (each):
|
||
// lockId (uint32)
|
||
// nameLen + name
|
||
// flags (uint32)
|
||
// -- 5 channel slots, all written even when unused; an
|
||
// unused slot has kind=None and zeroed fields.
|
||
// channels[5] × {
|
||
// kind (uint8) + pad[1]
|
||
// skillRequired (uint16)
|
||
// targetId (uint32)
|
||
// }
|
||
struct WoweeLock {
|
||
static constexpr int kChannelSlots = 5;
|
||
|
||
enum ChannelKind : uint8_t {
|
||
ChannelNone = 0,
|
||
ChannelItem = 1, // requires a key item (targetId = WIT itemId)
|
||
ChannelLockpick = 2, // requires lockpicking skill (targetId = skill ID)
|
||
ChannelSpell = 3, // requires casting a spell (targetId = spell ID)
|
||
ChannelDamage = 4, // can be forced open with damage (targetId unused)
|
||
};
|
||
|
||
enum Flags : uint32_t {
|
||
DestructOnOpen = 0x01, // lock destroyed after one successful open
|
||
RespawnOnKey = 0x02, // re-locks itself after key use (timed)
|
||
TrapOnFail = 0x04, // failure triggers a trap (script-handled)
|
||
};
|
||
|
||
struct Channel {
|
||
uint8_t kind = ChannelNone;
|
||
uint16_t skillRequired = 0; // only meaningful for ChannelLockpick
|
||
uint32_t targetId = 0; // item / skill / spell id
|
||
};
|
||
|
||
struct Entry {
|
||
uint32_t lockId = 0;
|
||
std::string name;
|
||
uint32_t flags = 0;
|
||
Channel channels[kChannelSlots] = {};
|
||
};
|
||
|
||
std::string name;
|
||
std::vector<Entry> entries;
|
||
|
||
bool isValid() const { return !entries.empty(); }
|
||
|
||
// Lookup by lockId — nullptr if not present.
|
||
const Entry* findById(uint32_t lockId) const;
|
||
|
||
static const char* channelKindName(uint8_t k);
|
||
};
|
||
|
||
class WoweeLockLoader {
|
||
public:
|
||
static bool save(const WoweeLock& cat,
|
||
const std::string& basePath);
|
||
static WoweeLock load(const std::string& basePath);
|
||
static bool exists(const std::string& basePath);
|
||
|
||
// Preset emitters used by --gen-locks* variants.
|
||
//
|
||
// makeStarter — 2 locks: a basic wooden chest lock
|
||
// (lockId=1, requires no skill, can be
|
||
// forced open) plus a small key-required
|
||
// lockbox (lockId=2). lockId=1 matches
|
||
// WGOT.makeDungeon's iron-door lockId.
|
||
// makeDungeon — 3 dungeon-tier locks: light lockpick
|
||
// (lockId=2 matching WGOT bandit chest),
|
||
// steel chest (heavy lockpick OR specific
|
||
// key), and a quest-key-only seal.
|
||
// makeProfessions — 4 profession-keyed locks: lockpick at
|
||
// ranks 1/100/175/250 covering the
|
||
// classic-tier rogue / engineering
|
||
// progression curve.
|
||
static WoweeLock makeStarter(const std::string& catalogName);
|
||
static WoweeLock makeDungeon(const std::string& catalogName);
|
||
static WoweeLock makeProfessions(const std::string& catalogName);
|
||
};
|
||
|
||
} // namespace pipeline
|
||
} // namespace wowee
|