Implements battlelist sorting
Features: Sort by age, name, health or distance Sort ascending or descending Button to toggle filters and sort options http://i.imgur.com/cSCBwr4.png closes 213
This commit is contained in:
		
							parent
							
								
									be071c7103
								
							
						
					
					
						commit
						c8185474de
					
				|  | @ -1,11 +1,16 @@ | |||
| battleWindow = nil | ||||
| battleButton = nil | ||||
| battlePanel = nil | ||||
| filterPanel = nil | ||||
| toggleFilterButton = nil | ||||
| lastBattleButtonSwitched = nil | ||||
| battleButtonsByCreaturesList = {} | ||||
| creatureAgeList = {} | ||||
| 
 | ||||
| mouseWidget = nil | ||||
| 
 | ||||
| sortTypeBox = nil | ||||
| sortOrderBox = nil | ||||
| hidePlayersButton = nil | ||||
| hideNPCsButton = nil | ||||
| hideMonstersButton = nil | ||||
|  | @ -25,6 +30,15 @@ function init() | |||
| 
 | ||||
|   battlePanel = battleWindow:recursiveGetChildById('battlePanel') | ||||
| 
 | ||||
|   filterPanel = battleWindow:recursiveGetChildById('filterPanel') | ||||
|   toggleFilterButton = battleWindow:recursiveGetChildById('toggleFilterButton') | ||||
|    | ||||
|   if isHidingFilters() then | ||||
|     hideFilterPanel() | ||||
|   end | ||||
|    | ||||
|   sortTypeBox = battleWindow:recursiveGetChildById('sortTypeBox') | ||||
|   sortOrderBox = battleWindow:recursiveGetChildById('sortOrderBox') | ||||
|   hidePlayersButton = battleWindow:recursiveGetChildById('hidePlayers') | ||||
|   hideNPCsButton = battleWindow:recursiveGetChildById('hideNPCs') | ||||
|   hideMonstersButton = battleWindow:recursiveGetChildById('hideMonsters') | ||||
|  | @ -38,6 +52,18 @@ function init() | |||
| 
 | ||||
|   battleWindow:setContentMinimumHeight(80) | ||||
| 
 | ||||
|   sortTypeBox:addOption('Name', 'name') | ||||
|   sortTypeBox:addOption('Distance', 'distance') | ||||
|   sortTypeBox:addOption('Age', 'age') | ||||
|   sortTypeBox:addOption('Health', 'health') | ||||
|   sortTypeBox:setCurrentOptionByData(getSortType()) | ||||
|   sortTypeBox.onOptionChange = onChangeSortType | ||||
| 
 | ||||
|   sortOrderBox:addOption('Asc.', 'asc') | ||||
|   sortOrderBox:addOption('Desc.', 'desc') | ||||
|   sortOrderBox:setCurrentOptionByData(getSortOrder()) | ||||
|   sortOrderBox.onOptionChange = onChangeSortOrder | ||||
|    | ||||
|   connect(Creature, { | ||||
|     onSkullChange = updateCreatureSkull, | ||||
|     onEmblemChange = updateCreatureEmblem, | ||||
|  | @ -47,6 +73,10 @@ function init() | |||
|     onAppear = onCreatureAppear, | ||||
|     onDisappear = onCreatureDisappear | ||||
|   }) | ||||
|    | ||||
|   connect(LocalPlayer, { | ||||
|     onPositionChange = onCreaturePositionChange | ||||
|   }) | ||||
| 
 | ||||
|   connect(g_game, { | ||||
|     onAttackingCreatureChange = onAttack, | ||||
|  | @ -75,6 +105,10 @@ function terminate() | |||
|     onDisappear = onCreatureDisappear | ||||
|   }) | ||||
| 
 | ||||
|   disconnect(LocalPlayer, { | ||||
|     onPositionChange = onCreaturePositionChange | ||||
|   }) | ||||
|    | ||||
|   disconnect(g_game, { | ||||
|     onAttackingCreatureChange = onAttack, | ||||
|     onFollowingCreatureChange = onFollow, | ||||
|  | @ -96,6 +130,93 @@ function onMiniWindowClose() | |||
|   battleButton:setOn(false) | ||||
| end | ||||
| 
 | ||||
| function getSortType() | ||||
|   local settings = g_settings.getNode('BattleList') | ||||
|   if not settings then | ||||
|     return 'name' | ||||
|   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) | ||||
|   setSortType(option:lower()) | ||||
| end | ||||
| 
 | ||||
| function onChangeSortOrder(comboBox, option) | ||||
|   setSortOrder(option:lower():gsub('[.]', ''))  -- Replace dot in option name | ||||
| end | ||||
| 
 | ||||
| function checkCreatures() | ||||
|   removeAllCreatures() | ||||
| 
 | ||||
|  | @ -151,15 +272,42 @@ end | |||
| function onCreatureHealthPercentChange(creature, health) | ||||
|   local battleButton = battleButtonsByCreaturesList[creature:getId()] | ||||
|   if battleButton then | ||||
|     if getSortType() == 'health' then | ||||
|       removeCreature(creature) | ||||
|       addCreature(creature) | ||||
|       return | ||||
|     end | ||||
|     battleButton:setLifeBarPercent(creature:getHealthPercent()) | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| local function getDistanceBetween(p1, p2) | ||||
|     return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y)) | ||||
| end | ||||
| 
 | ||||
