• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[OTClient Mod] Spacebar Attack

I made it attack automatically if you had no target already. It felt really nice together with a hotkey so you can switch target if the automatic one chooses a "bad" target and the issue with selecting targets offscreen was really annoying, nice to see that you fixed it
 
Last edited:
Hello! can you tell me please, how make with this module hold attack on latest target? so if target left from screen, i want keep target on last, and when off it, push escape, how to make this?
 
Unbind/bind
First of all, thanks for sharing the script/idea, I have had a fun time playing with it and learning some stuff because of it.

Moreover, during this, I found that BattleList keeps quite a messy table of dynamic indices and caches some creatures which are way out of range in a given moment, and also could grab some creatures from floors above me.
I downloaded the newest version of the battle list script from github, which seems to have dealt with the issue somewhat, but that entire module seems like it's doing so many unnecessary things.

I don't know for you, but upon testing it on the newest battle list, I once again ran into some issues with the script where it would attempt to target a nil entry in the table every time I finish a full rotation, thus always having 1 key press do nothing when rotating between several creatures.

Just curious, why didn't you detect creatures via getSpectator instead of battle list? And have you considered giving player access to choose which key they want to bind instead of space?

I have remade the script to do it like that, and it seems to be performing much more efficiently, not to mention the customization possibilities:

6uOz978.png


