wip
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "EncounterJournalMgr.h"
|
||||
|
||||
#include "Chat.h"
|
||||
#include "CommandScript.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace Acore::ChatCommands;
|
||||
|
||||
class encounter_journal_commandscript : public CommandScript
|
||||
{
|
||||
public:
|
||||
encounter_journal_commandscript() : CommandScript("encounter_journal_commandscript") { }
|
||||
|
||||
ChatCommandTable GetCommands() const override
|
||||
{
|
||||
static ChatCommandTable ejCommandTable =
|
||||
{
|
||||
{ "reload", HandleReloadCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "export", HandleExportCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "validate", HandleValidateCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "importloot", HandleImportLootCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "importskeleton", HandleImportSkeletonCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "importall", HandleImportAllCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "info", HandleInfoCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
{
|
||||
{ "ej", ejCommandTable },
|
||||
};
|
||||
return commandTable;
|
||||
}
|
||||
|
||||
static bool HandleReloadCommand(ChatHandler* handler)
|
||||
{
|
||||
sEncounterJournalMgr->Load();
|
||||
handler->PSendSysMessage("Encounter Journal reloaded ({} instances, "
|
||||
"{} encounters, {} sections, {} loot rows).",
|
||||
sEncounterJournalMgr->InstanceCount(),
|
||||
sEncounterJournalMgr->EncounterCount(),
|
||||
sEncounterJournalMgr->SectionCount(),
|
||||
sEncounterJournalMgr->LootCount());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleExportCommand(ChatHandler* handler)
|
||||
{
|
||||
sEncounterJournalMgr->Load();
|
||||
|
||||
std::vector<std::string> paths;
|
||||
std::string error;
|
||||
if (!sEncounterJournalMgr->Export(paths, error))
|
||||
{
|
||||
handler->SendErrorMessage("Encounter Journal export failed: {}", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage("Encounter Journal exported {} file(s):", paths.size());
|
||||
for (std::string const& p : paths)
|
||||
handler->PSendSysMessage(" {}", p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleValidateCommand(ChatHandler* handler)
|
||||
{
|
||||
sEncounterJournalMgr->Load();
|
||||
|
||||
std::vector<std::string> errors;
|
||||
std::size_t count = sEncounterJournalMgr->Validate(errors);
|
||||
if (count == 0)
|
||||
{
|
||||
handler->PSendSysMessage("Encounter Journal: validation OK "
|
||||
"({} instances, {} encounters, {} sections, {} loot rows).",
|
||||
sEncounterJournalMgr->InstanceCount(),
|
||||
sEncounterJournalMgr->EncounterCount(),
|
||||
sEncounterJournalMgr->SectionCount(),
|
||||
sEncounterJournalMgr->LootCount());
|
||||
return true;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage("Encounter Journal: {} issue(s):", count);
|
||||
// Cap output to avoid flooding chat in the in-game case.
|
||||
std::size_t const limit = 50;
|
||||
for (std::size_t i = 0; i < errors.size() && i < limit; ++i)
|
||||
handler->PSendSysMessage(" {}", errors[i]);
|
||||
if (errors.size() > limit)
|
||||
handler->PSendSysMessage(" ... and {} more (run from console for full list)",
|
||||
errors.size() - limit);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleImportLootCommand(ChatHandler* handler)
|
||||
{
|
||||
sEncounterJournalMgr->Load();
|
||||
|
||||
std::vector<std::string> warnings;
|
||||
std::size_t inserted = sEncounterJournalMgr->ImportLoot(warnings);
|
||||
|
||||
handler->PSendSysMessage(
|
||||
"Encounter Journal loot import: inserted {} row(s).", inserted);
|
||||
if (!warnings.empty())
|
||||
{
|
||||
handler->PSendSysMessage("{} warning(s):", warnings.size());
|
||||
std::size_t const limit = 25;
|
||||
for (std::size_t i = 0; i < warnings.size() && i < limit; ++i)
|
||||
handler->PSendSysMessage(" {}", warnings[i]);
|
||||
if (warnings.size() > limit)
|
||||
handler->PSendSysMessage(" ... and {} more",
|
||||
warnings.size() - limit);
|
||||
}
|
||||
if (inserted > 0)
|
||||
handler->PSendSysMessage("Reload the manager (.ej reload) to "
|
||||
"see the new rows in subsequent .ej export/validate.");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleImportSkeletonCommand(ChatHandler* handler)
|
||||
{
|
||||
sEncounterJournalMgr->Load();
|
||||
|
||||
MoonWell::EncounterJournal::Mgr::SkeletonCounts c;
|
||||
std::vector<std::string> warnings;
|
||||
std::size_t total = sEncounterJournalMgr->ImportSkeleton(c, warnings);
|
||||
|
||||
handler->PSendSysMessage(
|
||||
"Encounter Journal skeleton import: {} new rows.", total);
|
||||
handler->PSendSysMessage(
|
||||
" tiers={} instances={} tier_instances={}",
|
||||
c.tiers, c.instances, c.tierInstances);
|
||||
handler->PSendSysMessage(
|
||||
" encounters={} creatures={} sections={}",
|
||||
c.encounters, c.creatures, c.sections);
|
||||
|
||||
if (!warnings.empty())
|
||||
{
|
||||
handler->PSendSysMessage("{} warning(s):", warnings.size());
|
||||
std::size_t const limit = 25;
|
||||
for (std::size_t i = 0; i < warnings.size() && i < limit; ++i)
|
||||
handler->PSendSysMessage(" {}", warnings[i]);
|
||||
if (warnings.size() > limit)
|
||||
handler->PSendSysMessage(" ... and {} more",
|
||||
warnings.size() - limit);
|
||||
}
|
||||
if (total > 0)
|
||||
handler->PSendSysMessage("Run .ej importloot next, then .ej export.");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleImportAllCommand(ChatHandler* handler)
|
||||
{
|
||||
HandleImportSkeletonCommand(handler);
|
||||
sEncounterJournalMgr->Load();
|
||||
return HandleImportLootCommand(handler);
|
||||
}
|
||||
|
||||
static bool HandleInfoCommand(ChatHandler* handler)
|
||||
{
|
||||
handler->PSendSysMessage("Encounter Journal cache:");
|
||||
handler->PSendSysMessage(" tiers: {}", sEncounterJournalMgr->TierCount());
|
||||
handler->PSendSysMessage(" instances: {}", sEncounterJournalMgr->InstanceCount());
|
||||
handler->PSendSysMessage(" encounters: {}", sEncounterJournalMgr->EncounterCount());
|
||||
handler->PSendSysMessage(" creatures: {}", sEncounterJournalMgr->CreatureCount());
|
||||
handler->PSendSysMessage(" sections: {}", sEncounterJournalMgr->SectionCount());
|
||||
handler->PSendSysMessage(" loot rows: {}", sEncounterJournalMgr->LootCount());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_encounter_journal_commands()
|
||||
{
|
||||
new encounter_journal_commandscript();
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "EncounterJournalMgr.h"
|
||||
#include "Log.h"
|
||||
#include "ScriptMgr.h"
|
||||
|
||||
void AddSC_encounter_journal_commands();
|
||||
|
||||
class encounter_journal_world_script : public WorldScript
|
||||
{
|
||||
public:
|
||||
encounter_journal_world_script()
|
||||
: WorldScript("encounter_journal_world_script",
|
||||
{ WORLDHOOK_ON_LOAD_CUSTOM_DATABASE_TABLE })
|
||||
{ }
|
||||
|
||||
void OnLoadCustomDatabaseTable() override
|
||||
{
|
||||
if (!sConfigMgr->GetOption<bool>("EncounterJournal.Enable", true))
|
||||
{
|
||||
LOG_INFO("server.loading", ">> mod-encounter-journal disabled "
|
||||
"via EncounterJournal.Enable=0.");
|
||||
return;
|
||||
}
|
||||
|
||||
sEncounterJournalMgr->Load();
|
||||
|
||||
if (sConfigMgr->GetOption<bool>("EncounterJournal.ImportOnStart", false))
|
||||
{
|
||||
MoonWell::EncounterJournal::Mgr::SkeletonCounts sc;
|
||||
std::vector<std::string> warnings;
|
||||
std::size_t inserted =
|
||||
sEncounterJournalMgr->ImportSkeleton(sc, warnings);
|
||||
LOG_INFO("server.loading",
|
||||
">> mod-encounter-journal: ImportOnStart added {} skeleton rows "
|
||||
"(tiers={}, instances={}, encounters={}, creatures={}, sections={}).",
|
||||
inserted, sc.tiers, sc.instances, sc.encounters,
|
||||
sc.creatures, sc.sections);
|
||||
|
||||
std::vector<std::string> lootWarn;
|
||||
std::size_t lootRows = sEncounterJournalMgr->ImportLoot(lootWarn);
|
||||
LOG_INFO("server.loading",
|
||||
">> mod-encounter-journal: ImportOnStart added {} loot rows.",
|
||||
lootRows);
|
||||
|
||||
sEncounterJournalMgr->Load();
|
||||
}
|
||||
|
||||
if (!sConfigMgr->GetOption<bool>("EncounterJournal.ExportOnStart", false))
|
||||
return;
|
||||
|
||||
std::vector<std::string> paths;
|
||||
std::string error;
|
||||
if (!sEncounterJournalMgr->Export(paths, error))
|
||||
{
|
||||
LOG_ERROR("server.loading",
|
||||
">> mod-encounter-journal: ExportOnStart failed: {}", error);
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::string const& p : paths)
|
||||
LOG_INFO("server.loading",
|
||||
">> mod-encounter-journal: wrote {}", p);
|
||||
}
|
||||
};
|
||||
|
||||
void Addmod_encounter_journalScripts()
|
||||
{
|
||||
if (!sConfigMgr->GetOption<bool>("EncounterJournal.Enable", true))
|
||||
return;
|
||||
|
||||
new encounter_journal_world_script();
|
||||
AddSC_encounter_journal_commands();
|
||||
LOG_INFO("server.loading",
|
||||
">> mod-encounter-journal scripts registered (.ej commands available).");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef MOONWELL_ENCOUNTER_JOURNAL_MGR_H
|
||||
#define MOONWELL_ENCOUNTER_JOURNAL_MGR_H
|
||||
|
||||
#include "Define.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace MoonWell::EncounterJournal
|
||||
{
|
||||
struct Tier
|
||||
{
|
||||
uint32 id = 0;
|
||||
std::string name;
|
||||
uint32 flags = 0;
|
||||
int32 sortOrder = 0;
|
||||
};
|
||||
|
||||
struct Instance
|
||||
{
|
||||
uint32 id = 0;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string buttonIcon;
|
||||
std::string smallButtonIcon;
|
||||
std::string background;
|
||||
std::string loreBackground;
|
||||
uint32 mapId = 0;
|
||||
uint32 areaId = 0;
|
||||
uint32 worldMapAreaId = 0;
|
||||
uint32 flags = 0;
|
||||
int32 sortOrder = 0;
|
||||
};
|
||||
|
||||
struct TierInstance
|
||||
{
|
||||
uint32 tierId = 0;
|
||||
uint32 instanceId = 0;
|
||||
int32 sortOrder = 0;
|
||||
};
|
||||
|
||||
struct Encounter
|
||||
{
|
||||
uint32 id = 0;
|
||||
uint32 instanceId = 0;
|
||||
std::string name;
|
||||
std::string description;
|
||||
float mapPositionX = 0.f;
|
||||
float mapPositionY = 0.f;
|
||||
uint32 floorIndex = 1;
|
||||
uint32 worldMapAreaId = 0;
|
||||
uint32 firstSectionId = 0;
|
||||
uint32 difficultyMask = 0;
|
||||
uint32 flags = 0;
|
||||
int32 sortOrder = 0;
|
||||
};
|
||||
|
||||
struct Creature
|
||||
{
|
||||
uint32 id = 0;
|
||||
uint32 encounterId = 0;
|
||||
uint32 creatureId = 0;
|
||||
std::string name;
|
||||
std::string description;
|
||||
uint32 creatureDisplayId = 0;
|
||||
std::string icon;
|
||||
uint32 difficultyMask = 0;
|
||||
int32 sortOrder = 0;
|
||||
};
|
||||
|
||||
struct Section
|
||||
{
|
||||
uint32 id = 0;
|
||||
uint32 encounterId = 0;
|
||||
uint32 parentSectionId = 0;
|
||||
uint32 nextSectionId = 0;
|
||||
uint32 subSectionId = 0;
|
||||
std::string name;
|
||||
std::string description;
|
||||
uint32 creatureDisplayId = 0;
|
||||
uint32 descriptionSpellId = 0;
|
||||
uint32 iconSpellId = 0;
|
||||
std::string icon; // empty when SQL value is NULL or ''
|
||||
uint32 flags = 0;
|
||||
uint32 iconFlags = 0;
|
||||
int32 sortOrder = 0;
|
||||
uint32 type = 0;
|
||||
uint32 difficultyMask = 0;
|
||||
uint32 creatureId = 0;
|
||||
};
|
||||
|
||||
struct Loot
|
||||
{
|
||||
uint32 id = 0;
|
||||
uint32 encounterId = 0;
|
||||
uint32 itemId = 0;
|
||||
uint32 difficultyMask = 0;
|
||||
int32 factionMask = -1;
|
||||
int32 classMask = -1;
|
||||
uint32 flags = 0;
|
||||
int32 sortOrder = 0;
|
||||
};
|
||||
|
||||
class Mgr
|
||||
{
|
||||
public:
|
||||
static Mgr* instance();
|
||||
|
||||
// Loads all custom_ej_* tables into in-memory caches.
|
||||
// Safe to call repeatedly: previous data is dropped first.
|
||||
// Returns true on success (does not fail when tables are empty).
|
||||
bool Load();
|
||||
|
||||
// Drops cached data.
|
||||
void Unload();
|
||||
|
||||
// Writes the configured JSON file (always) and Lua file
|
||||
// (when Export.LuaFile is non-empty). On success @writtenPaths
|
||||
// contains the absolute paths created/overwritten.
|
||||
bool Export(std::vector<std::string>& writtenPaths, std::string& error);
|
||||
|
||||
// Validates relational integrity. Returns the number of errors
|
||||
// found and pushes one human-readable line per error into @errors.
|
||||
std::size_t Validate(std::vector<std::string>& errors);
|
||||
|
||||
// Imports loot rows from creature_loot_template via creature_template.lootid
|
||||
// for each creature card whose creature_id != 0. Reference rows are
|
||||
// followed one level. Existing rows in custom_ej_loot are skipped via
|
||||
// INSERT IGNORE on the unique key. Returns the number of inserted rows.
|
||||
std::size_t ImportLoot(std::vector<std::string>& warnings);
|
||||
|
||||
// Generates skeleton EJ rows from existing AzerothCore tables:
|
||||
// - custom_ej_tier from MapEntry.expansionID buckets
|
||||
// - custom_ej_instance from instance_template + sMapStore (name, type)
|
||||
// - custom_ej_encounter from instance_encounters (creditType=0 only)
|
||||
// - custom_ej_creature from creature_template referenced by encounters
|
||||
// - custom_ej_section one Overview placeholder per encounter
|
||||
// Existing rows are kept (INSERT IGNORE on PK). Returns total inserted
|
||||
// rows; per-table counts are reported via @counts (tier/instance/
|
||||
// tier_instance/encounter/creature/section).
|
||||
struct SkeletonCounts
|
||||
{
|
||||
std::size_t tiers = 0;
|
||||
std::size_t instances = 0;
|
||||
std::size_t tierInstances = 0;
|
||||
std::size_t encounters = 0;
|
||||
std::size_t creatures = 0;
|
||||
std::size_t sections = 0;
|
||||
};
|
||||
std::size_t ImportSkeleton(SkeletonCounts& counts,
|
||||
std::vector<std::string>& warnings);
|
||||
|
||||
std::size_t TierCount() const { return _tiers.size(); }
|
||||
std::size_t InstanceCount() const { return _instances.size(); }
|
||||
std::size_t EncounterCount() const { return _encounters.size(); }
|
||||
std::size_t CreatureCount() const { return _creatures.size(); }
|
||||
std::size_t SectionCount() const { return _sections.size(); }
|
||||
std::size_t LootCount() const { return _loot.size(); }
|
||||
|
||||
private:
|
||||
Mgr() = default;
|
||||
|
||||
std::string ResolveExportPath(std::string const& filename) const;
|
||||
std::string BuildJson() const;
|
||||
std::string BuildLua(std::string const& globalName) const;
|
||||
|
||||
std::vector<Tier> _tiers;
|
||||
std::vector<TierInstance> _tierInstances;
|
||||
std::unordered_map<uint32, Instance> _instances;
|
||||
std::unordered_map<uint32, Encounter> _encounters;
|
||||
std::unordered_map<uint32, Creature> _creatures;
|
||||
std::unordered_map<uint32, Section> _sections;
|
||||
std::vector<Loot> _loot;
|
||||
|
||||
// Sorted access caches (by sort_order, id). Filled at end of Load().
|
||||
std::vector<uint32> _instancesSorted;
|
||||
std::unordered_map<uint32, std::vector<uint32>> _encountersByInstance;
|
||||
std::unordered_map<uint32, std::vector<uint32>> _creaturesByEncounter;
|
||||
std::unordered_map<uint32, std::vector<std::size_t>> _lootByEncounter;
|
||||
std::unordered_map<uint32, std::vector<uint32>> _instancesByTier;
|
||||
};
|
||||
}
|
||||
|
||||
#define sEncounterJournalMgr ::MoonWell::EncounterJournal::Mgr::instance()
|
||||
|
||||
#endif // MOONWELL_ENCOUNTER_JOURNAL_MGR_H
|
||||
Reference in New Issue
Block a user