From 4bac36d3bc410e157f62b89dbec695333e3c4b22 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Sat, 25 Aug 2012 16:11:54 -0300 Subject: [PATCH] Implement new cool features * Implement walk booster (dash) as an option in settings menu * Dash is smarter (can pre-animate) * Implement smart walking (walk in diagonal when holding two arrow keys) * Implement ping meter for all protocols * Ping meter uses uses real ping packet for 9.6 and walk for others --- .../{general.otui => console.otui} | 18 +---- modules/client_options/game.otui | 21 ++++++ modules/client_options/options.lua | 17 +++-- .../skins/default/styles/labels.otui | 34 +++++++++ modules/client_topmenu/topmenu.otui | 14 +++- modules/corelib/keyboard.lua | 15 ++++ modules/game_interface/gameinterface.lua | 71 ++++++++++++++----- modules/gamelib/const.lua | 1 + src/otclient/const.h | 1 + src/otclient/game.cpp | 21 +++++- src/otclient/game.h | 3 + src/otclient/localplayer.cpp | 29 ++++---- src/otclient/localplayer.h | 5 +- src/otclient/luafunctions.cpp | 2 + src/otclient/protocolgameparse.cpp | 4 +- 15 files changed, 193 insertions(+), 63 deletions(-) rename modules/client_options/{general.otui => console.otui} (64%) create mode 100644 modules/client_options/game.otui diff --git a/modules/client_options/general.otui b/modules/client_options/console.otui similarity index 64% rename from modules/client_options/general.otui rename to modules/client_options/console.otui index 1c47eeab..77402017 100644 --- a/modules/client_options/general.otui +++ b/modules/client_options/console.otui @@ -1,12 +1,4 @@ Panel - OptionCheckBox - id: classicControl - !text: tr('Classic control') - - OptionCheckBox - id: autoChaseOverride - !text: tr('Allow auto chase override') - OptionCheckBox id: showInfoMessagesInConsole !text: tr('Show info messages in console') @@ -33,12 +25,4 @@ Panel OptionCheckBox id: showPrivateMessagesOnScreen - !text: tr('Show private messages on screen') - - OptionCheckBox - id: enableMusic - !text: tr('Enable music') - - OptionCheckBox - id: showLeftPanel - !text: tr('Show left panel') + !text: tr('Show private messages on screen') \ No newline at end of file diff --git a/modules/client_options/game.otui b/modules/client_options/game.otui new file mode 100644 index 00000000..2e57de9d --- /dev/null +++ b/modules/client_options/game.otui @@ -0,0 +1,21 @@ +Panel + OptionCheckBox + id: classicControl + !text: tr('Classic control') + + OptionCheckBox + id: autoChaseOverride + !text: tr('Allow auto chase override') + + OptionCheckBox + id: walkBooster + !text: tr('Enable walk booster') + !tooltip: tr('Also know as dash in tibia community, recommended\nfor playing characters with high speed') + + OptionCheckBox + id: enableMusic + !text: tr('Enable music') + + OptionCheckBox + id: showLeftPanel + !text: tr('Show left panel') diff --git a/modules/client_options/options.lua b/modules/client_options/options.lua index 5ad64036..8e26acb6 100644 --- a/modules/client_options/options.lua +++ b/modules/client_options/options.lua @@ -5,6 +5,8 @@ local defaultOptions = { showfps = true, fullscreen = false, classicControl = false, + walkBooster = false, + smartWalk = false, autoChaseOverride = true, showStatusMessagesInConsole = true, showEventMessagesInConsole = true, @@ -68,8 +70,8 @@ function Options.init() end end - g_keyboard.bindKeyDown('Ctrl+D', Options.toggle) g_keyboard.bindKeyDown('Ctrl+F', function() Options.toggleOption('fullscreen') end) + g_keyboard.bindKeyDown('Ctrl+D', function() Options.toggleOption('walkBooster') end) optionsWindow = g_ui.displayUI('options.otui') optionsWindow:hide() @@ -78,8 +80,11 @@ function Options.init() optionsTabBar = optionsWindow:getChildById('optionsTabBar') optionsTabBar:setContentWidget(optionsWindow:getChildById('optionsTabContent')) - generalPanel = g_ui.loadUI('general.otui') - optionsTabBar:addTab(tr('General'), generalPanel) + generalPanel = g_ui.loadUI('game.otui') + optionsTabBar:addTab(tr('Game'), generalPanel) + + generalPanel = g_ui.loadUI('console.otui') + optionsTabBar:addTab(tr('Console'), generalPanel) graphicsPanel = g_ui.loadUI('graphics.otui') optionsTabBar:addTab(tr('Graphics'), graphicsPanel) @@ -147,8 +152,8 @@ function Options.setOption(key, value) end if graphicsPanel then - graphicsPanel:getChildById('backgroundFrameRateLabel'):setText(tr('Game framerate limit: %s', text)) - end + graphicsPanel:getChildById('backgroundFrameRateLabel'):setText(tr('Game framerate limit: %s', text)) + end g_app.setBackgroundPaneMaxFps(value) elseif key == 'foregroundFrameRate' then local text = value @@ -158,7 +163,7 @@ function Options.setOption(key, value) end if graphicsPanel then - graphicsPanel:getChildById('foregroundFrameRateLabel'):setText(tr('Interface framerate limit: %s', text)) + graphicsPanel:getChildById('foregroundFrameRateLabel'):setText(tr('Interface framerate limit: %s', text)) end g_app.setForegroundPaneMaxFps(value) elseif key == 'painterEngine' then diff --git a/modules/client_skins/skins/default/styles/labels.otui b/modules/client_skins/skins/default/styles/labels.otui index 80e503d3..cea986e9 100644 --- a/modules/client_skins/skins/default/styles/labels.otui +++ b/modules/client_skins/skins/default/styles/labels.otui @@ -24,9 +24,43 @@ GameLabel < UILabel color: #bbbbbb FrameCounterLabel < Label + font: verdana-11px-rounded @onSetup: | self.updateEvent = cycleEvent(function() local text = 'FPS: ' .. g_app.getBackgroundPaneFps() self:setText(text) end, 1000) @onDestroy: self.updateEvent:cancel() + +PingLabel < Label + font: verdana-11px-rounded + @onSetup: | + self.updateEvent = cycleEvent(function() + if g_game.isOnline() then + local ping = -1 + if g_game.getFeature(GameClientPing) then + ping = g_game.getPing() + else + ping = g_game.getLocalPlayer():getWalkPing() + end + local text = 'Ping: ' + if ping < 0 then + text = text .. "??" + self:setColor('yellow') + else + text = text .. ping .. ' ms' + if ping >= 500 then + self:setColor('red') + elseif ping >= 250 then + self:setColor('yellow') + else + self:setColor('green') + end + end + self:setText(text) + self:show() + else + self:hide() + end + end, 1000) + @onDestroy: self.updateEvent:cancel() diff --git a/modules/client_topmenu/topmenu.otui b/modules/client_topmenu/topmenu.otui index b52f6f89..2700cad6 100644 --- a/modules/client_topmenu/topmenu.otui +++ b/modules/client_topmenu/topmenu.otui @@ -67,11 +67,21 @@ TopMenuPanel anchors.left: prev.right visible: false - TopMenuFrameCounterLabel + FrameCounterLabel + color: white + margin-top: 4 + margin-left: 5 id: frameCounter text-auto-resize: true anchors.top: parent.top - anchors.left: prev.right + anchors.left: leftGameButtonsPanel.right + + PingLabel + color: white + id: pingLabel + text-auto-resize: true + anchors.top: frameCounter.bottom + anchors.left: frameCounter.left TopMenuButtonsPanel id: rightButtonsPanel diff --git a/modules/corelib/keyboard.lua b/modules/corelib/keyboard.lua index b1750f38..f8896379 100644 --- a/modules/corelib/keyboard.lua +++ b/modules/corelib/keyboard.lua @@ -15,6 +15,14 @@ function translateKeyCombo(keyCombo) return keyComboDesc end +local function getKeyCode(key) + for keyCode, keyDesc in pairs(KeyCodeDescs) do + if keyDesc:lower() == key:trim():lower() then + return keyCode + end + end +end + local function retranslateKeyComboDesc(keyComboDesc) if keyComboDesc == nil then error('Unable to translate key combo \'' .. keyComboDesc .. '\'') @@ -140,6 +148,13 @@ function g_keyboard.getModifiers() return g_window.getKeyboardModifiers() end +function g_keyboard.isKeyPressed(key) + if type(key) == 'string' then + key = getKeyCode(key) + end + return g_window.isKeyPressed(key) +end + function g_keyboard.isCtrlPressed() return bit32.band(g_window.getKeyboardModifiers(), KeyboardCtrlModifier) ~= 0 end diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index 9ea42584..d4113185 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -33,10 +33,18 @@ function init() logoutButton = TopMenu.addRightButton('logoutButton', 'Logout', '/images/logout.png', tryLogout) logoutButton:hide() - g_keyboard.bindKeyPress('Up', function() g_game.walk(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Right', function() g_game.walk(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Down', function() g_game.walk(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Left', function() g_game.walk(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) + bindKeys() + + if g_game.isOnline() then + show() + end +end + +function bindKeys() + g_keyboard.bindKeyPress('Up', smartWalk, gameRootPanel, WALK_AUTO_REPEAT_DELAY) + g_keyboard.bindKeyPress('Right', smartWalk, gameRootPanel, WALK_AUTO_REPEAT_DELAY) + g_keyboard.bindKeyPress('Down', smartWalk, gameRootPanel, WALK_AUTO_REPEAT_DELAY) + g_keyboard.bindKeyPress('Left', smartWalk, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad8', function() g_game.walk(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad9', function() g_game.walk(NorthEast) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad6', function() g_game.walk(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) @@ -59,19 +67,8 @@ function init() g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel) - - g_keyboard.bindKeyDown('Ctrl+.', function() - if gameMapPanel:isKeepAspectRatioEnabled() then - gameMapPanel:setKeepAspectRatio(false) - else - gameMapPanel:setKeepAspectRatio(true) - gameMapPanel:setVisibleDimension({ width = 15, height = 11 }) - end - end, gameRootPanel) - - if g_game.isOnline() then - show() - end + g_keyboard.bindKeyDown('Ctrl+;', toggleDash, gameRootPanel) + g_keyboard.bindKeyDown('Ctrl+.', toggleAspectRatio, gameRootPanel) end function terminate() @@ -155,6 +152,46 @@ function tryLogout() anchor=AnchorHorizontalCenter}, yesCallback, noCallback) end +function smartWalk() + local dir + if g_keyboard.isKeyPressed('Up') and g_keyboard.isKeyPressed('Left') then + dir = NorthWest + elseif g_keyboard.isKeyPressed('Up') and g_keyboard.isKeyPressed('Right') then + dir = NorthEast + elseif g_keyboard.isKeyPressed('Down') and g_keyboard.isKeyPressed('Left') then + dir = SouthWest + elseif g_keyboard.isKeyPressed('Down') and g_keyboard.isKeyPressed('Right') then + dir = SouthEast + elseif g_keyboard.isKeyPressed('Up') then + dir = North + elseif g_keyboard.isKeyPressed('Down') then + dir = South + elseif g_keyboard.isKeyPressed('Left') then + dir = West + elseif g_keyboard.isKeyPressed('Right') then + dir = East + end + + if Options.getOption('walkBooster') then + if g_game.getLocalPlayer():canWalk(dir) then + g_game.walk(dir) + else + g_game.forceWalk(dir) + end + else + g_game.walk(dir) + end +end + +function toggleAspectRatio() + if gameMapPanel:isKeepAspectRatioEnabled() then + gameMapPanel:setKeepAspectRatio(false) + else + gameMapPanel:setKeepAspectRatio(true) + gameMapPanel:setVisibleDimension({ width = 15, height = 11 }) + end +end + function onMouseGrabberRelease(self, mousePosition, mouseButton) if selectedThing == nil then return false end if mouseButton == MouseLeftButton then diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua index 038412bf..eb9c1353 100644 --- a/modules/gamelib/const.lua +++ b/modules/gamelib/const.lua @@ -64,6 +64,7 @@ GameOfflineTrainingTime = 20 GamePurseSlot = 21 GameFormatCreatureName = 22 GameSpellList = 23 +GameClientPing = 24 TextColors = { red = '#f55e5e', --'#c83200' diff --git a/src/otclient/const.h b/src/otclient/const.h index f803905f..226126f3 100644 --- a/src/otclient/const.h +++ b/src/otclient/const.h @@ -339,6 +339,7 @@ namespace Otc GamePurseSlot = 21, GameFormatCreatureName = 22, GameSpellList = 23, + GameClientPing = 24, // 23-50 unused yet // 51-100 reserved to be defined in lua LastGameFeature = 101 diff --git a/src/otclient/game.cpp b/src/otclient/game.cpp index 66403664..b6688efd 100644 --- a/src/otclient/game.cpp +++ b/src/otclient/game.cpp @@ -55,6 +55,7 @@ void Game::resetGameStates() m_dead = false; m_serverBeat = 50; m_seq = 0; + m_ping = -1; m_canReportBugs = false; m_fightMode = Otc::FightBalanced; m_chaseMode = Otc::DontChase; @@ -68,6 +69,12 @@ void Game::resetGameStates() if(container) container->onClose(); } + + if(m_pingEvent) { + m_pingEvent->cancel(); + m_pingEvent = nullptr; + } + m_containers.clear(); m_vips.clear(); m_gmActions.clear(); @@ -122,6 +129,16 @@ void Game::processGameStart() enableBotCall(); g_lua.callGlobalField("g_game", "onGameStart"); disableBotCall(); + + if(g_game.getFeature(Otc::GameClientPing)) { + m_pingEvent = g_dispatcher.cycleEvent([this] { + if(m_protocolGame && m_protocolGame->isConnected()) { + enableBotCall(); + m_protocolGame->sendPing(); + disableBotCall(); + } + }, 1000); + } } void Game::processGameEnd() @@ -159,6 +176,7 @@ void Game::processPing() void Game::processPingBack(int elapsed) { + m_ping = elapsed; g_lua.callGlobalField("g_game", "onPingBack", elapsed); } @@ -1175,8 +1193,9 @@ void Game::setClientVersion(int version) enableFeature(Otc::GamePlayerMarket); } - if(version >= 954) { + if(version >= 953) { enableFeature(Otc::GamePurseSlot); + enableFeature(Otc::GameClientPing); } if(version >= 960) { diff --git a/src/otclient/game.h b/src/otclient/game.h index c28a1ca9..d45a8f09 100644 --- a/src/otclient/game.h +++ b/src/otclient/game.h @@ -252,6 +252,7 @@ public: bool isAttacking() { return !!m_attackingCreature; } bool isFollowing() { return !!m_followingCreature; } + int getPing() { return m_ping; } ContainerPtr getContainer(int index) { return m_containers[index]; } std::map getContainers() { return m_containers; } std::map getVips() { return m_vips; } @@ -289,6 +290,7 @@ private: bool m_denyBotCall; bool m_dead; int m_serverBeat; + int m_ping; uint m_seq; Otc::FightModes m_fightMode; Otc::ChaseModes m_chaseMode; @@ -298,6 +300,7 @@ private: std::string m_characterName; std::string m_worldName; std::bitset m_features; + ScheduledEventPtr m_pingEvent; int m_clientVersion; }; diff --git a/src/otclient/localplayer.cpp b/src/otclient/localplayer.cpp index c7bbbc36..9f74933e 100644 --- a/src/otclient/localplayer.cpp +++ b/src/otclient/localplayer.cpp @@ -37,6 +37,7 @@ LocalPlayer::LocalPlayer() m_states = 0; m_vocation = 0; m_walkLockExpiration = 0; + m_lastWalkPing = -1; m_skillsLevel.fill(-1); m_skillsBaseLevel.fill(-1); @@ -99,11 +100,8 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos) // a prewalk was going on if(m_preWalking) { if(m_waitingWalkPong) { - if(newPos == m_lastPrewalkDestionation) { - m_lastWalkPings.push_back(m_walkPingTimer.ticksElapsed()); - if(m_lastWalkPings.size() > 10) - m_lastWalkPings.pop_front(); - } + if(newPos == m_lastPrewalkDestionation) + m_lastWalkPing = m_walkPingTimer.ticksElapsed(); m_waitingWalkPong = false; } @@ -120,6 +118,7 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos) } // no prewalk was going on, this must be an server side automated walk else { + m_walkPingTimer.restart(); m_autoWalking = true; if(m_autoWalkEndEvent) m_autoWalkEndEvent->cancel(); @@ -136,6 +135,12 @@ void LocalPlayer::preWalk(Otc::Direction direction) if(m_preWalking && m_lastPrewalkDestionation == newPos) return; + m_waitingWalkPong = false; + if(m_walkPingTimer.ticksElapsed() > getStepDuration() && m_idleTimer.ticksElapsed() > getStepDuration()*2) { + m_waitingWalkPong = true; + m_walkPingTimer.restart(); + } + m_preWalking = true; if(m_autoWalkEndEvent) @@ -155,6 +160,8 @@ void LocalPlayer::cancelWalk(Otc::Direction direction) m_lastPrewalkDone = true; m_waitingWalkPong = false; + m_walkPingTimer.restart(); + m_idleTimer.restart(); // turn to the cancel direction if(direction != Otc::InvalidDirection) @@ -206,6 +213,7 @@ void LocalPlayer::terminateWalk() { Creature::terminateWalk(); m_preWalking = false; + m_idleTimer.restart(); auto self = asLocalPlayer(); @@ -458,17 +466,6 @@ void LocalPlayer::setSpells(const std::vector& spells) } } -double LocalPlayer::getWalkPing() -{ - if(m_lastWalkPings.empty()) - return 9999; - - double sum = 0; - for(int p : m_lastWalkPings) - sum += p; - return sum / (double)m_lastWalkPings.size(); -} - bool LocalPlayer::hasSight(const Position& pos) { return m_position.isInRange(pos, (Otc::VISIBLE_X_TILES - 1)/2, (Otc::VISIBLE_Y_TILES - 1)/2); diff --git a/src/otclient/localplayer.h b/src/otclient/localplayer.h index b7d6e5c4..279b4954 100644 --- a/src/otclient/localplayer.h +++ b/src/otclient/localplayer.h @@ -66,7 +66,7 @@ public: int getSkillBaseLevel(Otc::Skill skill) { return m_skillsBaseLevel[skill]; } int getSkillLevelPercent(Otc::Skill skill) { return m_skillsLevelPercent[skill]; } int getVocation() { return m_vocation; } - double getWalkPing(); + int getWalkPing() { return m_lastWalkPing; } double getHealth() { return m_health; } double getMaxHealth() { return m_maxHealth; } double getFreeCapacity() { return m_freeCapacity; } @@ -122,7 +122,8 @@ private: ScheduledEventPtr m_autoWalkEndEvent; stdext::boolean m_waitingWalkPong; Timer m_walkPingTimer; - std::deque m_lastWalkPings; + Timer m_idleTimer; + int m_lastWalkPing; std::array m_skillsLevel; std::array m_skillsBaseLevel; diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index dc69727a..4a42ca36 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -199,6 +199,7 @@ void OTClient::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "isDead", &Game::isDead, &g_game); g_lua.bindSingletonFunction("g_game", "isAttacking", &Game::isAttacking, &g_game); g_lua.bindSingletonFunction("g_game", "isFollowing", &Game::isFollowing, &g_game); + g_lua.bindSingletonFunction("g_game", "getPing", &Game::getPing, &g_game); g_lua.bindSingletonFunction("g_game", "getContainer", &Game::getContainer, &g_game); g_lua.bindSingletonFunction("g_game", "getContainers", &Game::getContainers, &g_game); g_lua.bindSingletonFunction("g_game", "getVips", &Game::getVips, &g_game); @@ -412,6 +413,7 @@ void OTClient::registerLuaFunctions() g_lua.bindClassMemberFunction("getTotalCapacity", &LocalPlayer::getTotalCapacity); g_lua.bindClassMemberFunction("getInventoryItem", &LocalPlayer::getInventoryItem); g_lua.bindClassMemberFunction("getVocation", &LocalPlayer::getVocation); + g_lua.bindClassMemberFunction("getWalkPing", &LocalPlayer::getWalkPing); g_lua.bindClassMemberFunction("isPremium", &LocalPlayer::isPremium); g_lua.bindClassMemberFunction("isKnown", &LocalPlayer::isKnown); g_lua.bindClassMemberFunction("isPreWalking", &LocalPlayer::isPreWalking); diff --git a/src/otclient/protocolgameparse.cpp b/src/otclient/protocolgameparse.cpp index 545797f9..1009ff1d 100644 --- a/src/otclient/protocolgameparse.cpp +++ b/src/otclient/protocolgameparse.cpp @@ -73,8 +73,8 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) break; case Proto::GameServerPing: case Proto::GameServerPingBack: - if((opcode == Proto::GameServerPing && g_game.getClientVersion() >= 953) || - (opcode == Proto::GameServerPingBack && g_game.getClientVersion() < 953)) + if((opcode == Proto::GameServerPing && g_game.getFeature(Otc::GameClientPing)) || + (opcode == Proto::GameServerPingBack && !g_game.getFeature(Otc::GameClientPing))) parsePingBack(msg); else parsePing(msg);