I think letting the player select the range at which it detects is also important, because some players may be playing a melee class that would like to prioritize creatures closer/closest to him instead of rotating through others that may be far away. (Yes, I know there is Sorting mechanism for battle list, but that's also another icky thing)

I'm curious to hear.
Thanks again, and if anyone wants to check/use my edits, you will find links below. I will just post parts that are relevant. (I didn't bother to change various function names so stuff might look weird)
Lua:
autoAttackButton = nil
alreadyAttacked = {}

function init()
end

function terminate()
  unbindSpacebar()
end

-------------------------------------------------
--Scripts----------------------------------------
-------------------------------------------------

local battleListCounter = 1

local function getDistanceBetween(p1, p2)
    return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
end

function chooseAimFromBattleList()
  local DetectDistance = modules.client_options.getOption('quicktargetrange')
  local spectators = g_map.getSpectators(g_game.getLocalPlayer():getPosition(), false)
  for k,v in pairs(spectators) do
        if getDistanceBetween(g_game.getLocalPlayer():getPosition(), v:getPosition()) <= DetectDistance then
            if isViableTarget(v) then
                if alreadyAttacked[v:getId()] then
                    if alreadyAttacked[v:getId()] ~= 1 then
                         g_game.attack(v)
                         break
                    end
                else
                     alreadyAttacked[v:getId()] = 1
                     g_game.attack(v)
                     break
                end
            end
        end

    if k == #spectators and alreadyAttacked ~= nil and next(alreadyAttacked) ~= nil then
        alreadyAttacked = nil
        alreadyAttacked = {}
        chooseAimFromBattleList()
    end
  end
end

function isViableTarget(creatureData)

  if creatureData:isMonster() then
    if creatureData:isInvisible() == false then
        return true
    end
  elseif creatureData:isPlayer() then
  --[[ Return False for now - when PVP is implemented, we will make a check whether the player wants to attack players or not via Options
    if g_game.isSafeFight() then
      return false
    else
      return true
    end ]]--
    return false
  elseif creatureData:isNpc() then
    return false
  else
    return false
  end
end

function bindSpacebar()
  g_keyboard.bindKeyPress(g_settings.getString("quicktargettingkey"), chooseAimFromBattleList)
end

function unbindSpacebar()
  g_keyboard.unbindKeyPress(g_settings.getString("quicktargettingkey"), chooseAimFromBattleList)
end

function toggleSpacebarBind()
  if g_settings.getBoolean("quicktargetting") == true then
    unbindSpacebar()
    bindSpacebar()
  else
    unbindSpacebar()
  end
end

-------------------------------------------------
--Scripts END------------------------------------
-------------------------------------------------




The code above will not work if inserted raw like this, mostly because there are names and references to things that might be called differently in your folders/scripts, so go over that yourself and fix the inconsistencies + make sure that the OTUI references refer to the proper parent/child relations, based on how you insert the OTUI code. The code I posted are children of a panel called extraOptions, you might do your option window differently, idk.
I believe that the easiest way to read monsters from battle list is by reading it from UI(BattlePanel) so users can set monster sorting settings by themselves

its just edit of chooseaimfrombattlelist:

Lua:
local function getDistanceBetween(p1, p2)
    return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
end

function chooseAimFromBattlelist()
  local DetectDistance = modules.client_options.getOption('quicktargetrange')
  local bPanel = modules.game_battle.battlePanel
  local battleListSize = bPanel:getChildCount()
  if g_game.isAttacking() == false then
    if battleListSize > 0 then
      local mobInfo = {}
      local myPos = g_game.getLocalPlayer():getPosition()
      for i = 1, battleListSize do
        mobInfo = bPanel:getChildByIndex(i):getCreature()
        if isViableTarget(mobInfo) then
          if getDistanceBetween(myPos, mobInfo:getPosition()) <= DetectDistance then
            g_game.attack(mobInfo)
            break
          end
        end
      end
    end
  else
    --if u want to cancel attak then just click space bar again
    g_game.cancelAttack()
  end
end

function isViableTarget(creatureData)
  if creatureData:isMonster() then
    return true
  elseif creatureData:isPlayer() and g_game.isSafeFight() == false then
    return true
  else
    return false
  end
end
 
Last edited:
First of all, thanks for sharing the script/idea, I have had a fun time playing with it and learning some stuff because of it.

Moreover, during this, I found that BattleList keeps quite a messy table of dynamic indices and caches some creatures which are way out of range in a given moment, and also could grab some creatures from floors above me.
I downloaded the newest version of the battle list script from github, which seems to have dealt with the issue somewhat, but that entire module seems like it's doing so many unnecessary things.

I don't know for you, but upon testing it on the newest battle list, I once again ran into some issues with the script where it would attempt to target a nil entry in the table every time I finish a full rotation, thus always having 1 key press do nothing when rotating between several creatures.

Just curious, why didn't you detect creatures via getSpectator instead of battle list? And have you considered giving player access to choose which key they want to bind instead of space?

I have remade the script to do it like that, and it seems to be performing much more efficiently, not to mention the customization possibilities:

6uOz978.png


I think letting the player select the range at which it detects is also important, because some players may be playing a melee class that would like to prioritize creatures closer/closest to him instead of rotating through others that may be far away. (Yes, I know there is Sorting mechanism for battle list, but that's also another icky thing)

I'm curious to hear.
Thanks again, and if anyone wants to check/use my edits, you will find links below. I will just post parts that are relevant. (I didn't bother to change various function names so stuff might look weird)
Lua:
autoAttackButton = nil
alreadyAttacked = {}

function init()
end

function terminate()
  unbindSpacebar()
end

-------------------------------------------------
--Scripts----------------------------------------
-------------------------------------------------

local battleListCounter = 1

local function getDistanceBetween(p1, p2)
    return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
end

function chooseAimFromBattleList()
  local DetectDistance = modules.client_options.getOption('quicktargetrange')
  local spectators = g_map.getSpectators(g_game.getLocalPlayer():getPosition(), false)
  for k,v in pairs(spectators) do
        if getDistanceBetween(g_game.getLocalPlayer():getPosition(), v:getPosition()) <= DetectDistance then
            if isViableTarget(v) then
                if alreadyAttacked[v:getId()] then
                    if alreadyAttacked[v:getId()] ~= 1 then
                         g_game.attack(v)
                         break
                    end
                else
                     alreadyAttacked[v:getId()] = 1
                     g_game.attack(v)
                     break
                end
            end
        end

    if k == #spectators and alreadyAttacked ~= nil and next(alreadyAttacked) ~= nil then
        alreadyAttacked = nil
        alreadyAttacked = {}
        chooseAimFromBattleList()
    end
  end
end

function isViableTarget(creatureData)

  if creatureData:isMonster() then
    if creatureData:isInvisible() == false then
        return true
    end
  elseif creatureData:isPlayer() then
  --[[ Return False for now - when PVP is implemented, we will make a check whether the player wants to attack players or not via Options
    if g_game.isSafeFight() then
      return false
    else
      return true
    end ]]--
    return false
  elseif creatureData:isNpc() then
    return false
  else
    return false
  end
end

function bindSpacebar()
  g_keyboard.bindKeyPress(g_settings.getString("quicktargettingkey"), chooseAimFromBattleList)
end

function unbindSpacebar()
  g_keyboard.unbindKeyPress(g_settings.getString("quicktargettingkey"), chooseAimFromBattleList)
end

function toggleSpacebarBind()
  if g_settings.getBoolean("quicktargetting") == true then
    unbindSpacebar()
    bindSpacebar()
  else
    unbindSpacebar()
  end
end

-------------------------------------------------
--Scripts END------------------------------------
-------------------------------------------------

In options lua, we add some new options:
Lua:
  quicktargetting = false,
  quicktargettingkey = "",
  quicktargetrange = 5

Below it, in locals:
Code:
local addQuickTargetButton = nil

In setup function:

Lua:
  addQuickTargetButton = generalPanel:getChildById("extraOptions"):getChildById("addHotkeyButton")
  local hotkeyButtonLabel = generalPanel:getChildById("extraOptions"):getChildById("hotkeyButtonLabel")

  local optvalue1 = g_settings.getString("quicktargettingkey")
  if optvalue1 == '' then optvalue1 = 'none' end
  hotkeyButtonLabel:setText('Currently: ' ..optvalue1)

In setoption function insert this in any appropriate place:

Lua:
elseif key == 'quicktargetrange' then
   generalPanel:getChildById('extraOptions'):getChildById('quickTargetRangeLabel'):setText(tr('Detect range: %d sqm', value))
elseif key == 'quicktargetting' then
    addQuickTargetButton = generalPanel:getChildById("extraOptions"):getChildById("addHotkeyButton")

    if generalPanel:getChildById("extraOptions"):getChildById("quicktargetting"):isChecked() then
        addQuickTargetButton:setEnabled(true)
        g_settings.set("quicktargetting", true)
    else
        addQuickTargetButton:setEnabled(false)
        g_settings.set("quicktargetting", false)
    end
modules.game_autoattack.toggleSpacebarBind()

New functions for options.lua which will be things that are triggered when new buttons are pressed:

Lua:
function addHotkey()
  local assignWindow = g_ui.createWidget('HotkeyAssignWindow', rootWidget)
  assignWindow:grabKeyboard()

  local comboLabel = assignWindow:getChildById('comboPreview')
  comboLabel.keyCombo = ''
  assignWindow.onKeyDown = hotkeyCapture
end

function hotkeyCapture(assignWindow, keyCode, keyboardModifiers)
  local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers)
  local comboPreview = assignWindow:getChildById('comboPreview')
  comboPreview:setText('Current key to add: ' ..keyCombo)
  comboPreview.keyCombo = keyCombo
  comboPreview:resizeToText()
  assignWindow:getChildById('addButton'):enable()
  assignWindow:getChildById('addButton').onClick = function() hotkeyCaptureOk(assignWindow, assignWindow:getChildById('comboPreview').keyCombo) end
  return true
end

function hotkeyCaptureOk(assignWindow, keyCombo)
  if g_settings.getBoolean("quicktargetting") == true then
      modules.game_autoattack.unbindSpacebar()
      generalPanel:getChildById("extraOptions"):getChildById("hotkeyButtonLabel"):setText('Currently: ' ..keyCombo)
      g_settings.set("quicktargettingkey", keyCombo)
      modules.game_autoattack.bindSpacebar()
  end
  assignWindow:destroy()
end

In OTUI you will need to have some new elements set up for it, i have some like:

Code:
    OptionCheckBox
      id: quicktargetting
      !text: tr('Enable Quick Targetting (Experimental)')
      !tooltip: tr('Upon pressing the set key, targets the next monster within the set detection range\nstarting with the ones closest to you.')
      font: verdana-11px-rounded
      anchors.left: parent.left
      margin-left: 5
      anchors.top: horizontalseparator1.bottom
      margin-top: 5
    Button
      id: addHotkeyButton
      !text: tr('Bind Key')
      width: 64
      anchors.left: parent.left
      anchors.top: prev.bottom
      margin-top: 5
      margin-left: 3
      @onClick: modules.client_options.addHotkey()
    Label
      id: hotkeyButtonLabel
      !text: tr('Currently: %s', 'none')
      anchors.left: prev.right
      anchors.top: prev.top
      font: verdana-11px-rounded
      margin-left: 5
      margin-top: 5
      text-auto-resize: true
    Label
      id: quickTargetRangeLabel
      color: #849a52
      font: verdana-11px-rounded
      anchors.left: parent.left
      anchors.top: addHotkeyButton.bottom
      margin-top: 5
      margin-left: 5
      text-auto-resize: true
      @onSetup: |
        local value = modules.client_options.getOption('quicktargetrange')
        self:setText(tr('Detect range: %d', value))
    OptionScrollbar
      id: quicktargetrange
      anchors.left: parent.left
      anchors.right: parent.right
      anchors.top: prev.bottom
      margin-left: 5
      margin-top: 5
      minimum: 1
      maximum: 10

The code above will not work if inserted raw like this, mostly because there are names and references to things that might be called differently in your folders/scripts, so go over that yourself and fix the inconsistencies + make sure that the OTUI references refer to the proper parent/child relations, based on how you insert the OTUI code. The code I posted are children of a panel called extraOptions, you might do your option window differently, idk.
i try to make this work but no result.. anyone how have result can share?
 
I think it would be really nice if you could do something like Alt + SpaceBar in order to go through the Battle List in reverse order... I had implemented it already, though it had a few bugs...
 
Back
Top