192 lines
8.2 KiB
Markdown
192 lines
8.2 KiB
Markdown
# mod-encounter-journal
|
|
|
|
Source-of-truth for the **MoonWellClient EncounterJournal** addon. Stores
|
|
all data (tiers, instances, encounters, sections, creatures, loot) in
|
|
`acore_world` under `custom_ej_*` tables and exports them as JSON / Lua
|
|
that the client can consume directly. No DBC dependency.
|
|
|
|
## Layout
|
|
|
|
```text
|
|
mod-encounter-journal/
|
|
├── conf/ module configuration
|
|
├── data/sql/db-world/base/
|
|
│ ├── 01_encounter_journal_schema.sql table definitions
|
|
│ └── 02_encounter_journal_sample.sql sample data (Deadmines, Naxxramas)
|
|
├── data/sql/db-world/updates/ future incremental migrations
|
|
├── docs/encounter_journal_format.md output structure reference
|
|
├── src/ C++ implementation (manager + commands)
|
|
└── tools/json_to_lua.py offline JSON → Lua fallback
|
|
```
|
|
|
|
## Tables
|
|
|
|
| Table | Purpose |
|
|
| --------------------------- | ---------------------------------------------------------- |
|
|
| `custom_ej_tier` | Top-level expansion / content bucket |
|
|
| `custom_ej_instance` | Dungeon / raid header |
|
|
| `custom_ej_tier_instance` | N:M tier ↔ instance |
|
|
| `custom_ej_encounter` | Boss / encounter under an instance |
|
|
| `custom_ej_creature` | Creature card (portrait, name, description) per encounter |
|
|
| `custom_ej_section` | Tree of Overview / Abilities sections |
|
|
| `custom_ej_loot` | Explicit loot table per encounter |
|
|
|
|
References to standard AzerothCore tables:
|
|
|
|
- `custom_ej_instance.map_id` ↔ `instance_template.map`
|
|
- `custom_ej_creature.creature_id` ↔ `creature_template.entry`
|
|
- `custom_ej_loot.item_id` ↔ `item_template.entry`
|
|
- `custom_ej_creature.creature_display_id` is optional and only used by the
|
|
client's 3D portrait tab; pull it from
|
|
`creature_template.modelid1..modelid4` when populating manually.
|
|
|
|
## Bitmasks
|
|
|
|
### `instance.flags`
|
|
|
|
| Bit | Meaning |
|
|
| ------- | ----------------- |
|
|
| `0x000` | dungeon (default) |
|
|
| `0x010` | raid |
|
|
| `0x040` | hide difficulty |
|
|
| `0x800` | open world |
|
|
|
|
### `encounter.difficulty_mask`
|
|
|
|
For dungeons:
|
|
|
|
| Bit | Meaning |
|
|
| ----- | --------------- |
|
|
| `0x1` | normal |
|
|
| `0x2` | heroic |
|
|
| `0x4` | mythic (custom) |
|
|
|
|
For raids:
|
|
|
|
| Bit | Meaning |
|
|
| ----- | --------- |
|
|
| `0x1` | 10 normal |
|
|
| `0x2` | 25 normal |
|
|
| `0x4` | 10 heroic |
|
|
| `0x8` | 25 heroic |
|
|
|
|
### `section.flags`
|
|
|
|
| Bit | Meaning |
|
|
| ------ | ------------ |
|
|
| `0x1` | starts open |
|
|
| `0x2` | heroic only |
|
|
|
|
## Console / GM commands
|
|
|
|
All commands require **administrator** rights, except `info` which is
|
|
gamemaster-only. They run from the in-game chat **and** the worldserver
|
|
console.
|
|
|
|
| Command | What it does |
|
|
| ---------------- | ---------------------------------------------------------------------------- |
|
|
| `.ej info` | Print row counts of the in-memory cache. |
|
|
| `.ej reload` | Re-read all `custom_ej_*` tables. |
|
|
| `.ej validate` | Run integrity checks (see "Validation" below). |
|
|
| `.ej importloot` | Auto-populate `custom_ej_loot` from `creature_loot_template` (skips dupes). |
|
|
| `.ej export` | Write `EncounterJournalData.json` (and `.lua`) to the configured path. |
|
|
|
|
## Configuration
|
|
|
|
`conf/mod_encounter_journal.conf.dist` (rename to `.conf` to override):
|
|
|
|
| Option | Default | Description |
|
|
| ----------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------- |
|
|
| `EncounterJournal.Enable` | `1` | Master switch. |
|
|
| `EncounterJournal.Export.Path` | (current working dir) | Output directory; must exist & be writable. |
|
|
| `EncounterJournal.Export.JsonFile` | `EncounterJournalData.json` | JSON filename. |
|
|
| `EncounterJournal.Export.LuaFile` | `EncounterJournalData.lua` | Lua filename. Empty string skips Lua generation. |
|
|
| `EncounterJournal.Export.LuaGlobal` | `MoonWellEncounterJournalData` | Name of the Lua global the addon reads. |
|
|
| `EncounterJournal.ExportOnStart` | `0` | When `1`, exports JSON/Lua right after startup load (useful for headless docker without TTY). |
|
|
|
|
## Output format
|
|
|
|
The exporter writes the following top-level structure (full per-row layout
|
|
in [`docs/encounter_journal_format.md`](docs/encounter_journal_format.md)):
|
|
|
|
```jsonc
|
|
{
|
|
"tiers": [ /* [id, name, flags, sort_order] */ ],
|
|
"instances": { /* "instance_id": [...] */ },
|
|
"tierInstances": { /* "tier_id": [instance_id, ...] */ },
|
|
"encounters": { /* "instance_id": [[...], [...]] */ },
|
|
"creatures": { /* "encounter_id": [[...], [...]] */ },
|
|
"sections": { /* "section_id": [...] */ },
|
|
"items": { /* "encounter_id": [[...], [...]] */ }
|
|
}
|
|
```
|
|
|
|
Field order inside each row matches the layout the MoonWellClient
|
|
EncounterJournal addon expects, so the Lua file can be loaded directly:
|
|
|
|
```lua
|
|
-- in your addon Init() or once on load
|
|
local data = MoonWellEncounterJournalData
|
|
for _, tier in ipairs(data.tiers) do
|
|
-- tier[1]=id, tier[2]=name, tier[3]=flags, tier[4]=sort_order
|
|
end
|
|
```
|
|
|
|
## Workflow
|
|
|
|
1. **Edit data** — INSERT/UPDATE rows in `custom_ej_*` tables.
|
|
2. **Validate** — `.ej validate` (no errors expected).
|
|
3. **Export** — `.ej export` (writes JSON + Lua to `Export.Path`).
|
|
4. **Ship** — copy `EncounterJournalData.lua` into the
|
|
MoonWellClient EncounterJournal addon directory.
|
|
|
|
For loot, you can shortcut step 1:
|
|
|
|
1. Insert the encounter and a `custom_ej_creature` row with `creature_id`
|
|
set to the boss NPC id.
|
|
2. Run `.ej importloot` — entries from `creature_loot_template` (and one
|
|
level of `reference_loot_template`) are inserted into `custom_ej_loot`
|
|
with `difficulty_mask` copied from the creature card. Existing rows are
|
|
skipped via the unique key.
|
|
3. Manually clean up: drop trash items, set `faction_mask` /
|
|
`class_mask` / `sort_order` where needed.
|
|
4. Re-run `.ej validate` and `.ej export`.
|
|
|
|
## Validation
|
|
|
|
`.ej validate` verifies:
|
|
|
|
- every `tier_instance.tier_id` and `tier_instance.instance_id` exists;
|
|
- every `encounter.instance_id` exists;
|
|
- every `encounter.first_section_id` exists or equals `0`;
|
|
- `encounter.difficulty_mask` only uses bits valid for the parent
|
|
instance type (dungeon → `0x7`, raid → `0xF`);
|
|
- every `creature.encounter_id` exists;
|
|
- every `creature.creature_id` (if non-zero) exists in `creature_template`;
|
|
- every `section.encounter_id` exists;
|
|
- every `section.parent_section_id` / `next_section_id` /
|
|
`sub_section_id` (if non-zero) exists;
|
|
- every `loot.encounter_id` exists;
|
|
- every `loot.item_id` exists in `item_template`;
|
|
- no `loot` duplicates by
|
|
`(encounter_id, item_id, difficulty_mask, faction_mask, class_mask)`.
|
|
|
|
`sort_order` stability is enforced by the schema (column has no
|
|
`AUTO_INCREMENT`/random default, exporter sorts on it).
|
|
|
|
## Offline JSON → Lua
|
|
|
|
If you produce the JSON outside the worldserver (e.g., from a CI job that
|
|
talks to MySQL directly), use the bundled converter:
|
|
|
|
```sh
|
|
python3 modules/mod-encounter-journal/tools/json_to_lua.py \
|
|
--input EncounterJournalData.json \
|
|
--output EncounterJournalData.lua \
|
|
--global MoonWellEncounterJournalData
|
|
```
|
|
|
|
The output uses the same field order, key sort, and global name as
|
|
`.ej export`, so the addon does not care which path produced the file.
|
|
Whitespace / float formatting may differ slightly.
|