mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-31 19:03:54 +00:00
fix: equipment visibility (remove layout verification gate), follow uses run speed
Equipment: removed the visibleItemLayoutVerified_ gate from updateOtherPlayerVisibleItems(). The default WotLK field layout (base=284, stride=2) is correct and should be used immediately. The verification heuristic was silently blocking ALL other-player equipment rendering by queuing for auto-inspect (which doesn't return items in WotLK anyway). Follow: auto-follow now uses run speed (autoRunning) instead of walk speed. Also uses squared distance for the distance checks. Commands: /quit, /exit aliases for /logout; /difficulty normal/heroic/25/25heroic sends CMSG_CHANGEPLAYER_DIFFICULTY.
This commit is contained in:
parent
b366773f29
commit
cccd52b32f
4 changed files with 97 additions and 28 deletions
|
|
@ -560,6 +560,9 @@ public:
|
|||
// Logout commands
|
||||
void requestLogout();
|
||||
void cancelLogout();
|
||||
|
||||
// Instance difficulty
|
||||
void sendSetDifficulty(uint32_t difficulty);
|
||||
bool isLoggingOut() const { return loggingOut_; }
|
||||
float getLogoutCountdown() const { return logoutCountdown_; }
|
||||
|
||||
|
|
|
|||
|
|
@ -13167,6 +13167,18 @@ void GameHandler::cancelLogout() {
|
|||
LOG_INFO("Cancelled logout");
|
||||
}
|
||||
|
||||
void GameHandler::sendSetDifficulty(uint32_t difficulty) {
|
||||
if (!isInWorld()) {
|
||||
LOG_WARNING("Cannot change difficulty: not in world");
|
||||
return;
|
||||
}
|
||||
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_CHANGEPLAYER_DIFFICULTY));
|
||||
packet.writeUInt32(difficulty);
|
||||
socket->send(packet);
|
||||
LOG_INFO("CMSG_CHANGEPLAYER_DIFFICULTY sent: difficulty=", difficulty);
|
||||
}
|
||||
|
||||
void GameHandler::setStandState(uint8_t standState) {
|
||||
if (!isInWorld()) {
|
||||
LOG_WARNING("Cannot change stand state: not in world or not connected");
|
||||
|
|
@ -15190,18 +15202,17 @@ void GameHandler::maybeDetectVisibleItemLayout() {
|
|||
|
||||
void GameHandler::updateOtherPlayerVisibleItems(uint64_t guid, const std::map<uint16_t, uint32_t>& fields) {
|
||||
if (guid == 0 || guid == playerGuid) return;
|
||||
if (visibleItemEntryBase_ < 0 || visibleItemStride_ <= 0) {
|
||||
// Layout not detected yet — queue this player for inspect as fallback.
|
||||
if (socket && state == WorldState::IN_WORLD) {
|
||||
pendingAutoInspect_.insert(guid);
|
||||
LOG_DEBUG("Queued player 0x", std::hex, guid, std::dec, " for auto-inspect (layout not detected)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the current base/stride (defaults are correct for WotLK 3.3.5a: base=284, stride=2).
|
||||
// The heuristic may refine these later, but we proceed immediately with whatever values
|
||||
// are set rather than waiting for verification.
|
||||
const int base = visibleItemEntryBase_;
|
||||
const int stride = visibleItemStride_;
|
||||
if (base < 0 || stride <= 0) return; // Defensive: should never happen with defaults.
|
||||
|
||||
std::array<uint32_t, 19> newEntries{};
|
||||
for (int s = 0; s < 19; s++) {
|
||||
uint16_t idx = static_cast<uint16_t>(visibleItemEntryBase_ + s * visibleItemStride_);
|
||||
uint16_t idx = static_cast<uint16_t>(base + s * stride);
|
||||
auto it = fields.find(idx);
|
||||
if (it != fields.end()) newEntries[s] = it->second;
|
||||
}
|
||||
|
|
@ -15210,7 +15221,7 @@ void GameHandler::updateOtherPlayerVisibleItems(uint64_t guid, const std::map<ui
|
|||
for (uint32_t e : newEntries) { if (e != 0) nonZero++; }
|
||||
if (nonZero > 0) {
|
||||
LOG_INFO("updateOtherPlayerVisibleItems: guid=0x", std::hex, guid, std::dec,
|
||||
" nonZero=", nonZero,
|
||||
" nonZero=", nonZero, " base=", base, " stride=", stride,
|
||||
" head=", newEntries[0], " shoulders=", newEntries[2],
|
||||
" chest=", newEntries[4], " legs=", newEntries[6],
|
||||
" mainhand=", newEntries[15], " offhand=", newEntries[16]);
|
||||
|
|
@ -15231,11 +15242,16 @@ void GameHandler::updateOtherPlayerVisibleItems(uint64_t guid, const std::map<ui
|
|||
}
|
||||
}
|
||||
|
||||
// If the server isn't sending visible item fields (all zeros), fall back to inspect.
|
||||
bool any = false;
|
||||
for (uint32_t e : newEntries) { if (e != 0) { any = true; break; } }
|
||||
if (!any && socket && state == WorldState::IN_WORLD) {
|
||||
pendingAutoInspect_.insert(guid);
|
||||
// Only fall back to auto-inspect if ALL extracted entries are zero (server didn't
|
||||
// send visible item fields at all). If we got at least one non-zero entry, the
|
||||
// update-field approach is working and inspect is unnecessary.
|
||||
if (nonZero == 0) {
|
||||
LOG_DEBUG("updateOtherPlayerVisibleItems: guid=0x", std::hex, guid, std::dec,
|
||||
" all entries zero (base=", base, " stride=", stride,
|
||||
" fieldCount=", fields.size(), ") — queuing auto-inspect");
|
||||
if (socket && state == WorldState::IN_WORLD) {
|
||||
pendingAutoInspect_.insert(guid);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
|
|
|
|||
|
|
@ -302,37 +302,39 @@ void CameraController::update(float deltaTime) {
|
|||
doCancelAutoFollow();
|
||||
}
|
||||
|
||||
// Auto-follow: face target and walk forward when within range
|
||||
bool autoFollowWalk = false;
|
||||
// Auto-follow: face target and run toward them when within range
|
||||
bool autoFollowMove = false;
|
||||
if (autoFollowTarget_ && followTarget && !movementRooted_) {
|
||||
glm::vec3 myPos = *followTarget;
|
||||
glm::vec3 tgtPos = *autoFollowTarget_;
|
||||
float dx = tgtPos.x - myPos.x;
|
||||
float dy = tgtPos.y - myPos.y;
|
||||
float dist2D = std::sqrt(dx * dx + dy * dy);
|
||||
float distSq2D = dx * dx + dy * dy;
|
||||
|
||||
if (dist2D > FOLLOW_MAX_DIST) {
|
||||
if (distSq2D > FOLLOW_MAX_DIST * FOLLOW_MAX_DIST) {
|
||||
doCancelAutoFollow();
|
||||
} else if (dist2D > FOLLOW_STOP_DIST) {
|
||||
} else if (distSq2D > FOLLOW_STOP_DIST * FOLLOW_STOP_DIST) {
|
||||
// Face target (render-space yaw: atan2(-dx, -dy) -> degrees)
|
||||
float targetYawRad = std::atan2(-dx, -dy);
|
||||
float targetYawDeg = targetYawRad * 180.0f / 3.14159265f;
|
||||
facingYaw = targetYawDeg;
|
||||
yaw = targetYawDeg;
|
||||
autoFollowWalk = true;
|
||||
autoFollowMove = true;
|
||||
}
|
||||
// else: within stop distance, stay put
|
||||
|
||||
// Cancel on strafe/turn keys
|
||||
if (keyA || keyD || keyQ || keyE) {
|
||||
doCancelAutoFollow();
|
||||
autoFollowWalk = false;
|
||||
autoFollowMove = false;
|
||||
}
|
||||
}
|
||||
|
||||
// When the server has rooted the player, suppress all horizontal movement input.
|
||||
const bool movBlocked = movementRooted_;
|
||||
bool nowForward = !movBlocked && (keyW || mouseAutorun || autoRunning || autoFollowWalk);
|
||||
// Auto-follow uses run speed (same as auto-run), not walk speed
|
||||
if (autoFollowMove) autoRunning = true;
|
||||
bool nowForward = !movBlocked && (keyW || mouseAutorun || autoRunning);
|
||||
bool nowBackward = !movBlocked && keyS;
|
||||
bool nowStrafeLeft = false;
|
||||
bool nowStrafeRight = false;
|
||||
|
|
|
|||
|
|
@ -2525,8 +2525,8 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
"/cancelaura", "/cancelform", "/cancellogout", "/cancelshapeshift",
|
||||
"/cast", "/castsequence", "/chathelp", "/clear", "/clearfocus",
|
||||
"/clearmainassist", "/clearmaintank", "/cleartarget", "/cloak",
|
||||
"/combatlog", "/dance", "/dismount", "/dnd", "/do", "/duel", "/dump",
|
||||
"/e", "/emote", "/equip", "/equipset",
|
||||
"/combatlog", "/dance", "/difficulty", "/dismount", "/dnd", "/do", "/duel", "/dump",
|
||||
"/e", "/emote", "/equip", "/equipset", "/exit",
|
||||
"/focus", "/follow", "/forfeit", "/friend",
|
||||
"/g", "/gdemote", "/ginvite", "/gkick", "/gleader", "/gmotd",
|
||||
"/gmticket", "/gpromote", "/gquit", "/grouploot", "/groster",
|
||||
|
|
@ -2541,6 +2541,7 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
"/p", "/party", "/petaggressive", "/petattack", "/petdefensive",
|
||||
"/petdismiss", "/petfollow", "/pethalt", "/petpassive", "/petstay",
|
||||
"/played", "/pvp",
|
||||
"/quit",
|
||||
"/r", "/raid", "/raidconvert", "/raidinfo", "/raidwarning", "/random", "/ready",
|
||||
"/readycheck", "/reload", "/reloadui", "/removefriend",
|
||||
"/reply", "/rl", "/roll", "/run",
|
||||
|
|
@ -6318,7 +6319,8 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
|||
"Target: /target /cleartarget /focus /clearfocus /inspect",
|
||||
"Movement: /sit /stand /kneel /dismount",
|
||||
"Misc: /played /time /zone /loc /afk /dnd /helm /cloak",
|
||||
" /trade /score /unstuck /logout /ticket /screenshot",
|
||||
" /trade /score /unstuck /logout /quit /exit /ticket",
|
||||
" /screenshot /difficulty",
|
||||
" /macrohelp /chathelp /help",
|
||||
};
|
||||
for (const char* line : kHelpLines) {
|
||||
|
|
@ -6625,8 +6627,8 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
|||
return;
|
||||
}
|
||||
|
||||
// /logout command (already exists but using /logout instead of going to login)
|
||||
if (cmdLower == "logout" || cmdLower == "camp") {
|
||||
// /logout command (also /camp, /quit, /exit)
|
||||
if (cmdLower == "logout" || cmdLower == "camp" || cmdLower == "quit" || cmdLower == "exit") {
|
||||
gameHandler.requestLogout();
|
||||
chatInputBuffer[0] = '\0';
|
||||
return;
|
||||
|
|
@ -6639,6 +6641,52 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
|||
return;
|
||||
}
|
||||
|
||||
// /difficulty command — set dungeon/raid difficulty (WotLK)
|
||||
if (cmdLower == "difficulty") {
|
||||
std::string arg;
|
||||
if (spacePos != std::string::npos) {
|
||||
arg = command.substr(spacePos + 1);
|
||||
// Trim whitespace
|
||||
size_t first = arg.find_first_not_of(" \t");
|
||||
size_t last = arg.find_last_not_of(" \t");
|
||||
if (first != std::string::npos)
|
||||
arg = arg.substr(first, last - first + 1);
|
||||
else
|
||||
arg.clear();
|
||||
for (auto& ch : arg) ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
|
||||
}
|
||||
|
||||
uint32_t diff = 0;
|
||||
bool valid = true;
|
||||
if (arg == "normal" || arg == "0") diff = 0;
|
||||
else if (arg == "heroic" || arg == "1") diff = 1;
|
||||
else if (arg == "25" || arg == "25normal" || arg == "25man" || arg == "2")
|
||||
diff = 2;
|
||||
else if (arg == "25heroic" || arg == "25manheroic" || arg == "3")
|
||||
diff = 3;
|
||||
else valid = false;
|
||||
|
||||
if (!valid || arg.empty()) {
|
||||
game::MessageChatData msg;
|
||||
msg.type = game::ChatType::SYSTEM;
|
||||
msg.language = game::ChatLanguage::UNIVERSAL;
|
||||
msg.message = "Usage: /difficulty normal|heroic|25|25heroic (0-3)";
|
||||
gameHandler.addLocalChatMessage(msg);
|
||||
} else {
|
||||
static constexpr const char* kDiffNames[] = {
|
||||
"Normal (5-man)", "Heroic (5-man)", "Normal (25-man)", "Heroic (25-man)"
|
||||
};
|
||||
game::MessageChatData msg;
|
||||
msg.type = game::ChatType::SYSTEM;
|
||||
msg.language = game::ChatLanguage::UNIVERSAL;
|
||||
msg.message = std::string("Setting difficulty to: ") + kDiffNames[diff];
|
||||
gameHandler.addLocalChatMessage(msg);
|
||||
gameHandler.sendSetDifficulty(diff);
|
||||
}
|
||||
chatInputBuffer[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
// /helm command
|
||||
if (cmdLower == "helm" || cmdLower == "helmet" || cmdLower == "showhelm") {
|
||||
gameHandler.toggleHelm();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue