mirror of
				https://github.com/OTCv8/otclientv8.git
				synced 2025-10-31 10:56:24 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			458 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			458 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| battleWindow = nil
 | |
| battleButton = nil
 | |
| battlePanel = nil
 | |
| filterPanel = nil
 | |
| toggleFilterButton = nil
 | |
| 
 | |
| mouseWidget = nil
 | |
| updateEvent = nil
 | |
| 
 | |
| hoveredCreature = nil
 | |
| newHoveredCreature = nil
 | |
| prevCreature = nil
 | |
| 
 | |
| battleButtons = {}
 | |
| local ageNumber = 1
 | |
| local ages = {}
 | |
| 
 | |
| function init()  
 | |
|   g_ui.importStyle('battlebutton')
 | |
|   battleButton = modules.client_topmenu.addRightGameToggleButton('battleButton', tr('Battle') .. ' (Ctrl+B)', '/images/topbuttons/battle', toggle, false, 2)
 | |
|   battleButton:setOn(true)
 | |
|   battleWindow = g_ui.loadUI('battle', modules.game_interface.getRightPanel())
 | |
|   g_keyboard.bindKeyDown('Ctrl+B', toggle)
 | |
| 
 | |
|   -- this disables scrollbar auto hiding
 | |
|   local scrollbar = battleWindow:getChildById('miniwindowScrollBar')
 | |
|   scrollbar:mergeStyle({ ['$!on'] = { }})
 | |
| 
 | |
|   battlePanel = battleWindow:recursiveGetChildById('battlePanel')
 | |
| 
 | |
|   filterPanel = battleWindow:recursiveGetChildById('filterPanel')
 | |
|   toggleFilterButton = battleWindow:recursiveGetChildById('toggleFilterButton')
 | |
| 
 | |
|   if isHidingFilters() then
 | |
|     hideFilterPanel()
 | |
|   end
 | |
| 
 | |
|   local sortTypeBox = filterPanel.sortPanel.sortTypeBox
 | |
|   local sortOrderBox = filterPanel.sortPanel.sortOrderBox
 | |
| 
 | |
|   mouseWidget = g_ui.createWidget('UIButton')
 | |
|   mouseWidget:setVisible(false)
 | |
|   mouseWidget:setFocusable(false)
 | |
|   mouseWidget.cancelNextRelease = false
 | |
| 
 | |
|   battleWindow:setContentMinimumHeight(80)
 | |
| 
 | |
|   sortTypeBox:addOption('Name', 'name')
 | |
|   sortTypeBox:addOption('Distance', 'distance')
 | |
|   sortTypeBox:addOption('Total age', 'age')
 | |
|   sortTypeBox:addOption('Screen age', 'screenage')
 | |
|   sortTypeBox:addOption('Health', 'health')
 | |
|   sortTypeBox:setCurrentOptionByData(getSortType())
 | |
|   sortTypeBox.onOptionChange = onChangeSortType
 | |
| 
 | |
|   sortOrderBox:addOption('Asc.', 'asc')
 | |
|   sortOrderBox:addOption('Desc.', 'desc')
 | |
|   sortOrderBox:setCurrentOptionByData(getSortOrder())
 | |
|   sortOrderBox.onOptionChange = onChangeSortOrder
 | |
| 
 | |
|   battleWindow:setup()
 | |
|   
 | |
|   for i=1,30 do
 | |
|     local battleButton = g_ui.createWidget('BattleButton', battlePanel)
 | |
|     battleButton:setup()
 | |
|     battleButton:hide()
 | |
|     battleButton.onHoverChange = onBattleButtonHoverChange
 | |
|     battleButton.onMouseRelease = onBattleButtonMouseRelease
 | |
|     table.insert(battleButtons, battleButton)
 | |
|   end
 | |
|   
 | |
|   updateBattleList()
 | |
|   
 | |
|   connect(LocalPlayer, {
 | |
|     onPositionChange = onPlayerPositionChange
 | |
|   })
 | |
