mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 08:03:50 +00:00
Add whisper support and slash command chat system
- Add WHISPER to chat type dropdown with target name field - Implement comprehensive slash command parsing for all chat channels - Support /w, /whisper, /tell, /t for whispers (with or without target name) - Add /s, /y, /p, /g shortcuts for say, yell, party, guild - Auto-fill whisper target field when switching to WHISPER with player targeted - Allow /w <message> to whisper currently targeted player - Add helpful error messages for invalid whisper usage - Maintain backward compatibility with existing /logout and /invite commands
This commit is contained in:
parent
adbb78b576
commit
3e64d0865d
2 changed files with 192 additions and 51 deletions
|
|
@ -40,8 +40,10 @@ public:
|
||||||
private:
|
private:
|
||||||
// Chat state
|
// Chat state
|
||||||
char chatInputBuffer[512] = "";
|
char chatInputBuffer[512] = "";
|
||||||
|
char whisperTargetBuffer[256] = "";
|
||||||
bool chatInputActive = false;
|
bool chatInputActive = false;
|
||||||
int selectedChatType = 0; // 0=SAY, 1=YELL, 2=PARTY, etc.
|
int selectedChatType = 0; // 0=SAY, 1=YELL, 2=PARTY, 3=GUILD, 4=WHISPER
|
||||||
|
int lastChatType = 0; // Track chat type changes
|
||||||
bool chatInputMoveCursorToEnd = false;
|
bool chatInputMoveCursorToEnd = false;
|
||||||
|
|
||||||
// UI state
|
// UI state
|
||||||
|
|
|
||||||
|
|
@ -442,8 +442,33 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
||||||
ImGui::Text("Type:");
|
ImGui::Text("Type:");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(100);
|
ImGui::SetNextItemWidth(100);
|
||||||
const char* chatTypes[] = { "SAY", "YELL", "PARTY", "GUILD" };
|
const char* chatTypes[] = { "SAY", "YELL", "PARTY", "GUILD", "WHISPER" };
|
||||||
ImGui::Combo("##ChatType", &selectedChatType, chatTypes, 4);
|
ImGui::Combo("##ChatType", &selectedChatType, chatTypes, 5);
|
||||||
|
|
||||||
|
// Auto-fill whisper target when switching to WHISPER mode
|
||||||
|
if (selectedChatType == 4 && lastChatType != 4) {
|
||||||
|
// Just switched to WHISPER mode
|
||||||
|
if (gameHandler.hasTarget()) {
|
||||||
|
auto target = gameHandler.getTarget();
|
||||||
|
if (target && target->getType() == game::ObjectType::PLAYER) {
|
||||||
|
auto player = std::static_pointer_cast<game::Player>(target);
|
||||||
|
if (!player->getName().empty()) {
|
||||||
|
strncpy(whisperTargetBuffer, player->getName().c_str(), sizeof(whisperTargetBuffer) - 1);
|
||||||
|
whisperTargetBuffer[sizeof(whisperTargetBuffer) - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastChatType = selectedChatType;
|
||||||
|
|
||||||
|
// Show whisper target field if WHISPER is selected
|
||||||
|
if (selectedChatType == 4) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("To:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(120);
|
||||||
|
ImGui::InputText("##WhisperTarget", whisperTargetBuffer, sizeof(whisperTargetBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("Message:");
|
ImGui::Text("Message:");
|
||||||
|
|
@ -902,39 +927,128 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) {
|
||||||
void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
||||||
if (strlen(chatInputBuffer) > 0) {
|
if (strlen(chatInputBuffer) > 0) {
|
||||||
std::string input(chatInputBuffer);
|
std::string input(chatInputBuffer);
|
||||||
|
game::ChatType type;
|
||||||
|
std::string message = input;
|
||||||
|
std::string target;
|
||||||
|
|
||||||
// Check for slash command emotes
|
// Check for slash commands
|
||||||
if (input.size() > 1 && input[0] == '/') {
|
if (input.size() > 1 && input[0] == '/') {
|
||||||
std::string command = input.substr(1);
|
std::string command = input.substr(1);
|
||||||
// Convert to lowercase
|
size_t spacePos = command.find(' ');
|
||||||
for (char& c : command) c = std::tolower(c);
|
std::string cmd = (spacePos != std::string::npos) ? command.substr(0, spacePos) : command;
|
||||||
|
|
||||||
if (command == "logout") {
|
// Convert command to lowercase for comparison
|
||||||
|
std::string cmdLower = cmd;
|
||||||
|
for (char& c : cmdLower) c = std::tolower(c);
|
||||||
|
|
||||||
|
// Special commands
|
||||||
|
if (cmdLower == "logout") {
|
||||||
core::Application::getInstance().logoutToLogin();
|
core::Application::getInstance().logoutToLogin();
|
||||||
chatInputBuffer[0] = '\0';
|
chatInputBuffer[0] = '\0';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string emoteText = rendering::Renderer::getEmoteText(command);
|
// /invite command
|
||||||
|
if (cmdLower == "invite" && spacePos != std::string::npos) {
|
||||||
|
std::string targetName = command.substr(spacePos + 1);
|
||||||
|
gameHandler.inviteToGroup(targetName);
|
||||||
|
chatInputBuffer[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chat channel slash commands
|
||||||
|
bool isChannelCommand = false;
|
||||||
|
if (cmdLower == "s" || cmdLower == "say") {
|
||||||
|
type = game::ChatType::SAY;
|
||||||
|
message = (spacePos != std::string::npos) ? command.substr(spacePos + 1) : "";
|
||||||
|
isChannelCommand = true;
|
||||||
|
} else if (cmdLower == "y" || cmdLower == "yell") {
|
||||||
|
type = game::ChatType::YELL;
|
||||||
|
message = (spacePos != std::string::npos) ? command.substr(spacePos + 1) : "";
|
||||||
|
isChannelCommand = true;
|
||||||
|
} else if (cmdLower == "p" || cmdLower == "party") {
|
||||||
|
type = game::ChatType::PARTY;
|
||||||
|
message = (spacePos != std::string::npos) ? command.substr(spacePos + 1) : "";
|
||||||
|
isChannelCommand = true;
|
||||||
|
} else if (cmdLower == "g" || cmdLower == "guild") {
|
||||||
|
type = game::ChatType::GUILD;
|
||||||
|
message = (spacePos != std::string::npos) ? command.substr(spacePos + 1) : "";
|
||||||
|
isChannelCommand = true;
|
||||||
|
} else if (cmdLower == "w" || cmdLower == "whisper" || cmdLower == "tell" || cmdLower == "t") {
|
||||||
|
// Parse: /w [TargetName] message text
|
||||||
|
// If no target name, use current target
|
||||||
|
if (spacePos != std::string::npos) {
|
||||||
|
std::string rest = command.substr(spacePos + 1);
|
||||||
|
size_t msgStart = rest.find(' ');
|
||||||
|
if (msgStart != std::string::npos) {
|
||||||
|
// Has both target and message: /w PlayerName message
|
||||||
|
target = rest.substr(0, msgStart);
|
||||||
|
message = rest.substr(msgStart + 1);
|
||||||
|
type = game::ChatType::WHISPER;
|
||||||
|
isChannelCommand = true;
|
||||||
|
} else {
|
||||||
|
// Only one word after /w - treat as message to current target
|
||||||
|
message = rest;
|
||||||
|
if (gameHandler.hasTarget()) {
|
||||||
|
auto targetEntity = gameHandler.getTarget();
|
||||||
|
if (targetEntity && targetEntity->getType() == game::ObjectType::PLAYER) {
|
||||||
|
auto player = std::static_pointer_cast<game::Player>(targetEntity);
|
||||||
|
target = player->getName();
|
||||||
|
type = game::ChatType::WHISPER;
|
||||||
|
isChannelCommand = true;
|
||||||
|
} else {
|
||||||
|
game::MessageChatData msg;
|
||||||
|
msg.type = game::ChatType::SYSTEM;
|
||||||
|
msg.language = game::ChatLanguage::UNIVERSAL;
|
||||||
|
msg.message = "You must target a player to whisper, or use: /w <player> <message>";
|
||||||
|
gameHandler.addLocalChatMessage(msg);
|
||||||
|
chatInputBuffer[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
game::MessageChatData msg;
|
||||||
|
msg.type = game::ChatType::SYSTEM;
|
||||||
|
msg.language = game::ChatLanguage::UNIVERSAL;
|
||||||
|
msg.message = "No player targeted. Use: /w <player> <message>";
|
||||||
|
gameHandler.addLocalChatMessage(msg);
|
||||||
|
chatInputBuffer[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Just "/w" with no message - show usage
|
||||||
|
game::MessageChatData msg;
|
||||||
|
msg.type = game::ChatType::SYSTEM;
|
||||||
|
msg.language = game::ChatLanguage::UNIVERSAL;
|
||||||
|
msg.message = "Usage: /w <message> (to target) or /w <player> <message>";
|
||||||
|
gameHandler.addLocalChatMessage(msg);
|
||||||
|
chatInputBuffer[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for emote commands
|
||||||
|
if (!isChannelCommand) {
|
||||||
|
std::string emoteText = rendering::Renderer::getEmoteText(cmdLower);
|
||||||
if (!emoteText.empty()) {
|
if (!emoteText.empty()) {
|
||||||
// Play the emote animation
|
// Play the emote animation
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = core::Application::getInstance().getRenderer();
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer->playEmote(command);
|
renderer->playEmote(cmdLower);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build emote message — targeted or untargeted
|
// Build emote message — targeted or untargeted
|
||||||
std::string chatText;
|
std::string chatText;
|
||||||
if (gameHandler.hasTarget()) {
|
if (gameHandler.hasTarget()) {
|
||||||
auto target = gameHandler.getTarget();
|
auto targetEntity = gameHandler.getTarget();
|
||||||
if (target) {
|
if (targetEntity) {
|
||||||
std::string targetName = getEntityName(target);
|
std::string targetName = getEntityName(targetEntity);
|
||||||
chatText = command + " at " + targetName + ".";
|
chatText = cmdLower + " at " + targetName + ".";
|
||||||
} else {
|
} else {
|
||||||
chatText = emoteText;
|
chatText = emoteText;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chatText = command + "."; // First person: "You wave."
|
chatText = cmdLower + "."; // First person: "You wave."
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add local chat message
|
// Add local chat message
|
||||||
|
|
@ -947,27 +1061,52 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
||||||
chatInputBuffer[0] = '\0';
|
chatInputBuffer[0] = '\0';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// /invite command (Phase 4)
|
|
||||||
if (command.size() > 7 && command.substr(0, 7) == "invite ") {
|
// Not a recognized command — fall through and send as normal chat
|
||||||
std::string targetName = input.substr(8);
|
if (!isChannelCommand) {
|
||||||
gameHandler.inviteToGroup(targetName);
|
message = input;
|
||||||
chatInputBuffer[0] = '\0';
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a recognized emote — fall through and send as normal chat
|
// If no valid command found and starts with /, just send as-is
|
||||||
}
|
if (!isChannelCommand && message == input) {
|
||||||
|
// Use the selected chat type from dropdown
|
||||||
game::ChatType type;
|
|
||||||
switch (selectedChatType) {
|
switch (selectedChatType) {
|
||||||
case 0: type = game::ChatType::SAY; break;
|
case 0: type = game::ChatType::SAY; break;
|
||||||
case 1: type = game::ChatType::YELL; break;
|
case 1: type = game::ChatType::YELL; break;
|
||||||
case 2: type = game::ChatType::PARTY; break;
|
case 2: type = game::ChatType::PARTY; break;
|
||||||
case 3: type = game::ChatType::GUILD; break;
|
case 3: type = game::ChatType::GUILD; break;
|
||||||
|
case 4: type = game::ChatType::WHISPER; target = whisperTargetBuffer; break;
|
||||||
default: type = game::ChatType::SAY; break;
|
default: type = game::ChatType::SAY; break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No slash command, use the selected chat type from dropdown
|
||||||
|
switch (selectedChatType) {
|
||||||
|
case 0: type = game::ChatType::SAY; break;
|
||||||
|
case 1: type = game::ChatType::YELL; break;
|
||||||
|
case 2: type = game::ChatType::PARTY; break;
|
||||||
|
case 3: type = game::ChatType::GUILD; break;
|
||||||
|
case 4: type = game::ChatType::WHISPER; target = whisperTargetBuffer; break;
|
||||||
|
default: type = game::ChatType::SAY; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gameHandler.sendChatMessage(type, chatInputBuffer);
|
// Validate whisper has a target
|
||||||
|
if (type == game::ChatType::WHISPER && target.empty()) {
|
||||||
|
game::MessageChatData msg;
|
||||||
|
msg.type = game::ChatType::SYSTEM;
|
||||||
|
msg.language = game::ChatLanguage::UNIVERSAL;
|
||||||
|
msg.message = "You must specify a player name for whisper.";
|
||||||
|
gameHandler.addLocalChatMessage(msg);
|
||||||
|
chatInputBuffer[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't send empty messages
|
||||||
|
if (!message.empty()) {
|
||||||
|
gameHandler.sendChatMessage(type, message, target);
|
||||||
|
}
|
||||||
|
|
||||||
// Clear input
|
// Clear input
|
||||||
chatInputBuffer[0] = '\0';
|
chatInputBuffer[0] = '\0';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue