diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index 870e00b7..771bbda8 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -615,7 +615,11 @@ enum class ChatType : uint8_t { MONSTER_WHISPER = 42, RAID_BOSS_WHISPER = 43, RAID_BOSS_EMOTE = 44, - MONSTER_PARTY = 50 + 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 }; /** diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index c1f65f18..8e5bc602 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -7930,10 +7930,10 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { if (ghostStateCallback_) ghostStateCallback_(true); } } - // Determine hostility from faction template for online creatures - if (unit->getFactionTemplate() != 0) { - unit->setHostile(isHostileFaction(unit->getFactionTemplate())); - } + // Determine hostility from faction template for online creatures. + // Always call isHostileFaction — factionTemplate=0 defaults to hostile + // in the lookup rather than silently staying at the struct default (false). + unit->setHostile(isHostileFaction(unit->getFactionTemplate())); // Trigger creature spawn callback for units/players with displayId if (block.objectType == ObjectType::UNIT && unit->getDisplayId() == 0) { LOG_WARNING("[Spawn] UNIT guid=0x", std::hex, block.guid, std::dec, @@ -14287,8 +14287,9 @@ void GameHandler::performGameObjectInteractionNow(uint64_t guid) { // animation/sound and expects the client to request the mail list. bool isMailbox = false; bool chestLike = false; - // Stock-like behavior: GO use opens GO loot context. Keep eager CMSG_LOOT only - // as Classic/Turtle fallback behavior. + // Chest-type game objects (type=3): on all expansions, also send CMSG_LOOT so + // the server opens the loot response. Other harvestable/interactive types rely + // on the server auto-sending SMSG_LOOT_RESPONSE after CMSG_GAMEOBJ_USE. bool shouldSendLoot = isActiveExpansion("classic") || isActiveExpansion("turtle"); if (entity && entity->getType() == ObjectType::GAMEOBJECT) { auto go = std::static_pointer_cast(entity); @@ -14305,6 +14306,8 @@ void GameHandler::performGameObjectInteractionNow(uint64_t guid) { refreshMailList(); } else if (info && info->type == 3) { chestLike = true; + // Type-3 chests require CMSG_LOOT on all expansions (AzerothCore WotLK included) + shouldSendLoot = true; } else if (turtleMode) { // Turtle compatibility: keep eager loot open behavior. shouldSendLoot = true; @@ -14315,21 +14318,19 @@ void GameHandler::performGameObjectInteractionNow(uint64_t guid) { std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); chestLike = (lower.find("chest") != std::string::npos); + if (chestLike) shouldSendLoot = true; } - // For WotLK chest-like gameobjects, report use but let server open loot. - if (!isMailbox && chestLike) { - if (isActiveExpansion("wotlk")) { - network::Packet reportUse(wireOpcode(Opcode::CMSG_GAMEOBJ_REPORT_USE)); - reportUse.writeUInt64(guid); - socket->send(reportUse); - } + // For WotLK chest-like gameobjects, also send CMSG_GAMEOBJ_REPORT_USE. + if (!isMailbox && chestLike && isActiveExpansion("wotlk")) { + network::Packet reportUse(wireOpcode(Opcode::CMSG_GAMEOBJ_REPORT_USE)); + reportUse.writeUInt64(guid); + socket->send(reportUse); } if (shouldSendLoot) { lootTarget(guid); } - // Retry use briefly to survive packet loss/order races. Keep loot retries only - // when we intentionally use eager loot-open mode. - const bool retryLoot = shouldSendLoot && (turtleMode || isActiveExpansion("classic")); + // Retry use briefly to survive packet loss/order races. + const bool retryLoot = shouldSendLoot; const bool retryUse = turtleMode || isActiveExpansion("classic"); if (retryUse || retryLoot) { pendingGameObjectLootRetries_.push_back(PendingLootRetry{guid, 0.15f, 2, retryLoot}); diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index d47c568d..95b518f0 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -1422,6 +1422,14 @@ bool MessageChatParser::parse(network::Packet& packet, MessageChatData& data) { break; } + case ChatType::BG_SYSTEM_NEUTRAL: + case ChatType::BG_SYSTEM_ALLIANCE: + case ChatType::BG_SYSTEM_HORDE: + // BG/Arena system messages — no sender GUID or name field, just message. + // Reclassify as SYSTEM for consistent display. + data.type = ChatType::SYSTEM; + break; + default: // SAY, GUILD, PARTY, YELL, WHISPER, WHISPER_INFORM, RAID, etc. // All have receiverGuid (typically senderGuid repeated) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 1263e359..2e11cfe9 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -1251,8 +1251,25 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) { renderTextWithLinks(fullMsg, color); } } else { - std::string fullMsg = tsPrefix + "[" + std::string(getChatTypeName(msg.type)) + "] " + processedMessage; - renderTextWithLinks(fullMsg, color); + // No sender name. For group/channel types show a bracket prefix; + // for sender-specific types (SAY, YELL, WHISPER, etc.) just show the + // raw message — these are server-side announcements without a speaker. + bool isGroupType = + msg.type == game::ChatType::PARTY || + msg.type == game::ChatType::GUILD || + msg.type == game::ChatType::OFFICER || + msg.type == game::ChatType::RAID || + msg.type == game::ChatType::RAID_LEADER || + msg.type == game::ChatType::RAID_WARNING || + msg.type == game::ChatType::BATTLEGROUND || + msg.type == game::ChatType::BATTLEGROUND_LEADER; + if (isGroupType) { + std::string fullMsg = tsPrefix + "[" + std::string(getChatTypeName(msg.type)) + "] " + processedMessage; + renderTextWithLinks(fullMsg, color); + } else { + // SAY, YELL, WHISPER, unknown BG_SYSTEM_* types, etc. — no prefix + renderTextWithLinks(tsPrefix + processedMessage, color); + } } } @@ -3421,6 +3438,9 @@ const char* GameScreen::getChatTypeName(game::ChatType type) const { case game::ChatType::ACHIEVEMENT: return "Achievement"; case game::ChatType::DND: return "DND"; case game::ChatType::AFK: return "AFK"; + case game::ChatType::BG_SYSTEM_NEUTRAL: + case game::ChatType::BG_SYSTEM_ALLIANCE: + case game::ChatType::BG_SYSTEM_HORDE: return "System"; default: return "Unknown"; } }