| function onCreaturePositionChange(creature, newPos, oldPos) | ||||
|   if creature:isLocalPlayer() then | ||||
|     if oldPos and newPos and newPos.z ~= oldPos.z then | ||||
|       checkCreatures() | ||||
|     else | ||||
|       -- Distance will change when moving, recalculate and move to correct index | ||||
|       if getSortType() == 'distance' then | ||||
|         local distanceList = {} | ||||
|         for id, creatureButton in pairs(battleButtonsByCreaturesList) do | ||||
|           table.insert(distanceList, {distance = getDistanceBetween(newPos, creatureButton.creature:getPosition()), widget = creatureButton}) | ||||
|         end | ||||
|    | ||||
|         if isSortAsc() then | ||||
|           table.sort(distanceList, function(a, b) return a.distance < b.distance end) | ||||
|         else | ||||
|           table.sort(distanceList, function(a, b) return a.distance > b.distance end)     | ||||
|         end | ||||
|          | ||||
|         for i = 1, #distanceList do | ||||
|           battlePanel:moveChildToIndex(distanceList[i].widget, i) | ||||
|         end | ||||
|       end     | ||||
| 
 | ||||
|       for id, creatureButton in pairs(battleButtonsByCreaturesList) do | ||||
|         addCreature(creatureButton.creature) | ||||
|       end | ||||
|  | @ -170,6 +318,9 @@ function onCreaturePositionChange(creature, newPos, oldPos) | |||
|     if has and not fit then | ||||
|       removeCreature(creature) | ||||
|     elseif fit then | ||||
|       if has and getSortType() == 'distance' then | ||||
|         removeCreature(creature) | ||||
|       end | ||||
|       addCreature(creature) | ||||
|     end | ||||
|   end | ||||
|  | @ -201,8 +352,13 @@ function addCreature(creature) | |||
|   local creatureId = creature:getId() | ||||
|   local battleButton = battleButtonsByCreaturesList[creatureId] | ||||
| 
 | ||||
|   -- Register when creature is added to battlelist for the first time | ||||
|   if not creatureAgeList[creatureId] then | ||||
|     creatureAgeList[creatureId] = os.time() | ||||
|   end | ||||
|    | ||||
|   if not battleButton then | ||||
|     battleButton = g_ui.createWidget('BattleButton', battlePanel) | ||||
|     battleButton = g_ui.createWidget('BattleButton') | ||||
|     battleButton:setup(creature) | ||||
| 
 | ||||
|     battleButton.onHoverChange = onBattleButtonHoverChange | ||||
|  | @ -217,6 +373,77 @@ function addCreature(creature) | |||
|     if creature == g_game.getFollowingCreature() then | ||||
|       onFollow(creature) | ||||
|     end | ||||
|      | ||||
|     local inserted = false | ||||
|     local nameLower = creature:getName():lower() | ||||
|     local healthPercent = creature:getHealthPercent() | ||||
|     local playerPosition = g_game.getLocalPlayer():getPosition() | ||||
|     local distance = getDistanceBetween(playerPosition, creature:getPosition()) | ||||
|     local age = creatureAgeList[creatureId] | ||||
|      | ||||
|     local childCount = battlePanel:getChildCount() | ||||
|     for i = 1, childCount do | ||||
|       local child = battlePanel:getChildByIndex(i) | ||||
|       local childName = child:getCreature():getName():lower() | ||||
|       local equal = false | ||||
|       if getSortType() == 'age' then | ||||
|         local childAge = creatureAgeList[child:getCreature():getId()] | ||||
|         if (age < childAge and isSortAsc()) or (age > childAge and isSortDesc()) then | ||||
|           battlePanel:insertChild(i, battleButton) | ||||
|           inserted = true | ||||
|           break | ||||
|         elseif age == childAge then | ||||
|           equal = true | ||||
|         end | ||||
|       elseif getSortType() == 'distance' then | ||||
|         local childDistance = getDistanceBetween(child:getCreature():getPosition(), playerPosition) | ||||
|         if (distance < childDistance and isSortAsc()) or (distance > childDistance and isSortDesc()) then | ||||
|           battlePanel:insertChild(i, battleButton) | ||||
|           inserted = true | ||||
|           break | ||||
|         elseif childDistance == distance then | ||||
|           equal = true | ||||
|         end | ||||
|       elseif getSortType() == 'health' then | ||||
|           local childHealth = child:getCreature():getHealthPercent() | ||||
|           if (healthPercent < childHealth and isSortAsc()) or (healthPercent > childHealth and isSortDesc()) then | ||||
|             battlePanel:insertChild(i, battleButton) | ||||
|             inserted = true | ||||
|             break | ||||
|           elseif healthPercent == childHealth then | ||||
|             equal = true | ||||
|           end             | ||||
|       end | ||||
|        | ||||
|       -- If any other sort type is selected and values are equal, sort it by name also | ||||
|       if getSortType() == 'name' or equal then | ||||
|           local length = math.min(childName:len(), nameLower:len()) | ||||
|           for j=1,length do | ||||
|             if (nameLower:byte(j) < childName:byte(j) and isSortAsc()) or (nameLower:byte(j) > childName:byte(j) and isSortDesc()) then | ||||
|               battlePanel:insertChild(i, battleButton) | ||||
|               inserted = true | ||||
|               break | ||||
|             elseif (nameLower:byte(j) > childName:byte(j) and isSortAsc()) or (nameLower:byte(j) < childName:byte(j) and isSortDesc()) then | ||||
|               break | ||||
|             elseif j == nameLower:len() and isSortAsc() then  | ||||
|               battlePanel:insertChild(i, battleButton) | ||||
|               inserted = true | ||||
|             elseif j == childName:len() and isSortDesc() then | ||||
|               battlePanel:insertChild(i, battleButton) | ||||
|               inserted = true | ||||
|             end | ||||
|           end | ||||
|       end | ||||
| 
 | ||||
