mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 01:23:51 +00:00
refactor(chat): decompose into modular architecture, add GM commands, fix protocol
- Extract ChatPanel monolith into 15+ focused modules under ui/chat/ (ChatInput, ChatTabManager, ChatTabCompleter, ChatMarkupParser, ChatMarkupRenderer, ChatCommandRegistry, ChatBubbleManager, ChatSettings, MacroEvaluator, GameStateAdapter, InputModifierAdapter) - Split 2700-line chat_panel_commands.cpp into 11 command modules - Add GM command handling: 190-command data table, dot-prefix interception, tab-completion, /gmhelp with category filter - Fix ChatType enum to match WoW wire protocol (SAY=0x01 not 0x00); values 0x00-0x1B shared across Vanilla/TBC/WotLK - Fix BG_SYSTEM_* values from 82-84 (UB in bitmask shifts) to 0x24-0x26 - Fix infinite Enter key loop after teleport (disable TOGGLE_CHAT repeat, add 2-frame input cooldown) - Add tests: chat_markup_parser, chat_tab_completer, gm_commands, macro_evaluator Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
parent
09c4a9a04a
commit
42f1bb98ea
54 changed files with 7363 additions and 3856 deletions
|
|
@ -570,48 +570,52 @@ public:
|
|||
};
|
||||
|
||||
/**
|
||||
* Chat message types
|
||||
* Chat message types — wire values shared across Vanilla 1.12, TBC 2.4.3, and WotLK 3.3.5a.
|
||||
* Core types (0x00–0x1B) are identical in all expansions.
|
||||
* WotLK adds: ACHIEVEMENT(0x30), GUILD_ACHIEVEMENT(0x31), PARTY_LEADER(0x33).
|
||||
*/
|
||||
enum class ChatType : uint8_t {
|
||||
SAY = 0,
|
||||
PARTY = 1,
|
||||
RAID = 2,
|
||||
GUILD = 3,
|
||||
OFFICER = 4,
|
||||
YELL = 5,
|
||||
WHISPER = 6,
|
||||
WHISPER_INFORM = 7,
|
||||
EMOTE = 8,
|
||||
TEXT_EMOTE = 9,
|
||||
SYSTEM = 10,
|
||||
MONSTER_SAY = 11,
|
||||
MONSTER_YELL = 12,
|
||||
MONSTER_EMOTE = 13,
|
||||
CHANNEL = 14,
|
||||
CHANNEL_JOIN = 15,
|
||||
CHANNEL_LEAVE = 16,
|
||||
CHANNEL_LIST = 17,
|
||||
CHANNEL_NOTICE = 18,
|
||||
CHANNEL_NOTICE_USER = 19,
|
||||
AFK = 20,
|
||||
DND = 21,
|
||||
IGNORED = 22,
|
||||
SKILL = 23,
|
||||
LOOT = 24,
|
||||
BATTLEGROUND = 25,
|
||||
BATTLEGROUND_LEADER = 26,
|
||||
RAID_LEADER = 27,
|
||||
RAID_WARNING = 28,
|
||||
ACHIEVEMENT = 29,
|
||||
GUILD_ACHIEVEMENT = 30,
|
||||
MONSTER_WHISPER = 42,
|
||||
RAID_BOSS_WHISPER = 43,
|
||||
RAID_BOSS_EMOTE = 44,
|
||||
MONSTER_PARTY = 50,
|
||||
// BG/Arena system messages (WoW 3.3.5a — no sender, treated as SYSTEM in display)
|
||||
BG_SYSTEM_NEUTRAL = 82,
|
||||
BG_SYSTEM_ALLIANCE = 83,
|
||||
BG_SYSTEM_HORDE = 84
|
||||
SYSTEM = 0x00,
|
||||
SAY = 0x01,
|
||||
PARTY = 0x02,
|
||||
RAID = 0x03,
|
||||
GUILD = 0x04,
|
||||
OFFICER = 0x05,
|
||||
YELL = 0x06,
|
||||
WHISPER = 0x07,
|
||||
WHISPER_FOREIGN = 0x08,
|
||||
WHISPER_INFORM = 0x09,
|
||||
EMOTE = 0x0A,
|
||||
TEXT_EMOTE = 0x0B,
|
||||
MONSTER_SAY = 0x0C,
|
||||
MONSTER_PARTY = 0x0D,
|
||||
MONSTER_YELL = 0x0E,
|
||||
MONSTER_WHISPER = 0x0F,
|
||||
MONSTER_EMOTE = 0x10,
|
||||
CHANNEL = 0x11,
|
||||
CHANNEL_JOIN = 0x12,
|
||||
CHANNEL_LEAVE = 0x13,
|
||||
CHANNEL_LIST = 0x14,
|
||||
CHANNEL_NOTICE = 0x15,
|
||||
CHANNEL_NOTICE_USER = 0x16,
|
||||
AFK = 0x17,
|
||||
DND = 0x18,
|
||||
IGNORED = 0x19,
|
||||
SKILL = 0x1A,
|
||||
LOOT = 0x1B,
|
||||
// 0x24–0x26: BG system messages
|
||||
BG_SYSTEM_NEUTRAL = 0x24,
|
||||
BG_SYSTEM_ALLIANCE = 0x25,
|
||||
BG_SYSTEM_HORDE = 0x26,
|
||||
RAID_LEADER = 0x27,
|
||||
RAID_WARNING = 0x28,
|
||||
RAID_BOSS_EMOTE = 0x29,
|
||||
RAID_BOSS_WHISPER = 0x2A,
|
||||
BATTLEGROUND = 0x2C,
|
||||
BATTLEGROUND_LEADER = 0x2D,
|
||||
ACHIEVEMENT = 0x30,
|
||||
GUILD_ACHIEVEMENT = 0x31,
|
||||
PARTY_LEADER = 0x33,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
37
include/ui/chat/cast_sequence_tracker.hpp
Normal file
37
include/ui/chat/cast_sequence_tracker.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* /castsequence persistent state — shared across all macros using the same spell list.
|
||||
*
|
||||
* Extracted from chat_panel_commands.cpp static global (Phase 1.5 of chat_panel_ref.md).
|
||||
* Keyed by the normalized (lowercase, comma-joined) spell sequence string.
|
||||
*/
|
||||
class CastSequenceTracker {
|
||||
public:
|
||||
struct State {
|
||||
size_t index = 0;
|
||||
float lastPressSec = 0.0f;
|
||||
uint64_t lastTargetGuid = 0;
|
||||
bool lastInCombat = false;
|
||||
};
|
||||
|
||||
/** Get (or create) the state for a given sequence key. */
|
||||
State& get(const std::string& seqKey) { return states_[seqKey]; }
|
||||
|
||||
/** Reset all tracked sequences. */
|
||||
void clear() { states_.clear(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, State> states_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
46
include/ui/chat/chat_bubble_manager.hpp
Normal file
46
include/ui/chat/chat_bubble_manager.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "ui/ui_services.hpp"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace game { class GameHandler; }
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Manages 3D-projected chat bubbles above entities.
|
||||
*
|
||||
* Extracted from ChatPanel (Phase 1.4 of chat_panel_ref.md).
|
||||
* Owns bubble lifecycle: add, update (tick), render (ImGui overlay).
|
||||
*/
|
||||
class ChatBubbleManager {
|
||||
public:
|
||||
/** Add or replace a bubble for the given entity. */
|
||||
void addBubble(uint64_t senderGuid, const std::string& message, bool isYell);
|
||||
|
||||
/** Render and tick all active bubbles (projects to screen via camera). */
|
||||
void render(game::GameHandler& gameHandler, const UIServices& services);
|
||||
|
||||
/** Register the chat-bubble callback on GameHandler (call once per session). */
|
||||
void setupCallback(game::GameHandler& gameHandler);
|
||||
|
||||
bool empty() const { return bubbles_.empty(); }
|
||||
|
||||
private:
|
||||
struct ChatBubble {
|
||||
uint64_t senderGuid = 0;
|
||||
std::string message;
|
||||
float timeRemaining = 0.0f;
|
||||
float totalDuration = 0.0f;
|
||||
bool isYell = false;
|
||||
};
|
||||
std::vector<ChatBubble> bubbles_;
|
||||
bool callbackSet_ = false;
|
||||
|
||||
static constexpr size_t kMaxBubbles = 10;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
50
include/ui/chat/chat_command_registry.hpp
Normal file
50
include/ui/chat/chat_command_registry.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// ChatCommandRegistry — command registration + dispatch.
|
||||
// Replaces the 500-line if/else chain in sendChatMessage() (Phase 3.1).
|
||||
#pragma once
|
||||
|
||||
#include "ui/chat/i_chat_command.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Registry of all slash commands.
|
||||
*
|
||||
* dispatch() looks up the command by alias and calls execute().
|
||||
* getCompletions() provides tab-completion for /command prefixes.
|
||||
*/
|
||||
class ChatCommandRegistry {
|
||||
public:
|
||||
/** Register a command (takes ownership). All aliases are mapped. */
|
||||
void registerCommand(std::unique_ptr<IChatCommand> cmd);
|
||||
|
||||
/**
|
||||
* Dispatch a slash command.
|
||||
* @param cmdLower lowercase command name (e.g. "cast", "whisper")
|
||||
* @param ctx context with args, gameHandler, services, etc.
|
||||
* @return result indicating if handled and whether to clear input
|
||||
*/
|
||||
ChatCommandResult dispatch(const std::string& cmdLower, ChatCommandContext& ctx);
|
||||
|
||||
/** Get all command aliases matching a prefix (for tab completion). */
|
||||
std::vector<std::string> getCompletions(const std::string& prefix) const;
|
||||
|
||||
/** Get help entries: (alias, helpText) for all registered commands. */
|
||||
std::vector<std::pair<std::string, std::string>> getHelpEntries() const;
|
||||
|
||||
/** Check if a command alias is registered. */
|
||||
bool hasCommand(const std::string& alias) const;
|
||||
|
||||
private:
|
||||
// alias → raw pointer (non-owning, commands_ owns the objects)
|
||||
std::unordered_map<std::string, IChatCommand*> commandMap_;
|
||||
// Ownership of all registered commands
|
||||
std::vector<std::unique_ptr<IChatCommand>> commands_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
37
include/ui/chat/chat_fwd.hpp
Normal file
37
include/ui/chat/chat_fwd.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Forward declarations for the chat subsystem.
|
||||
// Include this instead of the full headers when only pointers/references are needed.
|
||||
// Extracted in Phase 6.6 of chat_panel_ref.md.
|
||||
#pragma once
|
||||
|
||||
namespace wowee {
|
||||
|
||||
namespace game { class GameHandler; }
|
||||
|
||||
namespace ui {
|
||||
|
||||
class ChatPanel;
|
||||
class InventoryScreen;
|
||||
class SpellbookScreen;
|
||||
class QuestLogScreen;
|
||||
|
||||
// Chat subsystem types (under include/ui/chat/)
|
||||
class ChatSettings;
|
||||
class ChatInput;
|
||||
class ChatTabManager;
|
||||
class ChatBubbleManager;
|
||||
class ChatMarkupParser;
|
||||
class ChatMarkupRenderer;
|
||||
class ChatCommandRegistry;
|
||||
class ChatTabCompleter;
|
||||
class CastSequenceTracker;
|
||||
class MacroEvaluator;
|
||||
class IGameState;
|
||||
class IModifierState;
|
||||
class IChatCommand;
|
||||
|
||||
struct ChatCommandContext;
|
||||
struct ChatCommandResult;
|
||||
struct UIServices;
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
95
include/ui/chat/chat_input.hpp
Normal file
95
include/ui/chat/chat_input.hpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Chat input state: buffer, whisper target, sent-history, focus management.
|
||||
*
|
||||
* Extracted from ChatPanel (Phase 1.2 of chat_panel_ref.md).
|
||||
* No UI or network dependencies — pure state management.
|
||||
*/
|
||||
class ChatInput {
|
||||
public:
|
||||
// ---- Buffer access ----
|
||||
char* getBuffer() { return buffer_; }
|
||||
const char* getBuffer() const { return buffer_; }
|
||||
size_t getBufferSize() const { return sizeof(buffer_); }
|
||||
|
||||
char* getWhisperBuffer() { return whisperBuffer_; }
|
||||
const char* getWhisperBuffer() const { return whisperBuffer_; }
|
||||
size_t getWhisperBufferSize() const { return sizeof(whisperBuffer_); }
|
||||
|
||||
void clear() { buffer_[0] = '\0'; }
|
||||
bool isEmpty() const { return buffer_[0] == '\0'; }
|
||||
std::string getText() const { return std::string(buffer_); }
|
||||
void setText(const std::string& text) {
|
||||
strncpy(buffer_, text.c_str(), sizeof(buffer_) - 1);
|
||||
buffer_[sizeof(buffer_) - 1] = '\0';
|
||||
}
|
||||
|
||||
// ---- Whisper target ----
|
||||
std::string getWhisperTarget() const { return std::string(whisperBuffer_); }
|
||||
void setWhisperTarget(const std::string& name) {
|
||||
strncpy(whisperBuffer_, name.c_str(), sizeof(whisperBuffer_) - 1);
|
||||
whisperBuffer_[sizeof(whisperBuffer_) - 1] = '\0';
|
||||
}
|
||||
|
||||
// ---- Sent-message history (Up/Down arrow recall) ----
|
||||
void pushToHistory(const std::string& msg);
|
||||
std::string historyUp();
|
||||
std::string historyDown();
|
||||
void resetHistoryIndex() { historyIdx_ = -1; }
|
||||
int getHistoryIndex() const { return historyIdx_; }
|
||||
const std::vector<std::string>& getSentHistory() const { return sentHistory_; }
|
||||
|
||||
// ---- Focus state ----
|
||||
bool isActive() const { return active_; }
|
||||
void setActive(bool v) { active_ = v; }
|
||||
|
||||
bool shouldFocus() const { return focusRequested_; }
|
||||
void requestFocus() { focusRequested_ = true; }
|
||||
void clearFocusRequest() { focusRequested_ = false; }
|
||||
|
||||
bool shouldMoveCursorToEnd() const { return moveCursorToEnd_; }
|
||||
void requestMoveCursorToEnd() { moveCursorToEnd_ = true; }
|
||||
void clearMoveCursorToEnd() { moveCursorToEnd_ = false; }
|
||||
|
||||
// ---- Chat type selection ----
|
||||
int getSelectedChatType() const { return selectedChatType_; }
|
||||
void setSelectedChatType(int t) { selectedChatType_ = t; }
|
||||
int getLastChatType() const { return lastChatType_; }
|
||||
void setLastChatType(int t) { lastChatType_ = t; }
|
||||
int getSelectedChannelIdx() const { return selectedChannelIdx_; }
|
||||
void setSelectedChannelIdx(int i) { selectedChannelIdx_ = i; }
|
||||
|
||||
// ---- Link insertion (shift-click) ----
|
||||
void insertLink(const std::string& link);
|
||||
|
||||
// ---- Slash key activation ----
|
||||
void activateSlashInput();
|
||||
|
||||
private:
|
||||
char buffer_[512] = "";
|
||||
char whisperBuffer_[256] = "";
|
||||
bool active_ = false;
|
||||
bool focusRequested_ = false;
|
||||
bool moveCursorToEnd_ = false;
|
||||
|
||||
// Chat type dropdown state
|
||||
int selectedChatType_ = 0; // 0=SAY .. 10=CHANNEL
|
||||
int lastChatType_ = 0;
|
||||
int selectedChannelIdx_ = 0;
|
||||
|
||||
// Sent-message history
|
||||
std::vector<std::string> sentHistory_;
|
||||
int historyIdx_ = -1;
|
||||
static constexpr int kMaxHistory = 50;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
55
include/ui/chat/chat_markup_parser.hpp
Normal file
55
include/ui/chat/chat_markup_parser.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Segment types produced by ChatMarkupParser.
|
||||
*
|
||||
* Each segment represents a contiguous piece of a chat message
|
||||
* after WoW markup (|c...|r, |Hitem:...|h[...]|h, URLs) has been decoded.
|
||||
*/
|
||||
enum class SegmentType {
|
||||
Text, // Plain text (render with base message color)
|
||||
ColoredText, // Text with explicit |cAARRGGBB color
|
||||
ItemLink, // |Hitem:ID:...|h[Name]|h
|
||||
SpellLink, // |Hspell:ID:...|h[Name]|h
|
||||
QuestLink, // |Hquest:ID:LEVEL|h[Name]|h
|
||||
AchievementLink, // |Hachievement:ID:...|h[Name]|h
|
||||
Url, // https://... URL
|
||||
};
|
||||
|
||||
/**
|
||||
* A single parsed segment of a chat message.
|
||||
*/
|
||||
struct ChatSegment {
|
||||
SegmentType type = SegmentType::Text;
|
||||
std::string text; // display text (or URL)
|
||||
ImVec4 color = ImVec4(1, 1, 1, 1); // explicit color (for ColoredText / links)
|
||||
uint32_t id = 0; // itemId / spellId / questId / achievementId
|
||||
uint32_t extra = 0; // quest level (for QuestLink)
|
||||
std::string rawLink; // full original markup for shift-click insertion
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses raw WoW-markup text into a flat list of typed segments.
|
||||
*
|
||||
* Extracted from ChatPanel::render() inline lambdas (Phase 2.1 of chat_panel_ref.md).
|
||||
* Pure logic — no ImGui calls, no game-state access. Fully unit-testable.
|
||||
*/
|
||||
class ChatMarkupParser {
|
||||
public:
|
||||
/** Parse a raw chat message string into ordered segments. */
|
||||
std::vector<ChatSegment> parse(const std::string& rawMessage) const;
|
||||
|
||||
/** Parse |cAARRGGBB color code at given position. */
|
||||
static ImVec4 parseWowColor(const std::string& text, size_t pos);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
62
include/ui/chat/chat_markup_renderer.hpp
Normal file
62
include/ui/chat/chat_markup_renderer.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "ui/chat/chat_markup_parser.hpp"
|
||||
#include "ui/ui_services.hpp"
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace game { class GameHandler; }
|
||||
namespace pipeline { class AssetManager; }
|
||||
namespace ui {
|
||||
|
||||
class InventoryScreen;
|
||||
class SpellbookScreen;
|
||||
class QuestLogScreen;
|
||||
|
||||
/**
|
||||
* Context needed by the renderer to display links, tooltips, and icons.
|
||||
*/
|
||||
struct MarkupRenderContext {
|
||||
game::GameHandler* gameHandler = nullptr;
|
||||
InventoryScreen* inventory = nullptr;
|
||||
SpellbookScreen* spellbook = nullptr;
|
||||
QuestLogScreen* questLog = nullptr;
|
||||
pipeline::AssetManager* assetMgr = nullptr;
|
||||
// Spell icon callback — same as ChatPanel::getSpellIcon
|
||||
std::function<VkDescriptorSet(uint32_t, pipeline::AssetManager*)> getSpellIcon;
|
||||
// Chat input buffer for shift-click link insertion
|
||||
char* chatInputBuffer = nullptr;
|
||||
size_t chatInputBufSize = 0;
|
||||
bool* moveCursorToEnd = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders parsed ChatSegments via ImGui.
|
||||
*
|
||||
* Extracted from ChatPanel::render() inline lambdas (Phase 2.2 of chat_panel_ref.md).
|
||||
* Handles: colored text, item/spell/quest/achievement link tooltips+icons,
|
||||
* URL click-to-open, shift-click link insertion.
|
||||
*/
|
||||
class ChatMarkupRenderer {
|
||||
public:
|
||||
/** Render a list of segments with ImGui. baseColor is the message-type color. */
|
||||
void render(const std::vector<ChatSegment>& segments,
|
||||
const ImVec4& baseColor,
|
||||
const MarkupRenderContext& ctx) const;
|
||||
|
||||
/**
|
||||
* Render a full item tooltip for the given item entry.
|
||||
* Extracted from the renderItemLinkTooltip inline lambda.
|
||||
*/
|
||||
static void renderItemTooltip(uint32_t itemEntry,
|
||||
game::GameHandler& gameHandler,
|
||||
InventoryScreen& inventoryScreen,
|
||||
pipeline::AssetManager* assetMgr);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
40
include/ui/chat/chat_settings.hpp
Normal file
40
include/ui/chat/chat_settings.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
// Forward declaration for ImGui (avoid pulling full imgui header)
|
||||
struct ImGuiContext;
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Chat appearance and auto-join settings.
|
||||
*
|
||||
* Extracted from ChatPanel (Phase 1.1 of chat_panel_ref.md).
|
||||
* Pure data + settings UI; no dependency on GameHandler or network.
|
||||
*/
|
||||
struct ChatSettings {
|
||||
// Appearance
|
||||
bool showTimestamps = false;
|
||||
int fontSize = 1; // 0=small, 1=medium, 2=large
|
||||
|
||||
// Auto-join channels
|
||||
bool autoJoinGeneral = true;
|
||||
bool autoJoinTrade = true;
|
||||
bool autoJoinLocalDefense = true;
|
||||
bool autoJoinLFG = true;
|
||||
bool autoJoinLocal = true;
|
||||
|
||||
// Window state
|
||||
bool windowLocked = true;
|
||||
|
||||
/** Reset all chat settings to defaults. */
|
||||
void restoreDefaults();
|
||||
|
||||
/** Render the "Chat" tab inside the Settings window. */
|
||||
void renderSettingsTab(std::function<void()> saveSettingsFn);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
53
include/ui/chat/chat_tab_completer.hpp
Normal file
53
include/ui/chat/chat_tab_completer.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// ChatTabCompleter — cycling tab-completion state machine.
|
||||
// Extracted from scattered vars in ChatPanel (Phase 5.1).
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Stateful tab-completion engine.
|
||||
*
|
||||
* The caller gathers candidates and calls startCompletion() or cycle().
|
||||
* The completer stores the prefix, sorted matches, and current index.
|
||||
*/
|
||||
class ChatTabCompleter {
|
||||
public:
|
||||
/**
|
||||
* Start a new completion session with the given candidates.
|
||||
* Resets the index to 0.
|
||||
*/
|
||||
void startCompletion(const std::string& prefix, std::vector<std::string> candidates);
|
||||
|
||||
/**
|
||||
* Cycle to the next match. Returns true if there are matches.
|
||||
* If the prefix changed, the caller should call startCompletion() instead.
|
||||
*/
|
||||
bool next();
|
||||
|
||||
/** Get the current match, or "" if no matches. */
|
||||
std::string getCurrentMatch() const;
|
||||
|
||||
/** Get the number of matches in the current session. */
|
||||
size_t matchCount() const { return matches_.size(); }
|
||||
|
||||
/** Check if a completion session is active. */
|
||||
bool isActive() const { return matchIdx_ >= 0; }
|
||||
|
||||
/** Get the current prefix. */
|
||||
const std::string& getPrefix() const { return prefix_; }
|
||||
|
||||
/** Reset the completer (e.g. on text change or arrow key). */
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::string prefix_;
|
||||
std::vector<std::string> matches_;
|
||||
int matchIdx_ = -1;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
54
include/ui/chat/chat_tab_manager.hpp
Normal file
54
include/ui/chat/chat_tab_manager.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/world_packets.hpp"
|
||||
#include <imgui.h>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Chat tab definitions, unread tracking, type colors, and type names.
|
||||
*
|
||||
* Extracted from ChatPanel (Phase 1.3 of chat_panel_ref.md).
|
||||
* Owns the tab configuration, unread badge counts, and message filtering.
|
||||
*/
|
||||
class ChatTabManager {
|
||||
public:
|
||||
ChatTabManager();
|
||||
|
||||
// ---- Tab access ----
|
||||
int getTabCount() const { return static_cast<int>(tabs_.size()); }
|
||||
const std::string& getTabName(int idx) const { return tabs_[idx].name; }
|
||||
uint64_t getTabTypeMask(int idx) const { return tabs_[idx].typeMask; }
|
||||
|
||||
// ---- Unread tracking ----
|
||||
int getUnreadCount(int idx) const;
|
||||
void clearUnread(int idx);
|
||||
/** Scan new messages since last call and increment unread counters for non-active tabs. */
|
||||
void updateUnread(const std::deque<game::MessageChatData>& history, int activeTab);
|
||||
|
||||
// ---- Message filtering ----
|
||||
bool shouldShowMessage(const game::MessageChatData& msg, int tabIndex) const;
|
||||
|
||||
// ---- Chat type helpers (static, no state needed) ----
|
||||
static const char* getChatTypeName(game::ChatType type);
|
||||
static ImVec4 getChatTypeColor(game::ChatType type);
|
||||
|
||||
private:
|
||||
struct ChatTab {
|
||||
std::string name;
|
||||
uint64_t typeMask;
|
||||
};
|
||||
std::vector<ChatTab> tabs_;
|
||||
std::vector<int> unread_;
|
||||
size_t seenCount_ = 0;
|
||||
|
||||
void initTabs();
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
43
include/ui/chat/chat_utils.hpp
Normal file
43
include/ui/chat/chat_utils.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/world_packets.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
|
||||
// Forward declaration
|
||||
namespace game { class GameHandler; }
|
||||
|
||||
namespace ui {
|
||||
namespace chat_utils {
|
||||
|
||||
/** Create a system-type chat message (used 15+ times throughout commands). */
|
||||
inline game::MessageChatData makeSystemMessage(const std::string& text) {
|
||||
game::MessageChatData msg;
|
||||
msg.type = game::ChatType::SYSTEM;
|
||||
msg.language = game::ChatLanguage::UNIVERSAL;
|
||||
msg.message = text;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/** Trim leading/trailing whitespace from a string. */
|
||||
inline std::string trim(const std::string& s) {
|
||||
size_t first = s.find_first_not_of(" \t\r\n");
|
||||
if (first == std::string::npos) return "";
|
||||
size_t last = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
/** Convert string to lowercase (returns copy). */
|
||||
inline std::string toLower(std::string s) {
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {
|
||||
return static_cast<char>(std::tolower(c));
|
||||
});
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace chat_utils
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
63
include/ui/chat/game_state_adapter.hpp
Normal file
63
include/ui/chat/game_state_adapter.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// GameStateAdapter — wraps GameHandler + Renderer to implement IGameState.
|
||||
// Phase 4.2 of chat_panel_ref.md.
|
||||
#pragma once
|
||||
|
||||
#include "ui/chat/i_game_state.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace game { class GameHandler; }
|
||||
namespace rendering { class Renderer; }
|
||||
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Concrete adapter from GameHandler + Renderer → IGameState.
|
||||
* Flatten complex entity/aura queries into the simple IGameState interface.
|
||||
*/
|
||||
class GameStateAdapter : public IGameState {
|
||||
public:
|
||||
GameStateAdapter(game::GameHandler& gameHandler, rendering::Renderer* renderer);
|
||||
|
||||
// --- GUIDs ---
|
||||
uint64_t getPlayerGuid() const override;
|
||||
uint64_t getTargetGuid() const override;
|
||||
uint64_t getFocusGuid() const override;
|
||||
uint64_t getPetGuid() const override;
|
||||
uint64_t getMouseoverGuid() const override;
|
||||
|
||||
// --- Player state ---
|
||||
bool isInCombat() const override;
|
||||
bool isMounted() const override;
|
||||
bool isSwimming() const override;
|
||||
bool isFlying() const override;
|
||||
bool isCasting() const override;
|
||||
bool isChanneling() const override;
|
||||
bool isStealthed() const override;
|
||||
bool hasPet() const override;
|
||||
bool isInGroup() const override;
|
||||
bool isInRaid() const override;
|
||||
bool isIndoors() const override;
|
||||
|
||||
// --- Numeric ---
|
||||
uint8_t getActiveTalentSpec() const override;
|
||||
uint32_t getVehicleId() const override;
|
||||
uint32_t getCurrentCastSpellId() const override;
|
||||
|
||||
// --- Spell/aura ---
|
||||
std::string getSpellName(uint32_t spellId) const override;
|
||||
bool hasAuraByName(uint64_t targetGuid, const std::string& spellName,
|
||||
bool wantDebuff) const override;
|
||||
bool hasFormAura() const override;
|
||||
|
||||
// --- Entity queries ---
|
||||
bool entityExists(uint64_t guid) const override;
|
||||
bool entityIsDead(uint64_t guid) const override;
|
||||
bool entityIsHostile(uint64_t guid) const override;
|
||||
|
||||
private:
|
||||
game::GameHandler& gameHandler_;
|
||||
rendering::Renderer* renderer_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
272
include/ui/chat/gm_command_data.hpp
Normal file
272
include/ui/chat/gm_command_data.hpp
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
// GM command metadata — names, security levels, syntax, help text.
|
||||
// Sourced from AzerothCore GM commands wiki.
|
||||
// Used for tab-completion and .gmhelp display; the server handles execution.
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
struct GmCommandEntry {
|
||||
std::string_view name; // e.g. "gm on"
|
||||
uint8_t security; // 0=player, 1=mod, 2=gm, 3=admin, 4=console
|
||||
std::string_view syntax; // e.g. ".gm [on/off]"
|
||||
std::string_view help; // short description
|
||||
};
|
||||
|
||||
// Curated list of the most useful GM commands for a client emulator.
|
||||
// The full AzerothCore list has 500+ commands — this table covers the
|
||||
// ones players/GMs actually type regularly, organized by category.
|
||||
inline constexpr std::array kGmCommands = {
|
||||
|
||||
// ── GM mode & info ──────────────────────────────────────
|
||||
GmCommandEntry{"gm", 1, ".gm [on/off]", "Toggle GM mode or show state"},
|
||||
GmCommandEntry{"gm on", 1, ".gm on", "Enable GM mode"},
|
||||
GmCommandEntry{"gm off", 1, ".gm off", "Disable GM mode"},
|
||||
GmCommandEntry{"gm fly", 2, ".gm fly [on/off]", "Toggle fly mode"},
|
||||
GmCommandEntry{"gm visible", 2, ".gm visible [on/off]", "Toggle GM visibility"},
|
||||
GmCommandEntry{"gm chat", 2, ".gm chat [on/off]", "Toggle GM chat badge"},
|
||||
GmCommandEntry{"gm ingame", 0, ".gm ingame", "List online GMs"},
|
||||
GmCommandEntry{"gm list", 3, ".gm list", "List all GM accounts"},
|
||||
|
||||
// ── Teleportation ───────────────────────────────────────
|
||||
GmCommandEntry{"tele", 1, ".tele #location", "Teleport to location"},
|
||||
GmCommandEntry{"tele group", 2, ".tele group #location", "Teleport group to location"},
|
||||
GmCommandEntry{"tele name", 2, ".tele name $player #location", "Teleport player to location"},
|
||||
GmCommandEntry{"go xyz", 1, ".go xyz #x #y [#z [#map [#o]]]", "Teleport to coordinates"},
|
||||
GmCommandEntry{"go creature", 1, ".go creature #guid", "Teleport to creature by GUID"},
|
||||
GmCommandEntry{"go creature id", 1, ".go creature id #entry", "Teleport to creature by entry"},
|
||||
GmCommandEntry{"go gameobject", 1, ".go gameobject #guid", "Teleport to gameobject"},
|
||||
GmCommandEntry{"go graveyard", 1, ".go graveyard #id", "Teleport to graveyard"},
|
||||
GmCommandEntry{"go taxinode", 1, ".go taxinode #id", "Teleport to taxinode"},
|
||||
GmCommandEntry{"go trigger", 1, ".go trigger #id", "Teleport to areatrigger"},
|
||||
GmCommandEntry{"go zonexy", 1, ".go zonexy #x #y [#zone]", "Teleport to zone coordinates"},
|
||||
GmCommandEntry{"appear", 1, ".appear $player", "Teleport to player"},
|
||||
GmCommandEntry{"summon", 2, ".summon $player", "Summon player to you"},
|
||||
GmCommandEntry{"groupsummon", 2, ".groupsummon $player", "Summon player and group"},
|
||||
GmCommandEntry{"recall", 2, ".recall [$player]", "Return to pre-teleport location"},
|
||||
GmCommandEntry{"unstuck", 2, ".unstuck $player [inn/graveyard]", "Unstuck player"},
|
||||
GmCommandEntry{"gps", 1, ".gps", "Show current position info"},
|
||||
|
||||
// ── Character & level ───────────────────────────────────
|
||||
GmCommandEntry{"levelup", 2, ".levelup [$player] [#levels]", "Increase player level"},
|
||||
GmCommandEntry{"character level", 3, ".character level [$player] [#lvl]", "Set character level"},
|
||||
GmCommandEntry{"character rename", 2, ".character rename [$name]", "Flag character for rename"},
|
||||
GmCommandEntry{"character changefaction", 2, ".character changefaction $name", "Flag for faction change"},
|
||||
GmCommandEntry{"character changerace", 2, ".character changerace $name", "Flag for race change"},
|
||||
GmCommandEntry{"character customize", 2, ".character customize [$name]", "Flag for customization"},
|
||||
GmCommandEntry{"character reputation", 2, ".character reputation [$name]", "Show reputation info"},
|
||||
GmCommandEntry{"character titles", 2, ".character titles [$name]", "Show known titles"},
|
||||
GmCommandEntry{"pinfo", 2, ".pinfo [$player]", "Show player account info"},
|
||||
GmCommandEntry{"guid", 2, ".guid", "Show target GUID"},
|
||||
|
||||
// ── Items & inventory ───────────────────────────────────
|
||||
GmCommandEntry{"additem", 2, ".additem #id [#count]", "Add item to inventory"},
|
||||
GmCommandEntry{"additem set", 2, ".additem set #setid", "Add item set to inventory"},
|
||||
|
||||
// ── Spells & auras ──────────────────────────────────────
|
||||
GmCommandEntry{"learn", 2, ".learn #spell [all]", "Learn a spell"},
|
||||
GmCommandEntry{"unlearn", 2, ".unlearn #spell [all]", "Unlearn a spell"},
|
||||
GmCommandEntry{"learn all my class", 2, ".learn all my class", "Learn all class spells"},
|
||||
GmCommandEntry{"learn all my spells", 2, ".learn all my spells", "Learn all available spells"},
|
||||
GmCommandEntry{"learn all my talents", 2, ".learn all my talents", "Learn all talents"},
|
||||
GmCommandEntry{"learn all crafts", 2, ".learn all crafts", "Learn all professions"},
|
||||
GmCommandEntry{"learn all lang", 2, ".learn all lang", "Learn all languages"},
|
||||
GmCommandEntry{"learn all recipes", 2, ".learn all recipes [$prof]", "Learn all recipes"},
|
||||
GmCommandEntry{"cast", 2, ".cast #spell [triggered]", "Cast spell on target"},
|
||||
GmCommandEntry{"cast self", 2, ".cast self #spell", "Cast spell on self"},
|
||||
GmCommandEntry{"aura", 2, ".aura #spell", "Add aura to target"},
|
||||
GmCommandEntry{"unaura", 2, ".unaura #spell", "Remove aura from target"},
|
||||
GmCommandEntry{"cooldown", 2, ".cooldown [#spell]", "Remove cooldowns"},
|
||||
GmCommandEntry{"maxskill", 2, ".maxskill", "Max all skills for target"},
|
||||
GmCommandEntry{"setskill", 2, ".setskill #skill #level [#max]", "Set skill level"},
|
||||
|
||||
// ── Modify stats ────────────────────────────────────────
|
||||
GmCommandEntry{"modify money", 2, ".modify money #amount", "Add/remove money"},
|
||||
GmCommandEntry{"modify hp", 2, ".modify hp #value", "Set current HP"},
|
||||
GmCommandEntry{"modify mana", 2, ".modify mana #value", "Set current mana"},
|
||||
GmCommandEntry{"modify energy", 2, ".modify energy #value", "Set current energy"},
|
||||
GmCommandEntry{"modify speed all", 2, ".modify speed all #rate", "Set all movement speeds"},
|
||||
GmCommandEntry{"modify speed fly", 2, ".modify speed fly #rate", "Set fly speed"},
|
||||
GmCommandEntry{"modify speed walk", 2, ".modify speed walk #rate", "Set walk speed"},
|
||||
GmCommandEntry{"modify speed swim", 2, ".modify speed swim #rate", "Set swim speed"},
|
||||
GmCommandEntry{"modify mount", 2, ".modify mount #id #speed", "Display as mounted"},
|
||||
GmCommandEntry{"modify scale", 2, ".modify scale #rate", "Set model scale"},
|
||||
GmCommandEntry{"modify honor", 2, ".modify honor #amount", "Add honor points"},
|
||||
GmCommandEntry{"modify reputation", 2, ".modify reputation #faction #val", "Set faction reputation"},
|
||||
GmCommandEntry{"modify talentpoints", 2, ".modify talentpoints #amount", "Set talent points"},
|
||||
GmCommandEntry{"modify gender", 2, ".modify gender male/female", "Change gender"},
|
||||
|
||||
// ── Cheats ──────────────────────────────────────────────
|
||||
GmCommandEntry{"cheat god", 2, ".cheat god [on/off]", "Toggle god mode"},
|
||||
GmCommandEntry{"cheat casttime", 2, ".cheat casttime [on/off]", "Toggle no cast time"},
|
||||
GmCommandEntry{"cheat cooldown", 2, ".cheat cooldown [on/off]", "Toggle no cooldowns"},
|
||||
GmCommandEntry{"cheat power", 2, ".cheat power [on/off]", "Toggle no mana cost"},
|
||||
GmCommandEntry{"cheat explore", 2, ".cheat explore #flag", "Reveal/hide all maps"},
|
||||
GmCommandEntry{"cheat taxi", 2, ".cheat taxi on/off", "Toggle all taxi routes"},
|
||||
GmCommandEntry{"cheat waterwalk", 2, ".cheat waterwalk on/off", "Toggle waterwalk"},
|
||||
GmCommandEntry{"cheat status", 2, ".cheat status", "Show active cheats"},
|
||||
|
||||
// ── NPC ─────────────────────────────────────────────────
|
||||
GmCommandEntry{"npc add", 3, ".npc add #entry", "Spawn creature"},
|
||||
GmCommandEntry{"npc delete", 3, ".npc delete [#guid]", "Delete creature"},
|
||||
GmCommandEntry{"npc info", 1, ".npc info", "Show NPC details"},
|
||||
GmCommandEntry{"npc guid", 1, ".npc guid", "Show NPC GUID"},
|
||||
GmCommandEntry{"npc near", 2, ".npc near [#dist]", "List nearby NPCs"},
|
||||
GmCommandEntry{"npc say", 2, ".npc say $message", "Make NPC say text"},
|
||||
GmCommandEntry{"npc yell", 2, ".npc yell $message", "Make NPC yell text"},
|
||||
GmCommandEntry{"npc move", 3, ".npc move [#guid]", "Move NPC to your position"},
|
||||
GmCommandEntry{"npc set level", 3, ".npc set level #level", "Set NPC level"},
|
||||
GmCommandEntry{"npc set model", 3, ".npc set model #displayid", "Set NPC model"},
|
||||
GmCommandEntry{"npc tame", 2, ".npc tame", "Tame selected creature"},
|
||||
|
||||
// ── Game objects ────────────────────────────────────────
|
||||
GmCommandEntry{"gobject add", 3, ".gobject add #entry", "Spawn gameobject"},
|
||||
GmCommandEntry{"gobject delete", 3, ".gobject delete #guid", "Delete gameobject"},
|
||||
GmCommandEntry{"gobject info", 1, ".gobject info [#entry]", "Show gameobject info"},
|
||||
GmCommandEntry{"gobject near", 3, ".gobject near [#dist]", "List nearby objects"},
|
||||
GmCommandEntry{"gobject move", 3, ".gobject move #guid [#x #y #z]", "Move gameobject"},
|
||||
GmCommandEntry{"gobject target", 1, ".gobject target [#id]", "Find nearest gameobject"},
|
||||
GmCommandEntry{"gobject activate", 2, ".gobject activate #guid", "Activate object (door/button)"},
|
||||
|
||||
// ── Combat & death ──────────────────────────────────────
|
||||
GmCommandEntry{"revive", 2, ".revive", "Revive selected/self"},
|
||||
GmCommandEntry{"die", 2, ".die", "Kill selected/self"},
|
||||
GmCommandEntry{"damage", 2, ".damage #amount [#school [#spell]]", "Deal damage to target"},
|
||||
GmCommandEntry{"combatstop", 2, ".combatstop [$player]", "Stop combat for target"},
|
||||
GmCommandEntry{"freeze", 2, ".freeze [$player]", "Freeze player"},
|
||||
GmCommandEntry{"unfreeze", 2, ".unfreeze [$player]", "Unfreeze player"},
|
||||
GmCommandEntry{"dismount", 0, ".dismount", "Dismount if mounted"},
|
||||
GmCommandEntry{"respawn", 2, ".respawn", "Respawn nearby creatures/GOs"},
|
||||
GmCommandEntry{"respawn all", 2, ".respawn all", "Respawn all nearby"},
|
||||
|
||||
// ── Quests ──────────────────────────────────────────────
|
||||
GmCommandEntry{"quest add", 2, ".quest add #id", "Add quest to log"},
|
||||
GmCommandEntry{"quest complete", 2, ".quest complete #id", "Complete quest objectives"},
|
||||
GmCommandEntry{"quest remove", 2, ".quest remove #id", "Remove quest from log"},
|
||||
GmCommandEntry{"quest reward", 2, ".quest reward #id", "Grant quest reward"},
|
||||
GmCommandEntry{"quest status", 2, ".quest status #id [$name]", "Show quest status"},
|
||||
|
||||
// ── Honor & arena ───────────────────────────────────────
|
||||
GmCommandEntry{"honor add", 2, ".honor add #amount", "Add honor points"},
|
||||
GmCommandEntry{"honor update", 2, ".honor update", "Force honor field update"},
|
||||
GmCommandEntry{"achievement add", 2, ".achievement add #id", "Add achievement to target"},
|
||||
|
||||
// ── Group & guild ───────────────────────────────────────
|
||||
GmCommandEntry{"group list", 2, ".group list [$player]", "List group members"},
|
||||
GmCommandEntry{"group revive", 2, ".group revive $player", "Revive group members"},
|
||||
GmCommandEntry{"group disband", 2, ".group disband [$player]", "Disband player's group"},
|
||||
GmCommandEntry{"guild create", 2, ".guild create $leader \"$name\"", "Create guild"},
|
||||
GmCommandEntry{"guild delete", 2, ".guild delete \"$name\"", "Delete guild"},
|
||||
GmCommandEntry{"guild invite", 2, ".guild invite $player \"$guild\"", "Add player to guild"},
|
||||
GmCommandEntry{"guild info", 2, ".guild info", "Show guild info"},
|
||||
|
||||
// ── Lookup & search ─────────────────────────────────────
|
||||
GmCommandEntry{"lookup item", 1, ".lookup item $name", "Search item by name"},
|
||||
GmCommandEntry{"lookup spell", 1, ".lookup spell $name", "Search spell by name"},
|
||||
GmCommandEntry{"lookup spell id", 1, ".lookup spell id #id", "Look up spell by ID"},
|
||||
GmCommandEntry{"lookup creature", 1, ".lookup creature $name", "Search creature by name"},
|
||||
GmCommandEntry{"lookup quest", 1, ".lookup quest $name", "Search quest by name"},
|
||||
GmCommandEntry{"lookup gobject", 1, ".lookup gobject $name", "Search gameobject by name"},
|
||||
GmCommandEntry{"lookup area", 1, ".lookup area $name", "Search area by name"},
|
||||
GmCommandEntry{"lookup taxinode", 1, ".lookup taxinode $name", "Search taxinode by name"},
|
||||
GmCommandEntry{"lookup teleport", 1, ".lookup teleport $name", "Search teleport by name"},
|
||||
GmCommandEntry{"lookup faction", 1, ".lookup faction $name", "Search faction by name"},
|
||||
GmCommandEntry{"lookup title", 1, ".lookup title $name", "Search title by name"},
|
||||
GmCommandEntry{"lookup event", 1, ".lookup event $name", "Search event by name"},
|
||||
GmCommandEntry{"lookup map", 1, ".lookup map $name", "Search map by name"},
|
||||
GmCommandEntry{"lookup skill", 1, ".lookup skill $name", "Search skill by name"},
|
||||
|
||||
// ── Titles ──────────────────────────────────────────────
|
||||
GmCommandEntry{"titles add", 2, ".titles add #id", "Add title to target"},
|
||||
GmCommandEntry{"titles remove", 2, ".titles remove #id", "Remove title from target"},
|
||||
GmCommandEntry{"titles current", 2, ".titles current #id", "Set current title"},
|
||||
|
||||
// ── Morph & display ─────────────────────────────────────
|
||||
GmCommandEntry{"morph", 1, ".morph #displayid", "Change your model"},
|
||||
GmCommandEntry{"morph target", 1, ".morph target #displayid", "Change target model"},
|
||||
GmCommandEntry{"morph mount", 1, ".morph mount #displayid", "Change mount model"},
|
||||
GmCommandEntry{"morph reset", 1, ".morph reset", "Reset target model"},
|
||||
|
||||
// ── Debug & info ────────────────────────────────────────
|
||||
GmCommandEntry{"debug anim", 3, ".debug anim", "Debug animation"},
|
||||
GmCommandEntry{"debug arena", 3, ".debug arena", "Toggle arena debug"},
|
||||
GmCommandEntry{"debug bg", 3, ".debug bg", "Toggle BG debug"},
|
||||
GmCommandEntry{"debug los", 3, ".debug los", "Show line of sight info"},
|
||||
GmCommandEntry{"debug loot", 2, ".debug loot $type $id [#count]", "Simulate loot generation"},
|
||||
GmCommandEntry{"list auras", 1, ".list auras", "List auras on target"},
|
||||
GmCommandEntry{"list creature", 1, ".list creature #id [#max]", "List creature spawns"},
|
||||
GmCommandEntry{"list item", 1, ".list item #id [#max]", "List item locations"},
|
||||
|
||||
// ── Server & system ─────────────────────────────────────
|
||||
GmCommandEntry{"announce", 2, ".announce $message", "Broadcast to all players"},
|
||||
GmCommandEntry{"gmannounce", 2, ".gmannounce $message", "Broadcast to online GMs"},
|
||||
GmCommandEntry{"notify", 2, ".notify $message", "On-screen broadcast"},
|
||||
GmCommandEntry{"server info", 0, ".server info", "Show server version/players"},
|
||||
GmCommandEntry{"server motd", 0, ".server motd", "Show message of the day"},
|
||||
GmCommandEntry{"commands", 0, ".commands", "List available commands"},
|
||||
GmCommandEntry{"help", 0, ".help [$cmd]", "Show command help"},
|
||||
GmCommandEntry{"save", 0, ".save", "Save your character"},
|
||||
GmCommandEntry{"saveall", 2, ".saveall", "Save all characters"},
|
||||
|
||||
// ── Account & bans ──────────────────────────────────────
|
||||
GmCommandEntry{"account", 0, ".account", "Show account info"},
|
||||
GmCommandEntry{"account set gmlevel", 4, ".account set gmlevel $acct #lvl", "Set GM security level"},
|
||||
GmCommandEntry{"ban account", 2, ".ban account $name $time $reason", "Ban account"},
|
||||
GmCommandEntry{"ban character", 2, ".ban character $name $time $reason", "Ban character"},
|
||||
GmCommandEntry{"ban ip", 2, ".ban ip $ip $time $reason", "Ban IP address"},
|
||||
GmCommandEntry{"unban account", 3, ".unban account $name", "Unban account"},
|
||||
GmCommandEntry{"unban character", 3, ".unban character $name", "Unban character"},
|
||||
GmCommandEntry{"unban ip", 3, ".unban ip $ip", "Unban IP address"},
|
||||
GmCommandEntry{"kick", 2, ".kick [$player] [$reason]", "Kick player from world"},
|
||||
GmCommandEntry{"mute", 2, ".mute $player $minutes [$reason]", "Mute player chat"},
|
||||
GmCommandEntry{"unmute", 2, ".unmute [$player]", "Unmute player"},
|
||||
|
||||
// ── Misc ────────────────────────────────────────────────
|
||||
GmCommandEntry{"distance", 3, ".distance", "Distance to selected target"},
|
||||
GmCommandEntry{"wchange", 3, ".wchange #type #grade", "Change weather"},
|
||||
GmCommandEntry{"mailbox", 1, ".mailbox", "Open mailbox"},
|
||||
GmCommandEntry{"played", 0, ".played", "Show time played"},
|
||||
GmCommandEntry{"gear repair", 2, ".gear repair", "Repair all gear"},
|
||||
GmCommandEntry{"gear stats", 0, ".gear stats", "Show avg item level"},
|
||||
GmCommandEntry{"reset talents", 3, ".reset talents [$player]", "Reset talents"},
|
||||
GmCommandEntry{"reset spells", 3, ".reset spells [$player]", "Reset spells"},
|
||||
GmCommandEntry{"pet create", 2, ".pet create", "Create pet from target"},
|
||||
GmCommandEntry{"pet learn", 2, ".pet learn #spell", "Teach spell to pet"},
|
||||
|
||||
// ── Waypoints ───────────────────────────────────────────
|
||||
GmCommandEntry{"wp add", 3, ".wp add", "Add waypoint at your position"},
|
||||
GmCommandEntry{"wp show", 3, ".wp show on/off", "Toggle waypoint display"},
|
||||
GmCommandEntry{"wp load", 3, ".wp load #pathid", "Load path for creature"},
|
||||
GmCommandEntry{"wp unload", 3, ".wp unload", "Unload creature path"},
|
||||
|
||||
// ── Instance ────────────────────────────────────────────
|
||||
GmCommandEntry{"instance listbinds", 1, ".instance listbinds", "Show instance binds"},
|
||||
GmCommandEntry{"instance unbind", 2, ".instance unbind <map|all>", "Clear instance binds"},
|
||||
GmCommandEntry{"instance stats", 1, ".instance stats", "Show instance stats"},
|
||||
|
||||
// ── Events ──────────────────────────────────────────────
|
||||
GmCommandEntry{"event activelist", 2, ".event activelist", "Show active events"},
|
||||
GmCommandEntry{"event start", 2, ".event start #id", "Start event"},
|
||||
GmCommandEntry{"event stop", 2, ".event stop #id", "Stop event"},
|
||||
GmCommandEntry{"event info", 2, ".event info #id", "Show event info"},
|
||||
|
||||
// ── Reload (common) ─────────────────────────────────────
|
||||
GmCommandEntry{"reload all", 3, ".reload all", "Reload all tables"},
|
||||
GmCommandEntry{"reload creature_template", 3, ".reload creature_template #entry", "Reload creature template"},
|
||||
GmCommandEntry{"reload quest_template", 3, ".reload quest_template", "Reload quest templates"},
|
||||
GmCommandEntry{"reload config", 3, ".reload config", "Reload server config"},
|
||||
GmCommandEntry{"reload game_tele", 3, ".reload game_tele", "Reload teleport locations"},
|
||||
|
||||
// ── Ticket ──────────────────────────────────────────────
|
||||
GmCommandEntry{"ticket list", 2, ".ticket list", "List open GM tickets"},
|
||||
GmCommandEntry{"ticket close", 2, ".ticket close #id", "Close ticket"},
|
||||
GmCommandEntry{"ticket delete", 3, ".ticket delete #id", "Delete ticket permanently"},
|
||||
GmCommandEntry{"ticket viewid", 2, ".ticket viewid #id", "View ticket details"},
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
57
include/ui/chat/i_chat_command.hpp
Normal file
57
include/ui/chat/i_chat_command.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// IChatCommand — interface for all slash commands.
|
||||
// Phase 3.1 of chat_panel_ref.md.
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
|
||||
// Forward declarations
|
||||
namespace game { class GameHandler; }
|
||||
namespace ui { struct UIServices; class ChatPanel; }
|
||||
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Context passed to every command's execute() method.
|
||||
* Provides everything a command needs without coupling to ChatPanel.
|
||||
*/
|
||||
struct ChatCommandContext {
|
||||
game::GameHandler& gameHandler;
|
||||
UIServices& services;
|
||||
ChatPanel& panel; // for input buffer access, macro state
|
||||
std::string args; // everything after "/cmd "
|
||||
std::string fullCommand; // the original command name (lowercase)
|
||||
};
|
||||
|
||||
/**
|
||||
* Result returned by a command to tell the dispatcher what to do next.
|
||||
*/
|
||||
struct ChatCommandResult {
|
||||
bool handled = true; // false → command not recognized, fall through
|
||||
bool clearInput = true; // clear the input buffer after execution
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for all chat slash commands.
|
||||
*
|
||||
* Adding a new command = create a class implementing this interface,
|
||||
* register it in ChatCommandRegistry. Zero edits to existing code. (OCP)
|
||||
*/
|
||||
class IChatCommand {
|
||||
public:
|
||||
virtual ~IChatCommand() = default;
|
||||
|
||||
/** Execute the command. */
|
||||
virtual ChatCommandResult execute(ChatCommandContext& ctx) = 0;
|
||||
|
||||
/** Return all aliases for this command (e.g. {"w", "whisper", "tell", "t"}). */
|
||||
virtual std::vector<std::string> aliases() const = 0;
|
||||
|
||||
/** Optional help text shown by /help. */
|
||||
virtual std::string helpText() const { return ""; }
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
63
include/ui/chat/i_game_state.hpp
Normal file
63
include/ui/chat/i_game_state.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// IGameState — abstract interface for game state queries used by macro evaluation.
|
||||
// Allows unit testing with mock state. Phase 4.1 of chat_panel_ref.md.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Read-only view of game state for macro conditional evaluation.
|
||||
*
|
||||
* All entity/aura queries are flattened to simple types so callers
|
||||
* don't need to depend on game::Entity, game::Unit, etc.
|
||||
*/
|
||||
class IGameState {
|
||||
public:
|
||||
virtual ~IGameState() = default;
|
||||
|
||||
// --- GUIDs ---
|
||||
virtual uint64_t getPlayerGuid() const = 0;
|
||||
virtual uint64_t getTargetGuid() const = 0;
|
||||
virtual uint64_t getFocusGuid() const = 0;
|
||||
virtual uint64_t getPetGuid() const = 0;
|
||||
virtual uint64_t getMouseoverGuid() const = 0;
|
||||
|
||||
// --- Player state booleans ---
|
||||
virtual bool isInCombat() const = 0;
|
||||
virtual bool isMounted() const = 0;
|
||||
virtual bool isSwimming() const = 0;
|
||||
virtual bool isFlying() const = 0;
|
||||
virtual bool isCasting() const = 0;
|
||||
virtual bool isChanneling() const = 0;
|
||||
virtual bool isStealthed() const = 0;
|
||||
virtual bool hasPet() const = 0;
|
||||
virtual bool isInGroup() const = 0;
|
||||
virtual bool isInRaid() const = 0;
|
||||
virtual bool isIndoors() const = 0;
|
||||
|
||||
// --- Numeric state ---
|
||||
virtual uint8_t getActiveTalentSpec() const = 0; // 0-based index
|
||||
virtual uint32_t getVehicleId() const = 0;
|
||||
virtual uint32_t getCurrentCastSpellId() const = 0;
|
||||
|
||||
// --- Spell/aura queries ---
|
||||
virtual std::string getSpellName(uint32_t spellId) const = 0;
|
||||
|
||||
/** Check if target (or player if guid==playerGuid) has a buff/debuff by name. */
|
||||
virtual bool hasAuraByName(uint64_t targetGuid, const std::string& spellName,
|
||||
bool wantDebuff) const = 0;
|
||||
|
||||
/** Check if player has a form/stance aura (permanent aura, maxDurationMs == -1). */
|
||||
virtual bool hasFormAura() const = 0;
|
||||
|
||||
// --- Entity queries (flattened, no Entity* exposure) ---
|
||||
virtual bool entityExists(uint64_t guid) const = 0;
|
||||
virtual bool entityIsDead(uint64_t guid) const = 0;
|
||||
virtual bool entityIsHostile(uint64_t guid) const = 0;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
21
include/ui/chat/i_modifier_state.hpp
Normal file
21
include/ui/chat/i_modifier_state.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// IModifierState — abstract interface for keyboard modifier queries.
|
||||
// Allows unit testing macro conditionals without real input system. Phase 4.1.
|
||||
#pragma once
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Read-only view of keyboard modifier state for macro conditional evaluation.
|
||||
*/
|
||||
class IModifierState {
|
||||
public:
|
||||
virtual ~IModifierState() = default;
|
||||
|
||||
virtual bool isShiftHeld() const = 0;
|
||||
virtual bool isCtrlHeld() const = 0;
|
||||
virtual bool isAltHeld() const = 0;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
22
include/ui/chat/input_modifier_adapter.hpp
Normal file
22
include/ui/chat/input_modifier_adapter.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// InputModifierAdapter — wraps core::Input to implement IModifierState.
|
||||
// Phase 4.3 of chat_panel_ref.md.
|
||||
#pragma once
|
||||
|
||||
#include "ui/chat/i_modifier_state.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
/**
|
||||
* Concrete adapter from core::Input → IModifierState.
|
||||
* Reads real keyboard state from SDL.
|
||||
*/
|
||||
class InputModifierAdapter : public IModifierState {
|
||||
public:
|
||||
bool isShiftHeld() const override;
|
||||
bool isCtrlHeld() const override;
|
||||
bool isAltHeld() const override;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
50
include/ui/chat/macro_evaluator.hpp
Normal file
50
include/ui/chat/macro_evaluator.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// MacroEvaluator — WoW macro conditional parser and evaluator.
|
||||
// Extracted from evaluateMacroConditionals() in chat_panel_commands.cpp.
|
||||
// Phase 4.4 of chat_panel_ref.md.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace ui {
|
||||
|
||||
class IGameState;
|
||||
class IModifierState;
|
||||
|
||||
/**
|
||||
* Evaluates WoW-style macro conditional expressions.
|
||||
*
|
||||
* Syntax: [cond1,cond2] Spell1; [cond3] Spell2; DefaultSpell
|
||||
*
|
||||
* The first alternative whose conditions all evaluate true is returned.
|
||||
* If no conditions match, returns "".
|
||||
*
|
||||
* @p targetOverride is set to a specific GUID if [target=X] or [@X]
|
||||
* was in the matching conditions, or left as UINT64_MAX for "use normal target".
|
||||
*/
|
||||
class MacroEvaluator {
|
||||
public:
|
||||
MacroEvaluator(IGameState& gameState, IModifierState& modState);
|
||||
|
||||
/**
|
||||
* Evaluate a macro conditional string.
|
||||
* @param rawArg The conditional text (e.g. "[combat] Spell1; Spell2")
|
||||
* @param targetOverride Output: set to target GUID if specified, or -1
|
||||
* @return The matched argument text, or "" if nothing matched
|
||||
*/
|
||||
std::string evaluate(const std::string& rawArg, uint64_t& targetOverride) const;
|
||||
|
||||
private:
|
||||
/** Evaluate a single condition token (e.g. "combat", "mod:shift", "@focus"). */
|
||||
bool evalCondition(const std::string& cond, uint64_t& tgt) const;
|
||||
|
||||
/** Resolve effective target GUID (follows @/target= overrides). */
|
||||
uint64_t resolveEffectiveTarget(uint64_t tgt) const;
|
||||
|
||||
IGameState& gameState_;
|
||||
IModifierState& modState_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace wowee
|
||||
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
#include "game/game_handler.hpp"
|
||||
#include "ui/ui_services.hpp"
|
||||
#include "ui/chat/chat_settings.hpp"
|
||||
#include "ui/chat/chat_input.hpp"
|
||||
#include "ui/chat/chat_tab_manager.hpp"
|
||||
#include "ui/chat/chat_bubble_manager.hpp"
|
||||
#include "ui/chat/cast_sequence_tracker.hpp"
|
||||
#include "ui/chat/chat_markup_parser.hpp"
|
||||
#include "ui/chat/chat_markup_renderer.hpp"
|
||||
#include "ui/chat/chat_command_registry.hpp"
|
||||
#include "ui/chat/chat_tab_completer.hpp"
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
|
|
@ -69,9 +78,6 @@ public:
|
|||
|
||||
/** Execute a macro body (one line per 'click'). */
|
||||
void executeMacroText(game::GameHandler& gameHandler,
|
||||
InventoryScreen& inventoryScreen,
|
||||
SpellbookScreen& spellbookScreen,
|
||||
QuestLogScreen& questLogScreen,
|
||||
const std::string& macroText);
|
||||
|
||||
// ---- Slash-command side-effects ----
|
||||
|
|
@ -90,25 +96,31 @@ public:
|
|||
/** Return accumulated slash-command flags and reset them. */
|
||||
SlashCommands consumeSlashCommands();
|
||||
|
||||
// ---- Chat settings (read/written by GameScreen save/load & settings tab) ----
|
||||
// ---- Chat settings (delegated to ChatSettings) ----
|
||||
|
||||
bool chatShowTimestamps = false;
|
||||
int chatFontSize = 1; // 0=small, 1=medium, 2=large
|
||||
bool chatAutoJoinGeneral = true;
|
||||
bool chatAutoJoinTrade = true;
|
||||
bool chatAutoJoinLocalDefense = true;
|
||||
bool chatAutoJoinLFG = true;
|
||||
bool chatAutoJoinLocal = true;
|
||||
ChatSettings settings;
|
||||
int activeChatTab = 0;
|
||||
|
||||
// Legacy accessors — forward to settings struct for external code
|
||||
// (GameScreen save/load reads these directly)
|
||||
bool& chatShowTimestamps = settings.showTimestamps;
|
||||
int& chatFontSize = settings.fontSize;
|
||||
bool& chatAutoJoinGeneral = settings.autoJoinGeneral;
|
||||
bool& chatAutoJoinTrade = settings.autoJoinTrade;
|
||||
bool& chatAutoJoinLocalDefense = settings.autoJoinLocalDefense;
|
||||
bool& chatAutoJoinLFG = settings.autoJoinLFG;
|
||||
bool& chatAutoJoinLocal = settings.autoJoinLocal;
|
||||
|
||||
/** Spell icon lookup callback — set by GameScreen each frame before render(). */
|
||||
std::function<VkDescriptorSet(uint32_t, pipeline::AssetManager*)> getSpellIcon;
|
||||
|
||||
/** Render the "Chat" tab inside the Settings window. */
|
||||
void renderSettingsTab(std::function<void()> saveSettingsFn);
|
||||
/** Render the "Chat" tab inside the Settings window (delegates to settings). */
|
||||
void renderSettingsTab(std::function<void()> saveSettingsFn) {
|
||||
settings.renderSettingsTab(std::move(saveSettingsFn));
|
||||
}
|
||||
|
||||
/** Reset all chat settings to defaults. */
|
||||
void restoreDefaults();
|
||||
/** Reset all chat settings to defaults (delegates to settings). */
|
||||
void restoreDefaults() { settings.restoreDefaults(); }
|
||||
|
||||
// UIServices injection (Phase B singleton breaking)
|
||||
void setServices(const UIServices& services) { services_ = services; }
|
||||
|
|
@ -116,14 +128,31 @@ public:
|
|||
/** Replace $g/$G and $n/$N gender/name placeholders in quest/chat text. */
|
||||
std::string replaceGenderPlaceholders(const std::string& text, game::GameHandler& gameHandler);
|
||||
|
||||
// ---- Accessors for command system (Phase 3) ----
|
||||
char* getChatInputBuffer() { return chatInputBuffer_; }
|
||||
size_t getChatInputBufferSize() const { return sizeof(chatInputBuffer_); }
|
||||
char* getWhisperTargetBuffer() { return whisperTargetBuffer_; }
|
||||
size_t getWhisperTargetBufferSize() const { return sizeof(whisperTargetBuffer_); }
|
||||
int getSelectedChatType() const { return selectedChatType_; }
|
||||
void setSelectedChatType(int t) { selectedChatType_ = t; }
|
||||
int getSelectedChannelIdx() const { return selectedChannelIdx_; }
|
||||
bool& macroStopped() { return macroStopped_; }
|
||||
CastSequenceTracker& getCastSeqTracker() { return castSeqTracker_; }
|
||||
SlashCommands& getSlashCmds() { return slashCmds_; }
|
||||
UIServices& getServices() { return services_; }
|
||||
ChatCommandRegistry& getCommandRegistry() { return commandRegistry_; }
|
||||
|
||||
private:
|
||||
// Injected UI services (Phase B singleton breaking)
|
||||
UIServices services_;
|
||||
|
||||
// ---- Chat input state ----
|
||||
// NOTE: These will migrate to ChatInput in Phase 6 (slim ChatPanel).
|
||||
// ChatInput class is ready at include/ui/chat/chat_input.hpp.
|
||||
char chatInputBuffer_[512] = "";
|
||||
char whisperTargetBuffer_[256] = "";
|
||||
bool chatInputActive_ = false;
|
||||
int chatInputCooldown_ = 0; // frames to suppress re-activation after send
|
||||
int selectedChatType_ = 0; // 0=SAY .. 10=CHANNEL
|
||||
int lastChatType_ = 0;
|
||||
int selectedChannelIdx_ = 0;
|
||||
|
|
@ -137,55 +166,44 @@ private:
|
|||
// Macro stop flag
|
||||
bool macroStopped_ = false;
|
||||
|
||||
// Tab-completion state
|
||||
std::string chatTabPrefix_;
|
||||
std::vector<std::string> chatTabMatches_;
|
||||
int chatTabMatchIdx_ = -1;
|
||||
// /castsequence state (delegated to CastSequenceTracker, Phase 1.5)
|
||||
CastSequenceTracker castSeqTracker_;
|
||||
|
||||
// Command registry (Phase 3 — replaces if/else chain)
|
||||
ChatCommandRegistry commandRegistry_;
|
||||
void registerAllCommands();
|
||||
|
||||
// Markup parser + renderer (Phase 2)
|
||||
ChatMarkupParser markupParser_;
|
||||
ChatMarkupRenderer markupRenderer_;
|
||||
|
||||
// Tab-completion (Phase 5 — delegated to ChatTabCompleter)
|
||||
ChatTabCompleter tabCompleter_;
|
||||
|
||||
// Mention notification
|
||||
size_t chatMentionSeenCount_ = 0;
|
||||
|
||||
// ---- Chat tabs ----
|
||||
struct ChatTab {
|
||||
std::string name;
|
||||
uint64_t typeMask;
|
||||
};
|
||||
std::vector<ChatTab> chatTabs_;
|
||||
std::vector<int> chatTabUnread_;
|
||||
size_t chatTabSeenCount_ = 0;
|
||||
|
||||
void initChatTabs();
|
||||
bool shouldShowMessage(const game::MessageChatData& msg, int tabIndex) const;
|
||||
// ---- Chat tabs (delegated to ChatTabManager) ----
|
||||
ChatTabManager tabManager_;
|
||||
|
||||
// ---- Chat window visual state ----
|
||||
bool chatScrolledUp_ = false;
|
||||
bool chatForceScrollToBottom_ = false;
|
||||
bool chatWindowLocked_ = true;
|
||||
// windowLocked is in settings.windowLocked (kept in sync via reference)
|
||||
bool& chatWindowLocked_ = settings.windowLocked;
|
||||
ImVec2 chatWindowPos_ = ImVec2(0.0f, 0.0f);
|
||||
bool chatWindowPosInit_ = false;
|
||||
|
||||
// ---- Chat bubbles ----
|
||||
struct ChatBubble {
|
||||
uint64_t senderGuid = 0;
|
||||
std::string message;
|
||||
float timeRemaining = 0.0f;
|
||||
float totalDuration = 0.0f;
|
||||
bool isYell = false;
|
||||
};
|
||||
std::vector<ChatBubble> chatBubbles_;
|
||||
bool chatBubbleCallbackSet_ = false;
|
||||
// ---- Chat bubbles (delegated to ChatBubbleManager) ----
|
||||
ChatBubbleManager bubbleManager_;
|
||||
|
||||
// ---- Whisper toast state (populated in render, rendered by GameScreen/ToastManager) ----
|
||||
// Whisper scanning lives here because it's tightly coupled to chat history iteration.
|
||||
size_t whisperSeenCount_ = 0;
|
||||
|
||||
// ---- Helpers ----
|
||||
void sendChatMessage(game::GameHandler& gameHandler,
|
||||
InventoryScreen& inventoryScreen,
|
||||
SpellbookScreen& spellbookScreen,
|
||||
QuestLogScreen& questLogScreen);
|
||||
const char* getChatTypeName(game::ChatType type) const;
|
||||
ImVec4 getChatTypeColor(game::ChatType type) const;
|
||||
void sendChatMessage(game::GameHandler& gameHandler);
|
||||
// getChatTypeName / getChatTypeColor now static in ChatTabManager
|
||||
|
||||
// Cached game handler for input callback (set each frame in render)
|
||||
game::GameHandler* cachedGameHandler_ = nullptr;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue