mirror of
https://github.com/mehah/otclient
synced 2026-08-15 16:29:06 -04:00
New Features: - Full Weapon Proficiency system: track weapon XP, levels and perks. - Dedicated Proficiency window with item grid, progress, perk selection, filters and search. - Top-bar proficiency widget for quick status and one-click access. - Real-time updates when equipping items and gaining proficiency XP. - Client and scripting support to send proficiency actions and apply perk selections to the server. Co-authored-by: kokekanon <114332266+kokekanon@users.noreply.github.com>
553 lines
17 KiB
Lua
553 lines
17 KiB
Lua
local iconTopMenu = nil
|
|
|
|
local inventoryShrink = false
|
|
|
|
local pvpModeRadioGroup = nil
|
|
local monkMirrorItem = nil
|
|
|
|
local function getInventoryUi()
|
|
if inventoryShrink then
|
|
return inventoryController.ui.offPanel
|
|
end
|
|
|
|
return inventoryController.ui.onPanel
|
|
end
|
|
|
|
local getSlotPanelBySlot = {
|
|
[InventorySlotHead] = function(ui) return ui.helmet, ui.helmet.helmet end,
|
|
[InventorySlotNeck] = function(ui) return ui.amulet, ui.amulet.amulet end,
|
|
[InventorySlotBack] = function(ui) return ui.backpack, ui.backpack.backpack end,
|
|
[InventorySlotBody] = function(ui) return ui.armor, ui.armor.armor end,
|
|
[InventorySlotRight] = function(ui) return ui.shield, ui.shield.shield end,
|
|
[InventorySlotLeft] = function(ui) return ui.sword, ui.sword.sword end,
|
|
[InventorySlotLeg] = function(ui) return ui.legs, ui.legs.legs end,
|
|
[InventorySlotFeet] = function(ui) return ui.boots, ui.boots.boots end,
|
|
[InventorySlotFinger] = function(ui) return ui.ring, ui.ring.ring end,
|
|
[InventorySlotAmmo] = function(ui) return ui.tools, ui.tools.tools end
|
|
}
|
|
|
|
local function isPlayerMonk()
|
|
local player = g_game.getLocalPlayer()
|
|
if not player then
|
|
return false
|
|
end
|
|
return player:isMonk()
|
|
end
|
|
|
|
local function updateMonkMirrorItem(leftItem)
|
|
if not g_game.getFeature(GameVocationMonk) then
|
|
return
|
|
end
|
|
if inventoryShrink then
|
|
return
|
|
end
|
|
|
|
local ui = getInventoryUi()
|
|
if not ui or not ui.shield or not ui.shield.item then
|
|
return
|
|
end
|
|
|
|
local shieldSlot = ui.shield
|
|
local shieldItemWidget = shieldSlot.item
|
|
|
|
if not isPlayerMonk() then
|
|
if monkMirrorItem then
|
|
monkMirrorItem = nil
|
|
end
|
|
return
|
|
end
|
|
|
|
local player = g_game.getLocalPlayer()
|
|
local realShieldItem = player and player:getInventoryItem(InventorySlotRight)
|
|
|
|
if realShieldItem then
|
|
monkMirrorItem = nil
|
|
return
|
|
end
|
|
|
|
if leftItem then
|
|
monkMirrorItem = leftItem
|
|
shieldItemWidget:setItem(leftItem)
|
|
shieldItemWidget:setOpacity(0.5)
|
|
shieldItemWidget:setDraggable(false)
|
|
shieldItemWidget:setEnabled(false)
|
|
shieldItemWidget:setFlipDirection(FlipDirection.Horizontal)
|
|
shieldSlot.shield:setEnabled(false)
|
|
else
|
|
monkMirrorItem = nil
|
|
shieldItemWidget:setItem(nil)
|
|
shieldItemWidget:setOpacity(1.0)
|
|
shieldItemWidget:setDraggable(true)
|
|
shieldItemWidget:setEnabled(true)
|
|
shieldItemWidget:setFlipDirection(FlipDirection.None)
|
|
shieldSlot.shield:setEnabled(true)
|
|
if shieldItemWidget.tier then
|
|
shieldItemWidget.tier:setVisible(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
local function walkEvent()
|
|
if modules.client_options.getOption('autoChaseOverride') then
|
|
if g_game.isAttacking() and g_game.getChaseMode() == ChaseOpponent then
|
|
selectPosture('stand', false)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function combatEvent()
|
|
if g_game.getChaseMode() == ChaseOpponent then
|
|
selectPosture('follow', true)
|
|
else
|
|
selectPosture('stand', true)
|
|
end
|
|
|
|
if g_game.getFightMode() == FightOffensive then
|
|
selectCombat('attack', true)
|
|
elseif g_game.getFightMode() == FightBalanced then
|
|
selectCombat('balanced', true)
|
|
elseif g_game.getFightMode() == FightDefensive then
|
|
selectCombat('defense', true)
|
|
end
|
|
end
|
|
|
|
local function inventoryEvent(player, slot, item, oldItem)
|
|
if inventoryShrink then
|
|
return
|
|
end
|
|
|
|
local ui = getInventoryUi()
|
|
local getSlotInfo = getSlotPanelBySlot[slot]
|
|
if not getSlotInfo then
|
|
return
|
|
end
|
|
|
|
if slot == InventorySlotRight and isPlayerMonk() and not item and monkMirrorItem then
|
|
return
|
|
end
|
|
|
|
if slot == InventorySlotRight and item then
|
|
local slotPanel, toggler = getSlotInfo(ui)
|
|
slotPanel.item:setOpacity(1.0)
|
|
slotPanel.item:setDraggable(true)
|
|
slotPanel.item:setEnabled(true)
|
|
slotPanel.item:setFlipDirection(FlipDirection.None)
|
|
monkMirrorItem = nil
|
|
end
|
|
|
|
local slotPanel, toggler = getSlotInfo(ui)
|
|
|
|
slotPanel.item:setItem(item)
|
|
toggler:setEnabled(not item)
|
|
slotPanel.item:setWidth(34)
|
|
slotPanel.item:setHeight(34)
|
|
|
|
slotPanel.item:setShowDuration(g_game.getFeature(GameThingClock) and modules.client_options.getOption('showExpiryInInvetory'))
|
|
slotPanel.item:setShowCharges(g_game.getFeature(GameThingCounter) and modules.client_options.getOption('showExpiryInInvetory'))
|
|
ItemsDatabase.setTier(slotPanel.item, item)
|
|
|
|
if slot == InventorySlotLeft then
|
|
if item and modules.game_proficiency then
|
|
g_game.sendWeaponProficiencyAction(WeaponProficiency.WEAPON_PROFICIENCY_ITEM_INFO, item:getId())
|
|
modules.game_proficiency.updateTopBarProficiency()
|
|
end
|
|
updateMonkMirrorItem(item)
|
|
end
|
|
end
|
|
|
|
local function onSoulChange(localPlayer, soul)
|
|
local ui = getInventoryUi()
|
|
if not localPlayer then
|
|
return
|
|
end
|
|
if not soul then
|
|
return
|
|
end
|
|
|
|
if ui.soulPanel and ui.soulPanel.soul then
|
|
ui.soulPanel.soul:setText(soul)
|
|
end
|
|
|
|
if ui.soulAndCapacity and ui.soulAndCapacity.soul then
|
|
ui.soulAndCapacity.soul:setText(soul)
|
|
end
|
|
end
|
|
|
|
local function onFreeCapacityChange(player, freeCapacity)
|
|
if not player then
|
|
return
|
|
end
|
|
|
|
if not freeCapacity then
|
|
return
|
|
end
|
|
if freeCapacity > 99999 then
|
|
freeCapacity = math.min(9999, math.floor(freeCapacity / 1000)) .. "k"
|
|
elseif freeCapacity > 999 then
|
|
freeCapacity = math.floor(freeCapacity)
|
|
elseif freeCapacity > 99 then
|
|
freeCapacity = math.floor(freeCapacity * 10) / 10
|
|
end
|
|
local ui = getInventoryUi()
|
|
if ui.capacityPanel and ui.capacityPanel.capacity then
|
|
ui.capacityPanel.capacity:setText(freeCapacity)
|
|
end
|
|
if ui.soulAndCapacity and ui.soulAndCapacity.capacity then
|
|
ui.soulAndCapacity.capacity:setText(freeCapacity)
|
|
end
|
|
end
|
|
|
|
function getIconsPanelOn()
|
|
return inventoryController.ui.onPanel.icons
|
|
end
|
|
|
|
function getIconsPanelOff()
|
|
return inventoryController.ui.offPanel.icons
|
|
end
|
|
|
|
local function refreshInventory_panel()
|
|
local player = g_game.getLocalPlayer()
|
|
if player then
|
|
onSoulChange(player, player:getSoul())
|
|
onFreeCapacityChange(player, player:getFreeCapacity())
|
|
end
|
|
if inventoryShrink then
|
|
return
|
|
end
|
|
|
|
for i = InventorySlotFirst, InventorySlotPurse do
|
|
if g_game.isOnline() then
|
|
inventoryEvent(player, i, player:getInventoryItem(i))
|
|
else
|
|
inventoryEvent(player, i, nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function refreshInventorySizes()
|
|
if inventoryShrink then
|
|
inventoryController.ui:setOn(false)
|
|
inventoryController.ui.onPanel:hide()
|
|
inventoryController.ui.offPanel:show()
|
|
else
|
|
inventoryController.ui:setOn(true)
|
|
inventoryController.ui.onPanel:show()
|
|
inventoryController.ui.offPanel:hide()
|
|
refreshInventory_panel()
|
|
end
|
|
combatEvent()
|
|
walkEvent()
|
|
modules.game_mainpanel.reloadMainPanelSizes()
|
|
end
|
|
|
|
function onSetChaseMode(self, selectedChaseModeButton)
|
|
if selectedChaseModeButton == nil then
|
|
return
|
|
end
|
|
|
|
local buttonId = selectedChaseModeButton:getId()
|
|
local chaseMode
|
|
if buttonId == 'followPosture' then
|
|
chaseMode = ChaseOpponent
|
|
else
|
|
chaseMode = DontChase
|
|
end
|
|
g_game.setChaseMode(chaseMode)
|
|
end
|
|
|
|
inventoryController = Controller:new()
|
|
inventoryController:setUI('inventory', modules.game_interface.getMainRightPanel())
|
|
|
|
function inventoryController:onInit()
|
|
refreshInventory_panel()
|
|
local ui = getInventoryUi()
|
|
|
|
connect(inventoryController.ui.onPanel.pvp, {
|
|
onCheckChange = onSetSafeFight
|
|
})
|
|
connect(inventoryController.ui.offPanel.pvp, {
|
|
onCheckChange = onSetSafeFight
|
|
})
|
|
connect(inventoryController.ui.onPanel.expert, {
|
|
onCheckChange = expertMode
|
|
})
|
|
pvpModeRadioGroup = UIRadioGroup.create()
|
|
pvpModeRadioGroup:addWidget(inventoryController.ui.onPanel.whiteDoveBox)
|
|
pvpModeRadioGroup:addWidget(inventoryController.ui.onPanel.whiteHandBox)
|
|
pvpModeRadioGroup:addWidget(inventoryController.ui.onPanel.yellowHandBox)
|
|
pvpModeRadioGroup:addWidget(inventoryController.ui.onPanel.redFistBox)
|
|
connect(pvpModeRadioGroup, {
|
|
onSelectionChange = onSetPVPMode
|
|
})
|
|
end
|
|
|
|
function inventoryController:onGameStart()
|
|
local player = g_game.getLocalPlayer()
|
|
if player then
|
|
local char = g_game.getCharacterName()
|
|
local lastCombatControls = g_settings.getNode('LastCombatControls')
|
|
if not table.empty(lastCombatControls) then
|
|
if lastCombatControls[char] then
|
|
g_game.setFightMode(lastCombatControls[char].fightMode)
|
|
g_game.setChaseMode(lastCombatControls[char].chaseMode)
|
|
g_game.setSafeFight(lastCombatControls[char].safeFight)
|
|
if lastCombatControls[char].pvpMode then
|
|
g_game.setPVPMode(lastCombatControls[char].pvpMode)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
inventoryController:registerEvents(LocalPlayer, {
|
|
onInventoryChange = inventoryEvent,
|
|
onSoulChange = onSoulChange,
|
|
onFreeCapacityChange = onFreeCapacityChange
|
|
}):execute()
|
|
|
|
inventoryController:registerEvents(g_game, {
|
|
onWalk = walkEvent,
|
|
onAutoWalk = walkEvent,
|
|
onFightModeChange = combatEvent,
|
|
onChaseModeChange = combatEvent,
|
|
onSafeFightChange = combatEvent,
|
|
onPVPModeChange = combatEvent
|
|
}):execute()
|
|
|
|
inventoryShrink = g_settings.getBoolean('mainpanel_shrink_inventory')
|
|
refreshInventorySizes()
|
|
refreshInventory_panel()
|
|
|
|
local elements = {
|
|
{inventoryController.ui.offPanel.blessings, inventoryController.ui.onPanel.blessings},
|
|
{inventoryController.ui.offPanel.expert, inventoryController.ui.onPanel.expert},
|
|
{inventoryController.ui.onPanel.whiteDoveBox},
|
|
{inventoryController.ui.onPanel.whiteHandBox},
|
|
{inventoryController.ui.onPanel.yellowHandBox},
|
|
{inventoryController.ui.onPanel.redFistBox}
|
|
}
|
|
|
|
local showBlessings = g_game.getClientVersion() >= 1000
|
|
local showPVPMode = g_game.getFeature(GamePVPMode)
|
|
|
|
for i, elementGroup in ipairs(elements) do
|
|
local show = (i == 1 and showBlessings) or (i > 1 and showPVPMode)
|
|
for _, element in ipairs(elementGroup) do
|
|
if show then
|
|
element:show()
|
|
else
|
|
element:hide()
|
|
end
|
|
end
|
|
end
|
|
inventoryController.ui.onPanel.purseButton:setVisible(g_game.getFeature(GamePurseSlot))
|
|
|
|
if isPlayerMonk() and player then
|
|
local leftItem = player:getInventoryItem(InventorySlotLeft)
|
|
if leftItem then
|
|
updateMonkMirrorItem(leftItem)
|
|
end
|
|
end
|
|
end
|
|
|
|
function inventoryController:onGameEnd()
|
|
monkMirrorItem = nil
|
|
|
|
local lastCombatControls = g_settings.getNode('LastCombatControls')
|
|
if not lastCombatControls then
|
|
lastCombatControls = {}
|
|
end
|
|
local player = g_game.getLocalPlayer()
|
|
if player then
|
|
local char = g_game.getCharacterName()
|
|
lastCombatControls[char] = {
|
|
fightMode = g_game.getFightMode(),
|
|
chaseMode = g_game.getChaseMode(),
|
|
safeFight = g_game.isSafeFight()
|
|
}
|
|
if g_game.getFeature(GamePVPMode) then
|
|
lastCombatControls[char].pvpMode = g_game.getPVPMode()
|
|
end
|
|
g_settings.setNode('LastCombatControls', lastCombatControls)
|
|
end
|
|
toggleAdventurerStyle(false)
|
|
end
|
|
|
|
function inventoryController:onTerminate()
|
|
if iconTopMenu then
|
|
iconTopMenu:destroy()
|
|
iconTopMenu = nil
|
|
end
|
|
if pvpModeRadioGroup then
|
|
disconnect(pvpModeRadioGroup, {
|
|
onSelectionChange = onSetPVPMode
|
|
})
|
|
pvpModeRadioGroup:destroy()
|
|
pvpModeRadioGroup = nil
|
|
end
|
|
end
|
|
|
|
function onSetSafeFight(self, checked)
|
|
if not checked then
|
|
inventoryController.ui.onPanel.pvp:setChecked(false)
|
|
inventoryController.ui.offPanel.pvp:setChecked(false)
|
|
else
|
|
inventoryController.ui.onPanel.pvp:setChecked(true)
|
|
inventoryController.ui.offPanel.pvp:setChecked(true)
|
|
end
|
|
g_game.setSafeFight(not checked)
|
|
if not checked then
|
|
g_game.cancelAttack()
|
|
end
|
|
end
|
|
|
|
function selectPosture(key, ignoreUpdate)
|
|
local ui = getInventoryUi()
|
|
if key == 'stand' then
|
|
ui.standPosture:setEnabled(false)
|
|
ui.followPosture:setEnabled(true)
|
|
if not ignoreUpdate then
|
|
g_game.setChaseMode(DontChase)
|
|
end
|
|
elseif key == 'follow' then
|
|
ui.standPosture:setEnabled(true)
|
|
ui.followPosture:setEnabled(false)
|
|
if not ignoreUpdate then
|
|
g_game.setChaseMode(ChaseOpponent)
|
|
end
|
|
end
|
|
end
|
|
|
|
function selectCombat(combat, ignoreUpdate)
|
|
local ui = getInventoryUi()
|
|
if combat == 'attack' then
|
|
ui.attack:setEnabled(false)
|
|
ui.balanced:setEnabled(true)
|
|
ui.defense:setEnabled(true)
|
|
if not ignoreUpdate then
|
|
g_game.setFightMode(FightOffensive)
|
|
end
|
|
elseif combat == 'balanced' then
|
|
ui.attack:setEnabled(true)
|
|
ui.balanced:setEnabled(false)
|
|
ui.defense:setEnabled(true)
|
|
if not ignoreUpdate then
|
|
g_game.setFightMode(FightBalanced)
|
|
end
|
|
elseif combat == 'defense' then
|
|
ui.attack:setEnabled(true)
|
|
ui.balanced:setEnabled(true)
|
|
ui.defense:setEnabled(false)
|
|
if not ignoreUpdate then
|
|
g_game.setFightMode(FightDefensive)
|
|
end
|
|
end
|
|
end
|
|
|
|
function expertMode(self, checked)
|
|
local ui = getInventoryUi()
|
|
|
|
ui.whiteDoveBox:setVisible(checked)
|
|
ui.whiteHandBox:setVisible(checked)
|
|
ui.yellowHandBox:setVisible(checked)
|
|
ui.redFistBox:setVisible(checked)
|
|
end
|
|
|
|
function onSetPVPMode(self, selectedPVPButton)
|
|
if selectedPVPButton == nil then
|
|
return
|
|
end
|
|
|
|
local buttonId = selectedPVPButton:getId()
|
|
local pvpMode = PVPWhiteDove
|
|
|
|
if buttonId == 'whiteDoveBox' then
|
|
pvpMode = PVPWhiteDove
|
|
elseif buttonId == 'whiteHandBox' then
|
|
pvpMode = PVPWhiteHand
|
|
elseif buttonId == 'yellowHandBox' then
|
|
pvpMode = PVPYellowHand
|
|
elseif buttonId == 'redFistBox' then
|
|
pvpMode = PVPRedFist
|
|
end
|
|
g_game.setPVPMode(pvpMode)
|
|
end
|
|
|
|
function changeInventorySize()
|
|
inventoryShrink = not inventoryShrink
|
|
g_settings.set('mainpanel_shrink_inventory', inventoryShrink)
|
|
refreshInventorySizes()
|
|
modules.game_mainpanel.reloadMainPanelSizes()
|
|
local player = g_game.getLocalPlayer()
|
|
if player and g_game.isOnline() then
|
|
onFreeCapacityChange(player, player:getFreeCapacity())
|
|
onSoulChange(player, player:getSoul())
|
|
end
|
|
end
|
|
|
|
function getSlot5()
|
|
return inventoryController.ui.onPanel.shield
|
|
end
|
|
|
|
function reloadInventory()
|
|
|
|
for slot, getSlotInfo in pairs(getSlotPanelBySlot) do
|
|
local ui = getInventoryUi()
|
|
local slotPanel, toggler = getSlotInfo(ui)
|
|
if slotPanel then
|
|
local player = g_game.getLocalPlayer()
|
|
if player then
|
|
inventoryEvent(player, slot, player:getInventoryItem(slot))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function extendedView(extendedView)
|
|
if extendedView then
|
|
if not iconTopMenu then
|
|
iconTopMenu = modules.client_topmenu.addTopRightToggleButton('inventory', tr('Show inventory'),
|
|
'/images/topbuttons/inventory', toggle)
|
|
iconTopMenu:setOn(inventoryController.ui:isVisible())
|
|
inventoryController.ui:setBorderColor('black')
|
|
inventoryController.ui:setBorderWidth(2)
|
|
end
|
|
else
|
|
if iconTopMenu then
|
|
iconTopMenu:destroy()
|
|
iconTopMenu = nil
|
|
end
|
|
inventoryController.ui:setBorderColor('alpha')
|
|
inventoryController.ui:setBorderWidth(0)
|
|
local mainRightPanel = modules.game_interface.getMainRightPanel()
|
|
if not mainRightPanel:hasChild(inventoryController.ui) then
|
|
mainRightPanel:insertChild(3, inventoryController.ui)
|
|
end
|
|
inventoryController.ui:show()
|
|
end
|
|
inventoryController.ui.moveOnlyToMain = not extendedView
|
|
|
|
end
|
|
|
|
function toggle()
|
|
if iconTopMenu:isOn() then
|
|
inventoryController.ui:hide()
|
|
iconTopMenu:setOn(false)
|
|
else
|
|
inventoryController.ui:show()
|
|
iconTopMenu:setOn(true)
|
|
end
|
|
end
|
|
|
|
function toggleAdventurerStyle(hasBlessing)
|
|
for slot, getSlotInfo in pairs(getSlotPanelBySlot) do
|
|
local ui = getInventoryUi()
|
|
local slotPanel, toggler = getSlotInfo(ui)
|
|
if slotPanel then
|
|
slotPanel:setOn(hasBlessing)
|
|
end
|
|
end
|
|
end
|
|
|
|
function getButtonBlessings()
|
|
return getInventoryUi().blessings
|
|
end
|