|   connect(Creature, {
 | |
|     onAppear = updateSquare,
 | |
|     onDisappear = updateSquare
 | |
|   })  
 | |
|   connect(g_game, { 
 | |
|     onAttackingCreatureChange = updateSquare,
 | |
|     onFollowingCreatureChange = updateSquare 
 | |
|   })
 | |
| end
 | |
| 
 | |
| function terminate()
 | |
|   if battleButton == nil then
 | |
|     return
 | |
|   end
 | |
|   
 | |
|   battleButtons = {}
 | |
|   
 | |
|   g_keyboard.unbindKeyDown('Ctrl+B')
 | |
|   battleButton:destroy()
 | |
|   battleWindow:destroy()
 | |
|   mouseWidget:destroy()
 | |
| 	
 | |
|   disconnect(LocalPlayer, {
 | |
|     onPositionChange = onPlayerPositionChange
 | |
|   })
 | |
|   disconnect(Creature, {
 | |
|     onAppear = onCreatureAppear,
 | |
|     onDisappear = onCreatureDisappear
 | |
|   })  
 | |
|   disconnect(g_game, { 
 | |
|     onAttackingCreatureChange = updateSquare,
 | |
|     onFollowingCreatureChange = updateSquare 
 | |
|   })
 | |
| 
 | |
|   removeEvent(updateEvent)
 | |
| end
 | |
| 
 | |
| function toggle()
 | |
|   if battleButton:isOn() then
 | |
|     battleWindow:close()
 | |
|     battleButton:setOn(false)
 | |
|   else
 | |
|     battleWindow:open()
 | |
|     battleButton:setOn(true)
 | |
|   end
 | |
| end
 | |
| 
 | |
| function onMiniWindowClose()
 | |
|   battleButton:setOn(false)
 | |
| end
 | |
| 
 | |
| function getSortType()
 | |
|   local settings = g_settings.getNode('BattleList')
 | |
|   if not settings then
 | |
|     if g_app.isMobile() then
 | |
|       return 'distance'
 | |
|     else
 | |
|       return 'name'
 | |
|     end
 | |
|   end
 | |
|   return settings['sortType']
 | |
| end
 | |
| 
 | |
| function setSortType(state)
 | |
|   settings = {}
 | |
|   settings['sortType'] = state
 | |
|   g_settings.mergeNode('BattleList', settings)
 | |
| 
 | |
|   checkCreatures()
 | |
| end
 | |
| 
 | |
| function getSortOrder()
 | |
|   local settings = g_settings.getNode('BattleList')
 | |
|   if not settings then
 | |
|     return 'asc'
 | |
|   end
 | |
|   return settings['sortOrder']
 | |
| end
 | |
| 
 | |
| function setSortOrder(state)
 | |
|   settings = {}
 | |
|   settings['sortOrder'] = state
 | |
|   g_settings.mergeNode('BattleList', settings)
 | |
| 
 | |
|   checkCreatures()
 | |
| end
 | |
| 
 | |
| function isSortAsc()
 | |
|     return getSortOrder() == 'asc'
 | |
| end
 | |
| 
 | |
| function isSortDesc()
 | |
|     return getSortOrder() == 'desc'
 | |
| end
 | |
| 
 | |
| function isHidingFilters()
 | |
|   local settings = g_settings.getNode('BattleList')
 | |
|   if not settings then
 | |
|     return false
 | |
|   end
 | |
|   return settings['hidingFilters']
 | |
| end
 | |
| 
 | |
| function setHidingFilters(state)
 | |
|   settings = {}
 | |
|   settings['hidingFilters'] = state
 | |
|   g_settings.mergeNode('BattleList', settings)
 | |
| end
 | |
| 
 | |
| function hideFilterPanel()
 | |
|   filterPanel.originalHeight = filterPanel:getHeight()
 | |
|   filterPanel:setHeight(0)
 | |
|   toggleFilterButton:getParent():setMarginTop(0)
 | |
|   toggleFilterButton:setImageClip(torect("0 0 21 12"))
 | |
|   setHidingFilters(true)
 | |
|   filterPanel:setVisible(false)
 | |
| end
 | |
| 
 | |
| function showFilterPanel()
 | |
|   toggleFilterButton:getParent():setMarginTop(5)
 | |
|   filterPanel:setHeight(filterPanel.originalHeight)
 | |
|   toggleFilterButton:setImageClip(torect("21 0 21 12"))
 | |
|   setHidingFilters(false)
 | |
|   filterPanel:setVisible(true)
 | |
| end
 | |
| 
 | |
| function toggleFilterPanel()
 | |
|   if filterPanel:isVisible() then
 | |
|     hideFilterPanel()
 | |
|   else
 | |
|     showFilterPanel()
 | |
|   end
 | |
| end
 | |
| 
 | |
| function onChangeSortType(comboBox, option, value)
 | |
|   setSortType(value:lower())
 | |
| end
 | |
| 
 | |
| function onChangeSortOrder(comboBox, option, value)
 | |
|   -- Replace dot in option name
 | |
|   setSortOrder(value:lower():gsub('[.]', ''))
 | |
| end
 | |
| 
 | |
| -- functions
 | |
| function updateBattleList() 
 | |
|   removeEvent(updateEvent)
 | |
| 	updateEvent = scheduleEvent(updateBattleList, 100)
 | |
|   checkCreatures()
 | |
| end
 | |
| 
 | |
| function checkCreatures()
 | |
|   if not battlePanel or not g_game.isOnline() then
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   local player = g_game.getLocalPlayer()
 | |
|   if not player then
 | |
|     return
 | |
|   end
 | |
|   
 | |
|   local dimension = modules.game_interface.getMapPanel():getVisibleDimension()
 | |
|   local spectators = g_map.getSpectatorsInRangeEx(player:getPosition(), false, math.floor(dimension.width / 2), math.floor(dimension.width / 2), math.floor(dimension.height / 2), math.floor(dimension.height / 2))
 | |
|   local maxCreatures = battlePanel:getChildCount()
 | |
|   
 | |
|   local creatures = {}
 | |
|   local now = g_clock.millis()
 | |
|   local resetAgePoint = now - 250
 | |
|   for _, creature in ipairs(spectators) do
 | |
|     if doCreatureFitFilters(creature) and #creatures < maxCreatures then
 | |
|       if not creature.lastSeen or creature.lastSeen < resetAgePoint then
 | |
|         creature.screenAge = now        
 | |
|       end      
 | |
|       creature.lastSeen = now
 | |
|       if not ages[creature:getId()] then
 | |
|         if ageNumber > 1000 then
 | |
|           ageNumber = 1
 | |
|           ages = {}
 | |
|         end
 | |
|         ages[creature:getId()] = ageNumber
 | |
|         ageNumber = ageNumber + 1
 | |
|       end
 | |
|       table.insert(creatures, creature)	
 | |
|     end
 | |
|   end
 | |
|   
 | |
|   updateSquare()
 | |
|   sortCreatures(creatures)
 | |
|   battlePanel:getLayout():disableUpdates()
 | |
|   
 | |
|   -- sorting
 | |
|   local ascOrder = isSortAsc()
 | |
|   for i=1,#creatures do  
 | |
| 	  local creature = creatures[i]
 | |
| 	  if ascOrder then
 | |
|       creature = creatures[#creatures - i + 1]
 | |
| 	  end
 | |
|     local battleButton = battleButtons[i]      
 | |
|     battleButton:creatureSetup(creature)
 | |
|     battleButton:show()
 | |
|     battleButton:setOn(true)
 | |
|   end
 | |
|   
 | |
|   if g_app.isMobile() and #creatures > 0 then
 | |
|     onBattleButtonHoverChange(battleButtons[1], true)
 | |
|   end
 | |
|     
 | |
|   for i=#creatures + 1,maxCreatures do
 | |
|     if battleButtons[i]:isHidden() then break end
 | |
|     battleButtons[i]:hide()
 | |
|     battleButton:setOn(false)
 | |
|   end
 | |
| 
 | |
|   battlePanel:getLayout():enableUpdates()
 | |
|   battlePanel:getLayout():update()
 | |
| end
 | |
| 
 | |
| function doCreatureFitFilters(creature)
 | |
|   if creature:isLocalPlayer() then
 | |
|     return false
 | |
|   end
 | |
|   if creature:getHealthPercent() <= 0 then
 | |
|     return false
 | |
|   end
 | |
| 
 | |
|   local pos = creature:getPosition()
 | |
|   if not pos then return false end
 | |
| 
 | |
|   local localPlayer = g_game.getLocalPlayer()
 | |
|   if pos.z ~= localPlayer:getPosition().z or not creature:canBeSeen() then return false end
 | |
| 
 | |
|   local hidePlayers = filterPanel.buttons.hidePlayers:isChecked()
 | |
|   local hideNPCs = filterPanel.buttons.hideNPCs:isChecked()
 | |
|   local hideMonsters = filterPanel.buttons.hideMonsters:isChecked()
 | |
|   local hideSkulls = filterPanel.buttons.hideSkulls:isChecked()
 | |
|   local hideParty = filterPanel.buttons.hideParty:isChecked()
 | |
| 
 | |
|   if hidePlayers and creature:isPlayer() then
 | |
|     return false
 | |
|   elseif hideNPCs and creature:isNpc() then
 | |
|     return false
 | |
|   elseif hideMonsters and creature:isMonster() then
 | |
|     return false
 | |
|   elseif hideSkulls and creature:isPlayer() and creature:getSkull() == SkullNone then
 | |
|     return false
 | |
|   elseif hideParty and creature:getShield() > ShieldWhiteBlue then
 | |
|     return false
 | |
|   end
 | |
| 
 | |
|   return true
 | |
| end
 | |
| 
 | |
| local function getDistanceBetween(p1, p2)
 | |
|     return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
 | |
| end
 | |
| 
 | |
| function sortCreatures(creatures)
 | |
|   local player = g_game.getLocalPlayer()
 | |
|   
 | |
|   if getSortType() == 'distance' then
 | |
|     local playerPos = player:getPosition()
 | |
|     table.sort(creatures, function(a, b) 
 | |
|       if getDistanceBetween(playerPos, a:getPosition()) == getDistanceBetween(playerPos, b:getPosition()) then
 | |
|         return ages[a:getId()] > ages[b:getId()]
 | |
|       end
 | |
|       return getDistanceBetween(playerPos, a:getPosition()) > getDistanceBetween(playerPos, b:getPosition()) 
 | |
|     end)
 | |
|   elseif getSortType() == 'health' then
 | |
|     table.sort(creatures, function(a, b) 
 | |
|       if a:getHealthPercent() == b:getHealthPercent() then
 | |
|         return ages[a:getId()] > ages[b:getId()]
 | |
|       end
 | |
|       return a:getHealthPercent() > b:getHealthPercent() 
 | |
|     end)
 | |
|   elseif getSortType() == 'age' then
 | |
|     table.sort(creatures, function(a, b) return ages[a:getId()] > ages[b:getId()] end)
 | |
|   elseif getSortType() == 'screenage' then
 | |
|     table.sort(creatures, function(a, b) return a.screenAge > b.screenAge end)
 | |
|   else -- name
 | |
|     table.sort(creatures, function(a, b)
 | |
|       if a:getName():lower() == b:getName():lower() then
 | |
|         return ages[a:getId()] > ages[b:getId()]
 | |
|       end
 | |
|       return a:getName():lower() > b:getName():lower() 
 | |
|     end)
 | |
|   end
 | |
| end
 | |
| 
 | |
| -- other functions
 | |
| function onBattleButtonMouseRelease(self, mousePosition, mouseButton)
 | |
|   if mouseWidget.cancelNextRelease then
 | |
|     mouseWidget.cancelNextRelease = false
 | |
|     return false
 | |
|   end
 | |
|   if not self.creature then
 | |
|     return false
 | |
|   end
 | |
|   if ((g_mouse.isPressed(MouseLeftButton) and mouseButton == MouseRightButton)
 | |
|     or (g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then
 | |
|     mouseWidget.cancelNextRelease = true
 | |
|     g_game.look(self.creature, true)
 | |
|     return true
 | |
|   elseif mouseButton == MouseLeftButton and g_keyboard.isShiftPressed() then
 | |
|     g_game.look(self.creature, true)
 | |
|     return true
 | |
|   elseif mouseButton == MouseRightButton and not g_mouse.isPressed(MouseLeftButton) then
 | |
|     modules.game_interface.createThingMenu(mousePosition, nil, nil, self.creature)
 | |
|     return true
 | |
|   elseif mouseButton == MouseLeftButton and not g_mouse.isPressed(MouseRightButton) then
 | |
|     if self.isTarget then
 | |
|       g_game.cancelAttack()
 | |
|     else
 | |
|       g_game.attack(self.creature)
 | |
|     end
 | |
|     return true
 | |
|   end
 | |
|   return false
 | |
| end
 | |
| 
 | |
| function onBattleButtonHoverChange(battleButton, hovered)
 | |
|   if not hovered then
 | |
|     newHoveredCreature = nil    
 | |
|   else
 | |
|     newHoveredCreature = battleButton.creature
 | |
|   end
 | |
|   if battleButton.isHovered ~= hovered then
 | |
|     battleButton.isHovered = hovered
 | |
|     battleButton:update()
 | |
|   end
 | |
|   updateSquare()
 | |
| end
 | |
| 
 | |
| function onPlayerPositionChange(creature, newPos, oldPos)
 | |
|   addEvent(checkCreatures)
 | |
| end
 | |
| 
 | |
| local CreatureButtonColors = {
 | |
|   onIdle = {notHovered = '#888888', hovered = '#FFFFFF' },
 | |
|   onTargeted = {notHovered = '#FF0000', hovered = '#FF8888' },
 | |
|   onFollowed = {notHovered = '#00FF00', hovered = '#88FF88' }
 | |
| }
 | |
| 
 | |
| function updateSquare()
 | |
|   local following = g_game.getFollowingCreature()
 | |
|   local attacking = g_game.getAttackingCreature()
 | |
|     
 | |
|   if newHoveredCreature == nil then
 | |
|     if hoveredCreature ~= nil then
 | |
|       hoveredCreature:hideStaticSquare()
 | |
|       hoveredCreature = nil
 | |
|     end
 | |
|   else
 | |
|     if hoveredCreature ~= nil then
 | |
|       hoveredCreature:hideStaticSquare()
 | |
|     end
 | |
|     hoveredCreature = newHoveredCreature
 | |
|     hoveredCreature:showStaticSquare(CreatureButtonColors.onIdle.hovered)
 | |
|   end
 | |
|   
 | |
|   local color = CreatureButtonColors.onIdle
 | |
|   local creature = nil
 | |
|   if attacking then
 | |
|     color = CreatureButtonColors.onTargeted
 | |
|     creature = attacking
 | |
|   elseif following then
 | |
|     color = CreatureButtonColors.onFollowed
 | |
|     creature = following
 | |
|   end
 | |
| 
 | |
|   if prevCreature ~= creature then
 | |
|     if prevCreature ~= nil then
 | |
|       prevCreature:hideStaticSquare()
 | |
|     end
 | |
|     prevCreature = creature
 | |
|   end
 | |
|   
 | |
|   if not creature then
 | |
|     return
 | |
|   end
 | |
|   
 | |
|   color = creature == hoveredCreature and color.hovered or color.notHovered
 | |
|   creature:showStaticSquare(color)
 | |
| end | 