|       if inserted then | ||||
|         break | ||||
|       end | ||||
|     end | ||||
|      | ||||
|     -- Insert at the end if no other place is found | ||||
|     if not inserted then | ||||
|       battlePanel:insertChild(childCount + 1, battleButton) | ||||
|     end | ||||
|   else | ||||
|     battleButton:setLifeBarPercent(creature:getHealthPercent()) | ||||
|   end | ||||
|  | @ -226,6 +453,7 @@ function addCreature(creature) | |||
| end | ||||
| 
 | ||||
| function removeAllCreatures() | ||||
|   creatureAgeList = {} | ||||
|   for i, v in pairs(battleButtonsByCreaturesList) do | ||||
|     removeCreature(v.creature) | ||||
|   end | ||||
|  |  | |||
|  | @ -45,11 +45,12 @@ MiniWindow | |||
|   &save: true | ||||
| 
 | ||||
|   Panel | ||||
|     id: filterPanel | ||||
|     margin-top: 26 | ||||
|     anchors.top: parent.top | ||||
|     anchors.left: parent.left | ||||
|     anchors.right: miniwindowScrollBar.left | ||||
|     height: 20 | ||||
|     height: 45 | ||||
| 
 | ||||
|     Panel | ||||
|       anchors.top: parent.top | ||||
|  | @ -85,16 +86,55 @@ MiniWindow | |||
|         !tooltip: tr('Hide party members') | ||||
|         @onCheckChange: modules.game_battle.checkCreatures() | ||||
| 
 | ||||
|   HorizontalSeparator | ||||
|     Panel | ||||
|       anchors.top: prev.bottom | ||||
|       anchors.horizontalCenter: parent.horizontalCenter | ||||
|       height: 20 | ||||
|       width: 128 | ||||
|       margin-top: 6 | ||||
|        | ||||
|       ComboBox | ||||
|         id: sortTypeBox | ||||
|         width: 74 | ||||
|         anchors.top: parent.top | ||||
|         anchors.left: prev.right | ||||
|         margin-left: 5 | ||||
| 
 | ||||
|       ComboBox | ||||
|         id: sortOrderBox | ||||
|         width: 54 | ||||
|         anchors.top: parent.top | ||||
|         anchors.left: prev.right | ||||
|         margin-left: 4 | ||||
|          | ||||
|   Panel | ||||
|     height: 18 | ||||
|     anchors.top: prev.bottom | ||||
|     anchors.left: parent.left | ||||
|     anchors.right: miniwindowScrollBar.left | ||||
|     margin-right: 1 | ||||
|     margin-top: 4 | ||||
|     margin-top: 5 | ||||
| 
 | ||||
|     UIWidget | ||||
|       id: toggleFilterButton | ||||
|       anchors.top: prev.top | ||||
|       width: 21 | ||||
|       anchors.horizontalCenter: parent.horizontalCenter | ||||
|       image-source: /images/ui/arrow_vertical | ||||
|       image-rect: 0 0 21 12 | ||||
|       image-clip: 21 0 21 12 | ||||
|       @onClick: modules.game_battle.toggleFilterPanel() | ||||
|       phantom: false | ||||
| 
 | ||||
|   HorizontalSeparator | ||||
|     anchors.top: prev.top | ||||
|     anchors.left: parent.left | ||||
|     anchors.right: miniwindowScrollBar.left | ||||
|     margin-right: 1 | ||||
|     margin-top: 11 | ||||
|      | ||||
|   MiniWindowContents | ||||
|     anchors.top: prev.bottom | ||||
|     margin-top: 0 | ||||
|     margin-top: 2 | ||||
| 
 | ||||
|     Panel | ||||
|       id: battlePanel | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Sam
						Sam