mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-11 11:33:52 +00:00
feat(editor): add WMAR (Raid Marker Set) — 117th open format
Novel replacement for the hardcoded 8-marker raid set
vanilla WoW shipped (Star/Circle/Diamond/Triangle/Moon/
Square/Cross/Skull) plus world-map pin markers and
5-man party role markers. Each entry binds one marker
slot to its icon resource, single-character chat-overlay
glyph (for "{star}" chat-style links), and priority for
sort order in the marker-picker UI.
Four markerKind values (RaidTarget / WorldMap / Party /
Custom) cover the full marker-system surface. The chat-
overlay displayChar field enables the WoW-canonical
chat shortcuts: "{star}" gets rendered as a star icon
inline in chat, with "*" as the fallback glyph for
non-rich-text contexts (clipboard, log files, mod-
script handlers).
Three preset emitters: makeRaidTargets (8 canonical
raid markers in /raidicon priority order 0..7 with
their iconic colors and glyph mnemonics), makeWorldMap-
Pins (5 world-map pin markers — Pin/Flag/Crosshair/
Question/Compass), makeParty (4 role markers for group-
finder filtering — Tank/Healer/DPS/Caster).
Validator's most novel checks: per-(markerKind,
priority) tuple uniqueness — two markers at same kind+
priority would render in unstable picker UI order. Plus
RaidTarget priority > 7 warns (exceeds canonical 8-slot
/raidicon dispatch range; client keybind macros may not
reach the slot).
Format count 116 -> 117. CLI flag count 1241 -> 1246.
This commit is contained in:
parent
4be543a2ed
commit
42842958df
10 changed files with 714 additions and 0 deletions
296
src/pipeline/wowee_raid_markers.cpp
Normal file
296
src/pipeline/wowee_raid_markers.cpp
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
#include "pipeline/wowee_raid_markers.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kMagic[4] = {'W', 'M', 'A', 'R'};
|
||||
constexpr uint32_t kVersion = 1;
|
||||
|
||||
template <typename T>
|
||||
void writePOD(std::ofstream& os, const T& v) {
|
||||
os.write(reinterpret_cast<const char*>(&v), sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool readPOD(std::ifstream& is, T& v) {
|
||||
is.read(reinterpret_cast<char*>(&v), sizeof(T));
|
||||
return is.gcount() == static_cast<std::streamsize>(sizeof(T));
|
||||
}
|
||||
|
||||
void writeStr(std::ofstream& os, const std::string& s) {
|
||||
uint32_t n = static_cast<uint32_t>(s.size());
|
||||
writePOD(os, n);
|
||||
if (n > 0) os.write(s.data(), n);
|
||||
}
|
||||
|
||||
bool readStr(std::ifstream& is, std::string& s) {
|
||||
uint32_t n = 0;
|
||||
if (!readPOD(is, n)) return false;
|
||||
if (n > (1u << 20)) return false;
|
||||
s.resize(n);
|
||||
if (n > 0) {
|
||||
is.read(s.data(), n);
|
||||
if (is.gcount() != static_cast<std::streamsize>(n)) {
|
||||
s.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string normalizePath(std::string base) {
|
||||
if (base.size() < 5 || base.substr(base.size() - 5) != ".wmar") {
|
||||
base += ".wmar";
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
uint32_t packRgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0xFF) {
|
||||
return (static_cast<uint32_t>(a) << 24) |
|
||||
(static_cast<uint32_t>(b) << 16) |
|
||||
(static_cast<uint32_t>(g) << 8) |
|
||||
static_cast<uint32_t>(r);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const WoweeRaidMarkers::Entry*
|
||||
WoweeRaidMarkers::findById(uint32_t markerId) const {
|
||||
for (const auto& e : entries)
|
||||
if (e.markerId == markerId) return &e;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<const WoweeRaidMarkers::Entry*>
|
||||
WoweeRaidMarkers::findByKind(uint8_t markerKind) const {
|
||||
std::vector<const Entry*> out;
|
||||
for (const auto& e : entries)
|
||||
if (e.markerKind == markerKind) out.push_back(&e);
|
||||
std::sort(out.begin(), out.end(),
|
||||
[](const Entry* a, const Entry* b) {
|
||||
return a->priority < b->priority;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
bool WoweeRaidMarkersLoader::save(const WoweeRaidMarkers& cat,
|
||||
const std::string& basePath) {
|
||||
std::ofstream os(normalizePath(basePath), std::ios::binary);
|
||||
if (!os) return false;
|
||||
os.write(kMagic, 4);
|
||||
writePOD(os, kVersion);
|
||||
writeStr(os, cat.name);
|
||||
uint32_t entryCount = static_cast<uint32_t>(cat.entries.size());
|
||||
writePOD(os, entryCount);
|
||||
for (const auto& e : cat.entries) {
|
||||
writePOD(os, e.markerId);
|
||||
writeStr(os, e.name);
|
||||
writeStr(os, e.description);
|
||||
writePOD(os, e.markerKind);
|
||||
writePOD(os, e.priority);
|
||||
writePOD(os, e.pad0);
|
||||
writePOD(os, e.pad1);
|
||||
writeStr(os, e.iconPath);
|
||||
writeStr(os, e.displayChar);
|
||||
writePOD(os, e.iconColorRGBA);
|
||||
}
|
||||
return os.good();
|
||||
}
|
||||
|
||||
WoweeRaidMarkers WoweeRaidMarkersLoader::load(
|
||||
const std::string& basePath) {
|
||||
WoweeRaidMarkers out;
|
||||
std::ifstream is(normalizePath(basePath), std::ios::binary);
|
||||
if (!is) return out;
|
||||
char magic[4];
|
||||
is.read(magic, 4);
|
||||
if (std::memcmp(magic, kMagic, 4) != 0) return out;
|
||||
uint32_t version = 0;
|
||||
if (!readPOD(is, version) || version != kVersion) return out;
|
||||
if (!readStr(is, out.name)) return out;
|
||||
uint32_t entryCount = 0;
|
||||
if (!readPOD(is, entryCount)) return out;
|
||||
if (entryCount > (1u << 20)) return out;
|
||||
out.entries.resize(entryCount);
|
||||
for (auto& e : out.entries) {
|
||||
if (!readPOD(is, e.markerId)) {
|
||||
out.entries.clear(); return out;
|
||||
}
|
||||
if (!readStr(is, e.name) || !readStr(is, e.description)) {
|
||||
out.entries.clear(); return out;
|
||||
}
|
||||
if (!readPOD(is, e.markerKind) ||
|
||||
!readPOD(is, e.priority) ||
|
||||
!readPOD(is, e.pad0) ||
|
||||
!readPOD(is, e.pad1)) {
|
||||
out.entries.clear(); return out;
|
||||
}
|
||||
if (!readStr(is, e.iconPath) ||
|
||||
!readStr(is, e.displayChar)) {
|
||||
out.entries.clear(); return out;
|
||||
}
|
||||
if (!readPOD(is, e.iconColorRGBA)) {
|
||||
out.entries.clear(); return out;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool WoweeRaidMarkersLoader::exists(const std::string& basePath) {
|
||||
std::ifstream is(normalizePath(basePath), std::ios::binary);
|
||||
return is.good();
|
||||
}
|
||||
|
||||
WoweeRaidMarkers WoweeRaidMarkersLoader::makeRaidTargets(
|
||||
const std::string& catalogName) {
|
||||
using M = WoweeRaidMarkers;
|
||||
WoweeRaidMarkers c;
|
||||
c.name = catalogName;
|
||||
auto add = [&](uint32_t id, const char* name,
|
||||
uint8_t prio, const char* iconPath,
|
||||
const char* glyph, uint32_t color,
|
||||
const char* desc) {
|
||||
M::Entry e;
|
||||
e.markerId = id; e.name = name; e.description = desc;
|
||||
e.markerKind = M::RaidTarget;
|
||||
e.priority = prio;
|
||||
e.iconPath = iconPath;
|
||||
e.displayChar = glyph;
|
||||
e.iconColorRGBA = color;
|
||||
c.entries.push_back(e);
|
||||
};
|
||||
// Canonical 8-marker order from /raidicon command.
|
||||
add(1, "Star", 0,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_1.blp",
|
||||
"*", packRgba(255, 220, 80),
|
||||
"Star (yellow). Mark slot 1. /raidicon star.");
|
||||
add(2, "Circle", 1,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_2.blp",
|
||||
"o", packRgba(255, 130, 60),
|
||||
"Circle (orange). Mark slot 2.");
|
||||
add(3, "Diamond", 2,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_3.blp",
|
||||
"<>", packRgba(180, 100, 240),
|
||||
"Diamond (purple). Mark slot 3 — common "
|
||||
"polymorph CC-target marker.");
|
||||
add(4, "Triangle", 3,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_4.blp",
|
||||
"^", packRgba(80, 220, 80),
|
||||
"Triangle (green). Mark slot 4.");
|
||||
add(5, "Moon", 4,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_5.blp",
|
||||
"(", packRgba(220, 220, 240),
|
||||
"Moon (silver). Mark slot 5 — common Sap CC-"
|
||||
"target marker.");
|
||||
add(6, "Square", 5,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_6.blp",
|
||||
"#", packRgba(80, 130, 240),
|
||||
"Square (blue). Mark slot 6.");
|
||||
add(7, "Cross", 6,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_7.blp",
|
||||
"X", packRgba(220, 60, 60),
|
||||
"Cross (red). Mark slot 7 — universal kill-this-"
|
||||
"first marker.");
|
||||
add(8, "Skull", 7,
|
||||
"Interface\\TargetingFrame\\UI-RaidTargetingIcon_8.blp",
|
||||
"$", packRgba(220, 220, 220),
|
||||
"Skull (white). Mark slot 8 — universal HIGHEST-"
|
||||
"priority kill-target marker.");
|
||||
return c;
|
||||
}
|
||||
|
||||
WoweeRaidMarkers WoweeRaidMarkersLoader::makeWorldMapPins(
|
||||
const std::string& catalogName) {
|
||||
using M = WoweeRaidMarkers;
|
||||
WoweeRaidMarkers c;
|
||||
c.name = catalogName;
|
||||
auto add = [&](uint32_t id, const char* name,
|
||||
uint8_t prio, const char* iconPath,
|
||||
const char* glyph, uint32_t color,
|
||||
const char* desc) {
|
||||
M::Entry e;
|
||||
e.markerId = id; e.name = name; e.description = desc;
|
||||
e.markerKind = M::WorldMap;
|
||||
e.priority = prio;
|
||||
e.iconPath = iconPath;
|
||||
e.displayChar = glyph;
|
||||
e.iconColorRGBA = color;
|
||||
c.entries.push_back(e);
|
||||
};
|
||||
add(100, "Pin", 0,
|
||||
"Interface\\Minimap\\WorldMap\\Pin_Yellow.blp",
|
||||
"P", packRgba(255, 220, 80),
|
||||
"Generic yellow pin. Player-placed waypoint.");
|
||||
add(101, "Flag", 1,
|
||||
"Interface\\Minimap\\WorldMap\\Flag_Red.blp",
|
||||
"F", packRgba(220, 60, 60),
|
||||
"Red flag. Used by raid leaders for "
|
||||
"rendezvous points.");
|
||||
add(102, "Crosshair", 2,
|
||||
"Interface\\Minimap\\WorldMap\\Crosshair.blp",
|
||||
"+", packRgba(180, 180, 180),
|
||||
"Crosshair. Targeting / sniping indicator on "
|
||||
"PvP maps.");
|
||||
add(103, "Question", 3,
|
||||
"Interface\\Minimap\\WorldMap\\Question_Blue.blp",
|
||||
"?", packRgba(140, 200, 255),
|
||||
"Blue question mark. Quest-related point of "
|
||||
"interest.");
|
||||
add(104, "Compass", 4,
|
||||
"Interface\\Minimap\\WorldMap\\Compass.blp",
|
||||
"N", packRgba(220, 220, 240),
|
||||
"Compass rose. North-indicator overlay (rare; "
|
||||
"minimap usually fixes north at top).");
|
||||
return c;
|
||||
}
|
||||
|
||||
WoweeRaidMarkers WoweeRaidMarkersLoader::makeParty(
|
||||
const std::string& catalogName) {
|
||||
using M = WoweeRaidMarkers;
|
||||
WoweeRaidMarkers c;
|
||||
c.name = catalogName;
|
||||
auto add = [&](uint32_t id, const char* name,
|
||||
uint8_t prio, const char* iconPath,
|
||||
const char* glyph, uint32_t color,
|
||||
const char* desc) {
|
||||
M::Entry e;
|
||||
e.markerId = id; e.name = name; e.description = desc;
|
||||
e.markerKind = M::Party;
|
||||
e.priority = prio;
|
||||
e.iconPath = iconPath;
|
||||
e.displayChar = glyph;
|
||||
e.iconColorRGBA = color;
|
||||
c.entries.push_back(e);
|
||||
};
|
||||
add(200, "TankRole", 0,
|
||||
"Interface\\PartyFrame\\Role_Tank.blp",
|
||||
"T", packRgba(60, 100, 200),
|
||||
"Tank role icon. Shown in groupfinder + party "
|
||||
"frame for tank-specced players.");
|
||||
add(201, "HealerRole", 1,
|
||||
"Interface\\PartyFrame\\Role_Healer.blp",
|
||||
"H", packRgba(80, 200, 80),
|
||||
"Healer role icon. Shown for healer-specced "
|
||||
"players.");
|
||||
add(202, "DamageRole", 2,
|
||||
"Interface\\PartyFrame\\Role_Damage.blp",
|
||||
"D", packRgba(220, 80, 80),
|
||||
"DPS role icon (melee + ranged grouped).");
|
||||
add(203, "CasterRole", 3,
|
||||
"Interface\\PartyFrame\\Role_Caster.blp",
|
||||
"C", packRgba(180, 100, 240),
|
||||
"Caster sub-role icon for groupfinder filtering "
|
||||
"(distinct from melee DPS in some custom-server "
|
||||
"configurations).");
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue