mirror of
				https://github.com/OTCv8/otclientv8.git
				synced 2025-10-31 10:56:24 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			434 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			434 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| smartWalkDirs = {}
 | |
| smartWalkDir = nil
 | |
| wsadWalking = false
 | |
| nextWalkDir = nil
 | |
| lastWalkDir = nil
 | |
| lastFinishedStep = 0
 | |
| autoWalkEvent = nil
 | |
| firstStep = true
 | |
| walkLock = 0
 | |
| walkEvent = nil
 | |
| lastWalk = 0
 | |
| lastTurn = 0
 | |
| lastTurnDirection = 0
 | |
| lastStop = 0
 | |
| lastManualWalk = 0
 | |
| autoFinishNextServerWalk = 0
 | |
| turnKeys = {}
 | |
| 
 | |
| function init()
 | |
|   connect(g_game, { onTeleport = onTeleport })
 | |
|   
 | |
|   connect(LocalPlayer, {
 | |
|     onPositionChange = onPositionChange,
 | |
|     onWalk = onWalk,
 | |
|     onWalkFinish = onWalkFinish,
 | |
|     onCancelWalk = onCancelWalk
 | |
|   })
 | |
| 
 | |
|   modules.game_interface.getRootPanel().onFocusChange = stopSmartWalk
 | |
|   bindKeys()
 | |
| end
 | |
| 
 | |
| function terminate()
 | |
|   disconnect(g_game, { onTeleport = onTeleport })
 | |
|   
 | |
|   disconnect(LocalPlayer, {
 | |
|     onPositionChange = onPositionChange,
 | |
|     onWalk = onWalk,
 | |
|     onWalkFinish = onWalkFinish
 | |
|   })
 | |
|   removeEvent(autoWalkEvent)
 | |
|   stopSmartWalk()
 | |
|   unbindKeys()
 | |
|   disableWSAD()
 | |
| end
 | |
| 
 | |
| function bindKeys()
 | |
|   bindWalkKey('Up', North)
 | |
|   bindWalkKey('Right', East)
 | |
|   bindWalkKey('Down', South)
 | |
|   bindWalkKey('Left', West)
 | |
|   bindWalkKey('Numpad8', North)
 | |
|   bindWalkKey('Numpad9', NorthEast)
 | |
|   bindWalkKey('Numpad6', East)
 | |
|   bindWalkKey('Numpad3', SouthEast)
 | |
|   bindWalkKey('Numpad2', South)
 | |
|   bindWalkKey('Numpad1', SouthWest)
 | |
|   bindWalkKey('Numpad4', West)
 | |
|   bindWalkKey('Numpad7', NorthWest)
 | |
| 
 | |
|   bindTurnKey('Ctrl+Up', North)
 | |
|   bindTurnKey('Ctrl+Right', East)
 | |
|   bindTurnKey('Ctrl+Down', South)
 | |
|   bindTurnKey('Ctrl+Left', West)
 | |
|   bindTurnKey('Ctrl+Numpad8', North)
 | |
|   bindTurnKey('Ctrl+Numpad6', East)
 | |
|   bindTurnKey('Ctrl+Numpad2', South)
 | |
|   bindTurnKey('Ctrl+Numpad4', West)
 | |
| end
 | |
| 
 | |
| function unbindKeys()
 | |
|   unbindWalkKey('Up', North)
 | |
|   unbindWalkKey('Right', East)
 | |
|   unbindWalkKey('Down', South)
 | |
|   unbindWalkKey('Left', West)
 | |
|   unbindWalkKey('Numpad8', North)
 | |
|   unbindWalkKey('Numpad9', NorthEast)
 | |
|   unbindWalkKey('Numpad6', East)
 | |
|   unbindWalkKey('Numpad3', SouthEast)
 | |
|   unbindWalkKey('Numpad2', South)
 | |
|   unbindWalkKey('Numpad1', SouthWest)
 | |
|   unbindWalkKey('Numpad4', West)
 | |
|   unbindWalkKey('Numpad7', NorthWest)
 | |
| 
 | |
|   unbindTurnKey('Ctrl+Up', North)
 | |
|   unbindTurnKey('Ctrl+Right', East)
 | |
|   unbindTurnKey('Ctrl+Down', South)
 | |
|   unbindTurnKey('Ctrl+Left', West)
 | |
|   unbindTurnKey('Ctrl+Numpad8', North)
 | |
|   unbindTurnKey('Ctrl+Numpad6', East)
 | |
|   unbindTurnKey('Ctrl+Numpad2', South)
 | |
|   unbindTurnKey('Ctrl+Numpad4', West)
 | |
| end
 | |
| 
 | |
| function enableWSAD()
 | |
|   if wsadWalking then
 | |
|     return
 | |
|   end
 | |
|   wsadWalking = true  
 | |
|   local player = g_game.getLocalPlayer()
 | |
|   if player then
 | |
|     player:lockWalk(100) -- 100 ms walk lock for all directions    
 | |
|   end
 | |
| 
 | |
|   bindWalkKey("W", North)
 | |
|   bindWalkKey("D", East)
 | |
|   bindWalkKey("S", South)
 | |
|   bindWalkKey("A", West)
 | |
| 
 | |
|   bindTurnKey("Ctrl+W", North)
 | |
|   bindTurnKey("Ctrl+D", East)
 | |
|   bindTurnKey("Ctrl+S", South)
 | |
|   bindTurnKey("Ctrl+A", West)
 | |
| 
 | |
|   bindWalkKey("E", NorthEast)
 | |
|   bindWalkKey("Q", NorthWest)
 | |
|   bindWalkKey("C", SouthEast)
 | |
|   bindWalkKey("Z", SouthWest)
 | |
| end
 | |
| 
 | |
| function disableWSAD()
 | |
|   if not wsadWalking then
 | |
|     return
 | |
|   end
 | |
|   wsadWalking = false
 | |
| 
 | |
|   unbindWalkKey("W")
 | |
|   unbindWalkKey("D")
 | |
|   unbindWalkKey("S")
 | |
|   unbindWalkKey("A")
 | |
| 
 | |
|   unbindTurnKey("Ctrl+W")
 | |
|   unbindTurnKey("Ctrl+D")
 | |
|   unbindTurnKey("Ctrl+S")
 | |
|   unbindTurnKey("Ctrl+A")
 | |
| 
 | |
|   unbindWalkKey("E")
 | |
|   unbindWalkKey("Q")
 | |
|   unbindWalkKey("C")
 | |
|   unbindWalkKey("Z")
 | |
| end
 | |
| 
 | |
| function bindWalkKey(key, dir)
 | |
|   local gameRootPanel = modules.game_interface.getRootPanel()
 | |
|   g_keyboard.bindKeyDown(key, function() changeWalkDir(dir) end, gameRootPanel, true)
 | |
|   g_keyboard.bindKeyUp(key, function() changeWalkDir(dir, true) end, gameRootPanel, true)
 | |
|   g_keyboard.bindKeyPress(key, function(c, k, ticks) smartWalk(dir, ticks) end, gameRootPanel)
 | |
| end
 | |
| 
 | |
| function unbindWalkKey(key)
 | |
|   local gameRootPanel = modules.game_interface.getRootPanel()
 | |
|   g_keyboard.unbindKeyDown(key, gameRootPanel)
 | |
|   g_keyboard.unbindKeyUp(key, gameRootPanel)
 | |
|   g_keyboard.unbindKeyPress(key, gameRootPanel)
 | |
| end
 | |
| 
 | |
| function bindTurnKey(key, dir)
 | |
|   turnKeys[key] = dir
 | |
|   local gameRootPanel = modules.game_interface.getRootPanel()
 | |
|   g_keyboard.bindKeyDown(key, function() turn(dir, false) end, gameRootPanel)
 | |
|   g_keyboard.bindKeyPress(key, function() turn(dir, true) end, gameRootPanel)
 | |
|   g_keyboard.bindKeyUp(key, function() local player = g_game.getLocalPlayer() if player then player:lockWalk(200) end end, gameRootPanel)
 | |
| end
 | |
| 
 | |
| function unbindTurnKey(key)
 | |
|   turnKeys[key] = nil
 | |
|   local gameRootPanel = modules.game_interface.getRootPanel()
 | |
|   g_keyboard.unbindKeyDown(key, gameRootPanel)
 | |
|   g_keyboard.unbindKeyPress(key, gameRootPanel)
 | |
|   g_keyboard.unbindKeyUp(key, gameRootPanel)
 | |
| end
 | |
| 
 | |
| function stopSmartWalk()
 | |
|   smartWalkDirs = {}
 | |
|   smartWalkDir = nil
 | |
| end
 | |
| 
 | |
| function changeWalkDir(dir, pop)
 | |
|   while table.removevalue(smartWalkDirs, dir) do end
 | |
|   if pop then
 | |
|     if #smartWalkDirs == 0 then
 | |
|       stopSmartWalk()
 | |
|       return
 | |
|     end
 | |
|   else
 | |
|     table.insert(smartWalkDirs, 1, dir)
 | |
|   end
 | |
| 
 | |
|   smartWalkDir = smartWalkDirs[1]
 | |
|   if modules.client_options.getOption('smartWalk') and #smartWalkDirs > 1 then
 | |
|     for _,d in pairs(smartWalkDirs) do
 | |
|       if (smartWalkDir == North and d == West) or (smartWalkDir == West and d == North) then
 | |
|         smartWalkDir = NorthWest
 | |
|         break
 | |
|       elseif (smartWalkDir == North and d == East) or (smartWalkDir == East and d == North) then
 | |
|         smartWalkDir = NorthEast
 | |
|         break
 | |
|       elseif (smartWalkDir == South and d == West) or (smartWalkDir == West and d == South) then
 | |
|         smartWalkDir = SouthWest
 | |
|         break
 | |
|       elseif (smartWalkDir == South and d == East) or (smartWalkDir == East and d == South) then
 | |
|         smartWalkDir = SouthEast
 | |
|         break
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 | |
| 
 | |
| function smartWalk(dir, ticks)
 | |
|   walkEvent = scheduleEvent(function() 
 | |
|     if g_keyboard.getModifiers() == KeyboardNoModifier then
 | |
|       local direction = smartWalkDir or dir
 | |
|       walk(direction, ticks)
 | |
|       return true
 | |
|     end
 | |
|     return false
 | |
|   end, 20)
 | |
| end
 | |
| 
 | |
| function canChangeFloorDown(pos)
 | |
|   pos.z = pos.z + 1
 | |
|   toTile = g_map.getTile(pos)
 | |
|   return toTile and toTile:hasElevation(3)
 | |
| end
 | |
| 
 | |
| function canChangeFloorUp(pos)
 | |
|   pos.z = pos.z - 1
 | |
|   toTile = g_map.getTile(pos)
 | |
|   return toTile and toTile:isWalkable()
 | |
| end
 | |
| 
 | |
| function onPositionChange(player, newPos, oldPos)
 | |
| end
 | |
| 
 | |
| function onWalk(player, newPos, oldPos)
 | |
|   if autoFinishNextServerWalk + 200 > g_clock.millis() then
 | |
|     player:finishServerWalking()
 | |
|   end
 | |
| end
 | |
| 
 | |
| function onTeleport(player, newPos, oldPos)
 | |
|   if not newPos or not oldPos then
 | |
|     return
 | |
|   end
 | |
|   -- floor change is also teleport
 | |
|   if math.abs(newPos.x - oldPos.x) >= 3 or math.abs(newPos.y - oldPos.y) >= 3 or math.abs(newPos.z - oldPos.z) >= 2 then  
 | |
|     -- far teleport, lock walk for 100ms
 | |
|     walkLock = g_clock.millis() + g_settings.getNumber('walkTeleportDelay')
 | |
|   else
 | |
|     walkLock = g_clock.millis() + g_settings.getNumber('walkStairsDelay')
 | |
|   end
 | |
|   nextWalkDir = nil -- cancel autowalk
 | |
| end
 | |
| 
 | |
| function onWalkFinish(player)
 | |
|   lastFinishedStep = g_clock.millis()
 | |
|   if nextWalkDir ~= nil then
 | |
|     removeEvent(autoWalkEvent)
 | |
|     autoWalkEvent = addEvent(function() if nextWalkDir ~= nil then walk(nextWalkDir, 0) end end, false)
 | |
|   end
 | |
| end
 | |
| 
 | |
| function onCancelWalk(player)
 | |
|   player:lockWalk(50)
 | |
| end
 | |
| 
 | |
| function walk(dir, ticks) 
 | |
|   lastManualWalk = g_clock.millis()
 | |
|   local player = g_game.getLocalPlayer()
 | |
|   if not player or g_game.isDead() or player:isDead() then
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   if player:isWalkLocked() then
 | |
|     nextWalkDir = nil
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   if g_game.isFollowing() then
 | |
|     g_game.cancelFollow()
 | |
|   end
 | |
| 
 | |
|   if player:isAutoWalking() then
 | |
|     if lastStop + 100 < g_clock.millis() then
 | |
|       lastStop = g_clock.millis()
 | |
|       player:stopAutoWalk()
 | |
|       g_game.stop()
 | |
|     end
 | |
|   end
 | |
|      
 | |
|   local dash = false
 | |
|   local ignoredCanWalk = false
 | |
|   if not g_game.getFeature(GameNewWalking) then
 | |
|     dash = g_settings.getBoolean("dash", false)
 | |
|   end
 | |
| 
 | |
|   local ticksToNextWalk = player:getStepTicksLeft()
 | |
|   if not player:canWalk(dir) then -- canWalk return false when previous walk is not finished or not confirmed by server
 | |
|     if dash then 
 | |
|       ignoredCanWalk = true
 | |
|     else
 | |
|       if ticksToNextWalk < 500 and (lastWalkDir ~= dir or ticks == 0) then
 | |
|         nextWalkDir = dir
 | |
|       end
 | |
|       if ticksToNextWalk < 30 and lastFinishedStep + 400 > g_clock.millis() and nextWalkDir == nil then -- clicked walk 20 ms too early, try to execute again as soon possible to keep smooth walking
 | |
|         nextWalkDir = dir
 | |
|       end
 | |
|       return
 | |
|     end
 | |
|   end
 | |
|   
 | |
|   --if nextWalkDir ~= nil and lastFinishedStep + 200 < g_clock.millis() then
 | |
|   --  print("Cancel " .. nextWalkDir)
 | |
|   --  nextWalkDir = nil
 | |
|   --end
 | |
|   if nextWalkDir ~= nil and nextWalkDir ~= lastWalkDir then 
 | |
|     dir = nextWalkDir
 | |
|   end
 | |
| 
 | |
|   local toPos = player:getPrewalkingPosition(true)
 | |
|   if dir == North then
 | |
|     toPos.y = toPos.y - 1
 | |
|   elseif dir == East then
 | |
|     toPos.x = toPos.x + 1
 | |
|   elseif dir == South then
 | |
|     toPos.y = toPos.y + 1
 | |
|   elseif dir == West then
 | |
|     toPos.x = toPos.x - 1
 | |
|   elseif dir == NorthEast then
 | |
|     toPos.x = toPos.x + 1
 | |
|     toPos.y = toPos.y - 1
 | |
|   elseif dir == SouthEast then
 | |
|     toPos.x = toPos.x + 1
 | |
|     toPos.y = toPos.y + 1
 | |
|   elseif dir == SouthWest then
 | |
|     toPos.x = toPos.x - 1
 | |
|     toPos.y = toPos.y + 1
 | |
|   elseif dir == NorthWest then
 | |
|     toPos.x = toPos.x - 1
 | |
|     toPos.y = toPos.y - 1
 | |
|   end
 | |
|   local toTile = g_map.getTile(toPos)
 | |
