diff --git a/modules/game_walk/walk.lua b/modules/game_walk/walk.lua index 197566f8b..964269e3f 100644 --- a/modules/game_walk/walk.lua +++ b/modules/game_walk/walk.lua @@ -65,7 +65,7 @@ local function walk(dir) if not canChangeFloor(toPos, 1) and not canChangeFloor(toPos, -1) then return false end - elseif not player:isPreWalking() then + else player:preWalk(dir) end end @@ -88,7 +88,7 @@ local function addWalkEvent(dir, delay) walk(smartWalkDir or dir) end - walkEvent = delay == 0 and addEvent(walkCallback) or scheduleEvent(walkCallback, delay or 10) + walkEvent = delay == 0 and addEvent(walkCallback) or scheduleEvent(walkCallback, delay or 5) end --- Initiates a smart walk in the given direction. diff --git a/src/client/creature.cpp b/src/client/creature.cpp index bdacb7449..93c522394 100644 --- a/src/client/creature.cpp +++ b/src/client/creature.cpp @@ -614,7 +614,7 @@ void Creature::updateWalkingTile() // only render creatures where bottom right is inside tile rect if (virtualTileRect.contains(virtualCreatureRect.bottomRight())) { - newWalkingTile = g_map.getOrCreateTile(m_position.translated(xi, yi, 0)); + newWalkingTile = g_map.getOrCreateTile(getPosition().translated(xi, yi, 0)); } } } @@ -655,7 +655,7 @@ void Creature::nextWalkUpdate() void Creature::updateWalk() { - const float walkTicksPerPixel = (getStepDuration(true) + 8.f) / static_cast(g_gameConfig.getSpriteSize()); + const float walkTicksPerPixel = getStepDuration(true) / static_cast(g_gameConfig.getSpriteSize()); const int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, g_gameConfig.getSpriteSize()); diff --git a/src/client/creature.h b/src/client/creature.h index cca48dec6..b9822614f 100644 --- a/src/client/creature.h +++ b/src/client/creature.h @@ -186,8 +186,8 @@ minHeight, bool canShoot(int distance); protected: - virtual void updateWalkOffset(uint8_t totalPixelsWalked); virtual void terminateWalk(); + void updateWalkOffset(uint8_t totalPixelsWalked); void updateWalk(); ThingType* getThingType() const override; @@ -202,6 +202,7 @@ minHeight, Otc::Direction m_direction{ Otc::South }; Timer m_walkTimer; + Position m_lastStepToPosition; private: void nextWalkUpdate(); @@ -245,7 +246,6 @@ minHeight, CachedStep m_stepCache; Position m_lastStepFromPosition; - Position m_lastStepToPosition; Position m_oldPosition; Timer m_footTimer; diff --git a/src/client/game.cpp b/src/client/game.cpp index df03b283f..5de6148cb 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -55,6 +55,7 @@ void Game::resetGameStates() m_serverBeat = 50; m_seq = 0; m_ping = -1; + m_walkTicks = 0; setCanReportBugs(false); m_fightMode = Otc::FightBalanced; m_chaseMode = Otc::DontChase; @@ -684,6 +685,9 @@ void Game::forceWalk(const Otc::Direction direction) if (!canPerformGameAction()) return; + m_walkTimer.restart(); + m_walkTicks = -1; + switch (direction) { case Otc::North: m_protocolGame->sendWalkNorth(); diff --git a/src/client/game.h b/src/client/game.h index 57d4155ef..7a6494448 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -776,6 +776,8 @@ class Game void requestBossSlootInfo(); void requestBossSlotAction(uint8_t action, uint32_t raceId); void sendStatusTrackerBestiary(uint16_t raceId, bool status); + auto getServerWalkTicks() const { return m_walkTicks; } + auto getWalkTicksElapsed() const { return m_walkTimer.ticksElapsed(); } protected: void enableBotCall() { m_denyBotCall = false; } void disableBotCall() { m_denyBotCall = true; } @@ -827,6 +829,8 @@ class Game stdext::timer m_pingTimer; ticks_t m_ping{ -1 }; + ticks_t m_walkTicks{ 0 }; + Timer m_walkTimer; }; extern Game g_game; diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 8f359c9b4..c04af18ce 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -46,37 +46,42 @@ bool LocalPlayer::canWalk(const Otc::Direction dir, const bool ignoreLock) if (isPreWalking()) return false; // allow only single prewalk } - return m_walkTimer.ticksElapsed() >= getStepDuration(); // allow only if walk done, ex. diagonals may need additional ticks before taking another step + if (g_game.getServerWalkTicks() == -1) + return false; + + // allow only if walk done, ex. diagonals may need additional ticks before taking another step + return m_walkTimer.ticksElapsed() >= std::max(getStepDuration(), g_game.getServerWalkTicks()); } void LocalPlayer::walk(const Position& oldPos, const Position& newPos) { m_autoWalkRetries = 0; - if (isPreWalking()) { - if (newPos == m_lastPrewalkDestination) { - m_lastPrewalkDestination = {}; - updateWalk(); + if (oldPos.z == newPos.z) { + if (m_lastPrewalkDestination == newPos || g_game.getWalkTicksElapsed() <= 1) return; - } - m_lastPrewalkDestination = {}; + if (g_game.getServerWalkTicks() - getStepDuration() > g_game.getWalkTicksElapsed()) + return; } m_serverWalk = true; + Creature::walk(oldPos, newPos); } void LocalPlayer::preWalk(const Otc::Direction direction) { - auto pos = m_position.translatedToDirection(direction); // avoid reanimating prewalks - if (m_lastPrewalkDestination.isValid() || m_lastPrewalkDestination == pos) + if (m_lastPrewalkDestination.isValid()) + return; + + auto pos = m_position.translatedToDirection(direction); + + if (m_lastPrewalkDestination == pos) return; - // start walking to direction - m_lastPrewalkDestination = pos; - Creature::walk(m_position, m_lastPrewalkDestination); + Creature::walk(m_position, m_lastPrewalkDestination = std::move(pos)); } bool LocalPlayer::retryAutoWalk() @@ -185,26 +190,6 @@ void LocalPlayer::stopAutoWalk() m_autoWalkContinueEvent->cancel(); } -void LocalPlayer::updateWalkOffset(const uint8_t totalPixelsWalked) -{ - if (!isPreWalking()) { - Creature::updateWalkOffset(totalPixelsWalked); - return; - } - - // pre walks offsets are calculated in the oposite direction - m_walkOffset = {}; - if (m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest) - m_walkOffset.y = -totalPixelsWalked; - else if (m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest) - m_walkOffset.y = totalPixelsWalked; - - if (m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast) - m_walkOffset.x = totalPixelsWalked; - else if (m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest) - m_walkOffset.x = -totalPixelsWalked; -} - void LocalPlayer::terminateWalk() { Creature::terminateWalk(); diff --git a/src/client/localplayer.h b/src/client/localplayer.h index b97db8d30..5c2de49b6 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -105,6 +105,7 @@ class LocalPlayer final : public Player bool hasSight(const Position& pos); bool isKnown() { return m_known; } + bool isServerWalking() { return m_serverWalk; } bool isPreWalking() { return m_lastPrewalkDestination.isValid(); } bool isAutoWalking() { return m_autoWalkDestination.isValid(); } bool isPremium() { return m_premium; } @@ -117,13 +118,11 @@ class LocalPlayer final : public Player void onPositionChange(const Position& newPos, const Position& oldPos) override; void preWalk(Otc::Direction direction); - Position getLastPrewalkingPosition() { return m_lastPrewalkDestination; } - bool isServerWalking() { return m_serverWalk; } + Position getPosition() override { return m_lastStepToPosition.isValid() && m_lastStepToPosition.z == m_position.z && m_position.distance(m_lastStepToPosition) < 2 ? m_lastStepToPosition : m_position; } protected: void walk(const Position& oldPos, const Position& newPos) override; - void updateWalkOffset(uint8_t totalPixelsWalked) override; void terminateWalk() override; friend class Game; diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 74c9494b9..b88da6b8c 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -851,9 +851,7 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("getBlessings", &LocalPlayer::getBlessings); g_lua.bindClassMemberFunction("isPremium", &LocalPlayer::isPremium); g_lua.bindClassMemberFunction("isKnown", &LocalPlayer::isKnown); - g_lua.bindClassMemberFunction("isPreWalking", &LocalPlayer::isPreWalking); g_lua.bindClassMemberFunction("preWalk", &LocalPlayer::preWalk); - g_lua.bindClassMemberFunction("getLastPrewalkingPosition", &LocalPlayer::getLastPrewalkingPosition); g_lua.bindClassMemberFunction("hasSight", &LocalPlayer::hasSight); g_lua.bindClassMemberFunction("isAutoWalking", &LocalPlayer::isAutoWalking); g_lua.bindClassMemberFunction("stopAutoWalk", &LocalPlayer::stopAutoWalk); diff --git a/src/client/map.cpp b/src/client/map.cpp index 1db773978..01d4c59ea 100644 --- a/src/client/map.cpp +++ b/src/client/map.cpp @@ -244,7 +244,7 @@ bool Map::removeThing(const ThingPtr& thing) return false; if (thing->isMissile()) { - auto& missiles = m_floors[thing->getPosition().z].missiles; + auto& missiles = m_floors[thing->getServerPosition().z].missiles; const auto it = std::ranges::find(missiles, thing->static_self_cast()); if (it == missiles.end()) return false; @@ -255,7 +255,7 @@ bool Map::removeThing(const ThingPtr& thing) if (const auto& tile = thing->getTile()) { if (tile->removeThing(thing)) { - notificateTileUpdate(thing->getPosition(), thing, Otc::OPERATION_REMOVE); + notificateTileUpdate(thing->getServerPosition(), thing, Otc::OPERATION_REMOVE); return true; } } diff --git a/src/client/protocolgameparse.cpp b/src/client/protocolgameparse.cpp index 29276e71a..82d25ca71 100644 --- a/src/client/protocolgameparse.cpp +++ b/src/client/protocolgameparse.cpp @@ -1366,6 +1366,11 @@ void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg) const auto& creature = thing->static_self_cast(); creature->allowAppearWalk(); + if (creature->isLocalPlayer() && g_game.m_walkTicks == -1) { + g_game.m_walkTicks = g_game.m_walkTimer.ticksElapsed(); + g_game.m_walkTimer.restart(); + } + g_map.addThing(thing, newPos, -1); } @@ -3867,17 +3872,17 @@ void ProtocolGame::parseCyclopediaHousesInfo(const InputMessagePtr& msg) // TO-DO Lua // Otui } -void ProtocolGame::parseCyclopediaHouseList(const InputMessagePtr& msg) +void ProtocolGame::parseCyclopediaHouseList(const InputMessagePtr& msg) { const uint16_t housesCount = msg->getU16(); // housesCount for (auto i = 0; i < housesCount; ++i) { msg->getU32(); // clientId msg->getU8(); // 0x00 = Renovation, 0x01 = Available - + const auto type = static_cast(msg->getU8()); switch (type) { case Otc::CYCLOPEDIA_HOUSE_STATE_AVAILABLE: { - std::string bidderName = msg->getString(); + std::string bidderName = msg->getString(); const auto isBidder = static_cast(msg->getU8()); msg->getU8(); // disableIndex @@ -3893,7 +3898,7 @@ void ProtocolGame::parseCyclopediaHouseList(const InputMessagePtr& msg) case Otc::CYCLOPEDIA_HOUSE_STATE_RENTED: { msg->getString(); // ownerName msg->getU32(); // paidUntil - + const auto isRented = static_cast(msg->getU8()); if (isRented) { msg->getU8(); // unknown @@ -3913,7 +3918,7 @@ void ProtocolGame::parseCyclopediaHouseList(const InputMessagePtr& msg) msg->getString(); // bidderName msg->getU8(); // unknown msg->getU64(); // internalBid - + const auto isNewOwner = static_cast(msg->getU8()); if (isNewOwner) { msg->getU8(); // acceptTransferError diff --git a/src/client/thing.h b/src/client/thing.h index 78883229b..348756556 100644 --- a/src/client/thing.h +++ b/src/client/thing.h @@ -48,7 +48,8 @@ class Thing : public AttachableObject virtual uint32_t getId() { return m_clientId; } uint16_t getClientId() const { return m_clientId; } - Position getPosition() { return m_position; } + virtual Position getPosition() { return m_position; } + Position getServerPosition() { return m_position; } const TilePtr& getTile(); ContainerPtr getParentContainer(); diff --git a/src/client/tile.cpp b/src/client/tile.cpp index f618ee5c1..37062b471 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -113,10 +113,16 @@ void Tile::drawCreature(const Point& dest, const int flags, const bool forceDraw if (!forceDraw && !m_drawTopAndCreature) return; + bool localPlayerDrawed = false; if (hasCreature()) { for (const auto& thing : m_things) { if (!thing->isCreature() || thing->static_self_cast()->isWalking()) continue; + if (thing->isLocalPlayer()) { + if (thing->getPosition() != m_position) continue; + localPlayerDrawed = true; + } + drawThing(thing, dest, flags, drawElevation); } } @@ -129,6 +135,11 @@ void Tile::drawCreature(const Point& dest, const int flags, const bool forceDraw creature->draw(cDest, flags & Otc::DrawThings); } + + // draw the local character if he is on a virtual tile, that is, his visual position is not the same as the server. + if (!localPlayerDrawed && g_game.getLocalPlayer() && !g_game.getLocalPlayer()->isWalking() && g_game.getLocalPlayer()->getPosition() == m_position) { + drawThing(g_game.getLocalPlayer(), dest, flags, drawElevation); + } } void Tile::drawTop(const Point& dest, const int flags, const bool forceDraw, uint8_t drawElevation) @@ -977,4 +988,4 @@ bool Tile::canShoot(int distance) if (distance > 0 && std::max(std::abs(m_position.x - playerPos.x), std::abs(m_position.y - playerPos.y)) > distance) return false; return g_map.isSightClear(playerPos, m_position); -} +} \ No newline at end of file