| 
 | |
|   if walkLock >= g_clock.millis() and lastWalkDir == dir then
 | |
|     nextWalkDir = nil
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   if firstStep and lastWalkDir == dir and lastWalk + g_settings.getNumber('walkFirstStepDelay') > g_clock.millis() then
 | |
|     firstStep = false
 | |
|     walkLock = lastWalk + g_settings.getNumber('walkFirstStepDelay')
 | |
|     return
 | |
|   end
 | |
|   
 | |
|   if dash and lastWalkDir == dir and lastWalk + 50 > g_clock.millis() then
 | |
|     return
 | |
|   end  
 | |
|   
 | |
|   firstStep = (not player:isWalking() and lastFinishedStep + 100 < g_clock.millis() and walkLock + 100 < g_clock.millis())
 | |
|   if player:isServerWalking() and not dash then
 | |
|     walkLock = walkLock + math.max(g_settings.getNumber('walkFirstStepDelay'), 100)
 | |
|   end
 | |
|   
 | |
|   nextWalkDir = nil
 | |
|   removeEvent(autoWalkEvent)
 | |
|   autoWalkEvent = nil
 | |
|   local preWalked = false
 | |
|   if toTile and toTile:isWalkable() then
 | |
|     if not player:isServerWalking() and not ignoredCanWalk then
 | |
|       player:preWalk(dir)
 | |
|       preWalked = true
 | |
|     end
 | |
|   else
 | |
|     local playerTile = player:getTile()
 | |
|     if (playerTile and playerTile:hasElevation(3) and canChangeFloorUp(toPos)) or canChangeFloorDown(toPos) or (toTile and toTile:isEmpty() and not toTile:isBlocking()) then
 | |
|       player:lockWalk(100)
 | |
|     elseif player:isServerWalking() then
 | |
|       g_game.stop()
 | |
|       return
 | |
|     elseif not toTile then
 | |
|       player:lockWalk(100) -- bug fix for missing stairs down on map
 | |
|     else
 | |
|       if g_app.isMobile() and dir <= Directions.West then 
 | |
|         turn(dir, ticks > 0)
 | |
|       end
 | |
|       return -- not walkable tile
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   if player:isServerWalking() and not dash then
 | |
|     g_game.stop()
 | |
|     player:finishServerWalking()
 | |
|     autoFinishNextServerWalk = g_clock.millis() + 200
 | |
|   end
 | |
|   g_game.walk(dir, preWalked)  
 | |
|   
 | |
|   if not firstStep and lastWalkDir ~= dir then
 | |
|     walkLock = g_clock.millis() + g_settings.getNumber('walkTurnDelay')    
 | |
|   end
 | |
|   
 | |
|   lastWalkDir = dir
 | |
|   lastWalk = g_clock.millis()
 | |
|   return true
 | |
| end
 | |
| 
 | |
| function turn(dir, repeated)
 | |
|   local player = g_game.getLocalPlayer()
 | |
|   if player:isWalking() and player:getWalkDirection() == dir and not player:isServerWalking() then
 | |
|     return
 | |
|   end
 | |
|   
 | |
|   removeEvent(walkEvent)
 | |
|   
 | |
|   if not repeated or (lastTurn + 100 < g_clock.millis()) then
 | |
|     g_game.turn(dir)
 | |
|     changeWalkDir(dir)
 | |
|     lastTurn = g_clock.millis()
 | |
|     if not repeated then
 | |
|       lastTurn = g_clock.millis() + 50
 | |
|     end
 | |
|     lastTurnDirection = dir
 | |
|     nextWalkDir = nil
 | |
|     player:lockWalk(g_settings.getNumber('walkCtrlTurnDelay'))
 | |
|   end
 | |
| end
 | |
| 
 | |
| function checkTurn()
 | |
|   for keys, direction in pairs(turnKeys) do
 | |
|     if g_keyboard.areKeysPressed(keys) then
 | |
|       turn(direction, false)
 | |
|     end
 | |
|   end
 | |
| end
 | 
