mirror of
				https://github.com/ErikasKontenis/SabrehavenServer.git
				synced 2025-10-30 19:56:22 +01:00 
			
		
		
		
	commit client
This commit is contained in:
		
							
								
								
									
										391
									
								
								SabrehavenOTClient/modules/game_actionbar/actionbar.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								SabrehavenOTClient/modules/game_actionbar/actionbar.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,391 @@ | ||||
| actionPanel1 = nil | ||||
| actionPanel2 = nil | ||||
|  | ||||
| local actionConfig | ||||
| local hotkeyAssignWindow | ||||
| local actionButtonsInPanel = 50 | ||||
|  | ||||
| ActionTypes = { | ||||
|   USE = 0, | ||||
|   USE_SELF = 1, | ||||
|   USE_TARGET = 2, | ||||
|   USE_WITH = 3, | ||||
|   EQUIP = 4 | ||||
| } | ||||
|  | ||||
| ActionColors = { | ||||
|   empty = '#00000033', | ||||
|   text = '#00000033', | ||||
|   itemUse = '#8888FF88', | ||||
|   itemUseSelf = '#00FF0088', | ||||
|   itemUseTarget = '#FF000088', | ||||
|   itemUseWith = '#F5B32588', | ||||
|   itemEquip = '#FFFFFF88' | ||||
| } | ||||
|  | ||||
| function init() | ||||
|   local bottomPanel = modules.game_interface.getActionPanel() | ||||
|   actionPanel1 = g_ui.loadUI('actionbar', bottomPanel) | ||||
|   bottomPanel:moveChildToIndex(actionPanel1, 1) | ||||
|   actionPanel2 = g_ui.loadUI('actionbar', bottomPanel) | ||||
|   bottomPanel:moveChildToIndex(actionPanel2, 1) | ||||
|    | ||||
|   actionConfig = g_configs.create("/actionbar.otml") | ||||
|      | ||||
|   connect(g_game, { | ||||
|     onGameStart = online, | ||||
|     onGameEnd = offline, | ||||
|     onSpellGroupCooldown = onSpellGroupCooldown, | ||||
|     onSpellCooldown = onSpellCooldown | ||||
|   }) | ||||
|    | ||||
|   if g_game.isOnline() then | ||||
|     online() | ||||
|   end | ||||
| end | ||||
|  | ||||
| function terminate() | ||||
|   disconnect(g_game, { | ||||
|     onGameStart = online, | ||||
|     onGameEnd = offline, | ||||
|     onSpellGroupCooldown = onSpellGroupCooldown, | ||||
|     onSpellCooldown = onSpellCooldown | ||||
|   })   | ||||
|  | ||||
|   -- remove hotkeys, also saves config | ||||
|   if actionPanel1.tabBar:getChildCount() > 0 and actionPanel2.tabBar:getChildCount() > 0 then | ||||
|     offline() | ||||
|   end | ||||
|    | ||||
|   actionPanel1:destroy() | ||||
|   actionPanel2:destroy() | ||||
| end | ||||
|  | ||||
| function show() | ||||
|   if not g_game.isOnline() then return end | ||||
|   actionPanel1:setOn(g_settings.getBoolean("actionBar1", false)) | ||||
|   actionPanel2:setOn(g_settings.getBoolean("actionBar2", false)) | ||||
| end | ||||
|  | ||||
| function hide() | ||||
|   actionPanel1:setOn(false) | ||||
|   actionPanel2:setOn(false) | ||||
| end | ||||
|  | ||||
| function switchMode(newMode) | ||||
|   if newMode then | ||||
|     actionPanel1:setImageColor('#ffffff88')   | ||||
|     actionPanel2:setImageColor('#ffffff88')   | ||||
|   else | ||||
|     actionPanel1:setImageColor('white')     | ||||
|     actionPanel2:setImageColor('white')     | ||||
|   end | ||||
| end | ||||
|  | ||||
| function online() | ||||
|   setupActionPanel(1, actionPanel1) | ||||
|   setupActionPanel(2, actionPanel2) | ||||
|   show() | ||||
| end | ||||
|  | ||||
| function offline() | ||||
|   hide() | ||||
|   if hotkeyAssignWindow then | ||||
|     hotkeyAssignWindow:destroy() | ||||
|     hotkeyAssignWindow = nil | ||||
|   end | ||||
|  | ||||
|   local gameRootPanel = modules.game_interface.getRootPanel() | ||||
|   for index, panel in ipairs({actionPanel1, actionPanel2}) do | ||||
|     local config = {} | ||||
|     for i, child in ipairs(panel.tabBar:getChildren()) do | ||||
|       if child.config then | ||||
|         table.insert(config, child.config) | ||||
|         if type(child.config.hotkey) == 'string' and child.config.hotkey:len() > 0 then | ||||
|           g_keyboard.unbindKeyPress(child.config.hotkey, child.callback, gameRootPanel) | ||||
|         end | ||||
|       else | ||||
|         table.insert(config, {}) | ||||
|       end | ||||
|       if child.cooldownEvent then | ||||
|         removeEvent(child.cooldownEvent) | ||||
|       end | ||||
|     end | ||||
|     actionConfig:setNode('actions_' .. index, config) | ||||
|     panel.tabBar:destroyChildren() | ||||
|   end | ||||
|   actionConfig:save() | ||||
| end | ||||
|  | ||||
| function setupActionPanel(index, panel) | ||||
|   local rawConfig = actionConfig:getNode('actions_' .. index) or {} | ||||
|   local config = {} | ||||
|   for i, buttonConfig in pairs(rawConfig) do -- sorting, because key in rawConfig is string | ||||
|     config[tonumber(i)] = buttonConfig | ||||
|   end | ||||
|    | ||||
|   for i=1,actionButtonsInPanel do | ||||
|     local action = g_ui.createWidget('ActionButton', panel.tabBar) | ||||
|     action.config = config[i] or {} | ||||
|     setupAction(action) | ||||
|   end   | ||||
|    | ||||
|   panel.nextButton.onClick = function() | ||||
|     panel.tabBar:moveChildToIndex(panel.tabBar:getFirstChild(), panel.tabBar:getChildCount()) | ||||
|   end | ||||
|   panel.prevButton.onClick = function() | ||||
|     panel.tabBar:moveChildToIndex(panel.tabBar:getLastChild(), 1)   | ||||
|   end | ||||
| end | ||||
|  | ||||
| function setupAction(action) | ||||
|   local config = action.config | ||||
|   action.item:setShowCount(false) | ||||
|   action.onMouseRelease = actionOnMouseRelease | ||||
|   action.onTouchRelease = actionOnMouseRelease | ||||
|   action.callback = function(k, c, ticks) executeAction(action, ticks) end | ||||
|   action.item.onItemChange = nil -- disable callbacks for setup | ||||
|    | ||||
|   if config then | ||||
|     if type(config.text) == 'number' then | ||||
|       config.text = tostring(config.text) | ||||
|     end | ||||
|     if type(config.hotkey) == 'number' then | ||||
|       config.hotkey = tostring(config.hotkey) | ||||
|     end | ||||
|     if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then | ||||
|       local gameRootPanel = modules.game_interface.getRootPanel() | ||||
|       g_keyboard.bindKeyPress(config.hotkey, action.callback, gameRootPanel) | ||||
|       action.hotkeyLabel:setText(config.hotkey) | ||||
|     else | ||||
|       action.hotkeyLabel:setText("") | ||||
|     end | ||||
|  | ||||
|     action.text:setImageSource("") | ||||
|     action.cooldownTill = 0 | ||||
|     action.cooldownStart = 0 | ||||
|     if type(config.text) == 'string' and config.text:len() > 0 then | ||||
|       action.text:setText(config.text) | ||||
|       action.item:setBorderColor(ActionColors.text) | ||||
|       action.item:setOn(true) -- removes background | ||||
|       action.item:setItemId(0) | ||||
|       if Spells then | ||||
|         local spell, profile = Spells.getSpellByWords(config.text:lower()) | ||||
|         action.spell = spell | ||||
|         if action.spell and action.spell.icon and profile then | ||||
|           action.text:setImageSource(SpelllistSettings[profile].iconFile) | ||||
|           action.text:setImageClip(Spells.getImageClip(SpellIcons[action.spell.icon][1], profile)) | ||||
|           action.text:setText("") | ||||
|         end | ||||
|       end | ||||
|     else       | ||||
|       action.text:setText("") | ||||
|       action.spell = nil | ||||
|       if type(config.item) == 'number' and config.item > 100 then | ||||
|         --action.item:setOn(true) | ||||
|         --action.item:setItemId(config.item) | ||||
|         --action.item:setItemCount(config.count or 1) | ||||
|         --setupActionType(action, config.actionType) | ||||
|       else | ||||
|         action.item:setItemId(0) | ||||
|         action.item:setOn(false) | ||||
|         action.item:setBorderColor(ActionColors.empty) | ||||
|       end     | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   action.item.onItemChange = actionOnItemChange | ||||
| end | ||||
|  | ||||
| function setupActionType(action, actionType) | ||||
|   local item = action.item:getItem() | ||||
|   if action.item:getItem():isMultiUse() then | ||||
|     if not actionType or actionType <= ActionTypes.USE then | ||||
|      actionType = ActionTypes.USE_WITH | ||||
|     end | ||||
|   elseif g_game.getClientVersion() >= 910 then | ||||
|     if actionType ~= ActionTypes.USE and actionType ~= ActionTypes.EQUIP then | ||||
|       actionType = ActionTypes.USE | ||||
|     end | ||||
|   else | ||||
|     actionType = ActionTypes.USE | ||||
|   end | ||||
|  | ||||
|   action.config.actionType = actionType | ||||
|   if action.config.actionType == ActionTypes.USE then | ||||
|     action.item:setBorderColor(ActionColors.itemUse) | ||||
|   elseif action.config.actionType == ActionTypes.USE_SELF then | ||||
|     action.item:setBorderColor(ActionColors.itemUseSelf) | ||||
|   elseif action.config.actionType == ActionTypes.USE_TARGET then | ||||
|     action.item:setBorderColor(ActionColors.itemUseTarget) | ||||
|   elseif action.config.actionType == ActionTypes.USE_WITH then | ||||
|     action.item:setBorderColor(ActionColors.itemUseWith) | ||||
|   elseif action.config.actionType == ActionTypes.EQUIP then | ||||
|     action.item:setBorderColor(ActionColors.itemEquip) | ||||
|   end | ||||
| end | ||||
|  | ||||
| function updateAction(action, newConfig) | ||||
|   local config = action.config | ||||
|   if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then | ||||
|     local gameRootPanel = modules.game_interface.getRootPanel() | ||||
|     g_keyboard.unbindKeyPress(config.hotkey, action.callback, gameRootPanel) | ||||
|   end | ||||
|   for key, val in pairs(newConfig) do | ||||
|     action.config[key] = val | ||||
|   end | ||||
|   setupAction(action) | ||||
| end | ||||
|  | ||||
| function actionOnMouseRelease(action, mousePosition, mouseButton) | ||||
|   if mouseButton == MouseTouch then return end | ||||
|   if mouseButton == MouseRightButton or not action.item:isOn() then | ||||
|     local menu = g_ui.createWidget('PopupMenu') | ||||
|     menu:setGameMenu(true) | ||||
|     menu:addOption(tr('Set text'), function()  | ||||
|       modules.client_textedit.singlelineEditor(action.config.text or "", function(newText) | ||||
|         updateAction(action, {text=newText, item=0}) | ||||
|       end) | ||||
|     end) | ||||
|     menu:addOption(tr('Set hotkey'), function() | ||||
|       if hotkeyAssignWindow then | ||||
|         hotkeyAssignWindow:destroy() | ||||
|       end | ||||
|       local assignWindow = g_ui.createWidget('ActionAssignWindow', rootWidget) | ||||
|       assignWindow:grabKeyboard() | ||||
|       assignWindow.comboPreview.keyCombo = '' | ||||
|       assignWindow.onKeyDown = function(assignWindow, keyCode, keyboardModifiers) | ||||
|         local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers) | ||||
|         assignWindow.comboPreview:setText(tr('Current action hotkey: %s', keyCombo)) | ||||
|         assignWindow.comboPreview.keyCombo = keyCombo | ||||
|         assignWindow.comboPreview:resizeToText() | ||||
|         return true | ||||
|       end | ||||
|       assignWindow.onDestroy = function(widget) | ||||
|         if widget == hotkeyAssignWindow then | ||||
|           hotkeyAssignWindow = nil | ||||
|         end | ||||
|       end | ||||
|       assignWindow.addButton.onClick = function() | ||||
|         updateAction(action, {hotkey=tostring(assignWindow.comboPreview.keyCombo)}) | ||||
|         assignWindow:destroy() | ||||
|       end | ||||
|       hotkeyAssignWindow = assignWindow | ||||
|     end) | ||||
|     menu:addSeparator() | ||||
|     menu:addOption(tr('Clear'), function() | ||||
|       updateAction(action, {hotkey="", text="", item=0, count=1}) | ||||
|     end) | ||||
|     menu:display(mousePosition) | ||||
|     return true | ||||
|   elseif mouseButton == MouseLeftButton or mouseButton == MouseTouch2 or mouseButton == MouseTouch3 then | ||||
|     action.callback() | ||||
|     return true | ||||
|   end | ||||
|   return false | ||||
| end | ||||
|  | ||||
| function actionOnItemChange(widget) | ||||
|   updateAction(widget:getParent(), {text="", item=widget:getItemId(), count=widget:getItemCountOrSubType()}) | ||||
| end | ||||
|  | ||||
| function onSpellCooldown(iconId, duration) | ||||
|   for index, panel in ipairs({actionPanel1, actionPanel2}) do | ||||
|     for i, child in ipairs(panel.tabBar:getChildren()) do | ||||
|       if child.spell and child.spell.id == iconId then | ||||
|         startCooldown(child, duration) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| function onSpellGroupCooldown(groupId, duration) | ||||
|   for index, panel in ipairs({actionPanel1, actionPanel2}) do | ||||
|     for i, child in ipairs(panel.tabBar:getChildren()) do | ||||
|       if child.spell and child.spell.group then | ||||
|         for group, duration in pairs(child.spell.group) do | ||||
|           if groupId == group then | ||||
|             startCooldown(child, duration) | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| function startCooldown(action, duration) | ||||
|   if type(action.cooldownTill) == 'number' and action.cooldownTill > g_clock.millis() + duration then | ||||
|     return -- already has cooldown with greater duration | ||||
|   end | ||||
|   action.cooldownStart = g_clock.millis() | ||||
|   action.cooldownTill = g_clock.millis() + duration | ||||
|   updateCooldown(action) | ||||
| end | ||||
|  | ||||
| function updateCooldown(action) | ||||
|   if not action or not action.cooldownTill then return end | ||||
|   local timeleft = action.cooldownTill - g_clock.millis() | ||||
|   if timeleft <= 30 then | ||||
|     action.cooldown:setPercent(100) | ||||
|     action.cooldownEvent = nil     | ||||
|     return | ||||
|   end | ||||
|   local duration = action.cooldownTill - action.cooldownStart | ||||
|   action.cooldown:setPercent(100 - math.floor(100 * timeleft / duration)) | ||||
|   action.cooldownEvent = scheduleEvent(function() updateCooldown(action) end, 30) | ||||
| end | ||||
|  | ||||
| function executeAction(action, ticks) | ||||
|   if not action.config then return end | ||||
|   if type(ticks) ~= 'number' then ticks = 0 end | ||||
|  | ||||
|   local actionDelay = 100   | ||||
|   if ticks == 0 then | ||||
|     actionDelay = 200 -- for first use | ||||
|   elseif action.actionDelayTo ~= nil and g_clock.millis() < action.actionDelayTo then | ||||
|     return | ||||
|   end | ||||
|    | ||||
|   local actionType = action.config.actionType | ||||
|  | ||||
|   if type(action.config.text) == 'string' and action.config.text:len() > 0 then | ||||
|     if g_app.isMobile() then -- turn to direction of targer | ||||
|       local target = g_game.getAttackingCreature() | ||||
|       if target then | ||||
|         local pos = g_game.getLocalPlayer():getPosition() | ||||
|         local tpos = target:getPosition() | ||||
|         if pos and tpos then | ||||
|           local offx = tpos.x - pos.x | ||||
|           local offy = tpos.y - pos.y | ||||
|           if offy < 0 and offx <= 0 and math.abs(offx) < math.abs(offy) then | ||||
|             g_game.turn(Directions.North) | ||||
|           elseif offy > 0 and offx >= 0 and math.abs(offx) < math.abs(offy) then | ||||
|             g_game.turn(Directions.South) | ||||
|           elseif offx < 0 and offy <= 0 and math.abs(offx) > math.abs(offy) then | ||||
|             g_game.turn(Directions.West) | ||||
|           elseif offx > 0 and offy >= 0 and math.abs(offx) > math.abs(offy) then | ||||
|             g_game.turn(Directions.East) | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|     if modules.game_interface.isChatVisible() then | ||||
|       modules.game_console.sendMessage(action.config.text)     | ||||
|     else | ||||
|       g_game.talk(action.config.text) | ||||
|     end | ||||
|     action.actionDelayTo = g_clock.millis() + actionDelay | ||||
|   elseif action.item:getItemId() > 0 then     | ||||
|     if actionType == ActionTypes.USE then | ||||
|       action.actionDelayTo = g_clock.millis() + actionDelay | ||||
|     elseif actionType == ActionTypes.USE_SELF then | ||||
|       action.actionDelayTo = g_clock.millis() + actionDelay | ||||
|     elseif actionType == ActionTypes.USE_TARGET then | ||||
|       action.actionDelayTo = g_clock.millis() + actionDelay | ||||
|     elseif actionType == ActionTypes.USE_WITH then | ||||
|       action.actionDelayTo = g_clock.millis() + actionDelay | ||||
|     elseif actionType == ActionTypes.EQUIP then | ||||
|       action.actionDelayTo = g_clock.millis() + actionDelay | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,9 @@ | ||||
| Module | ||||
|   name: game_actionbar | ||||
|   description: Action bar | ||||
|   author: otclient@otclient.ovh | ||||
|   website: otclient.ovh | ||||
|   sandboxed: true | ||||
|   scripts: [ actionbar ] | ||||
|   @onLoad: init() | ||||
|   @onUnload: terminate() | ||||
							
								
								
									
										145
									
								
								SabrehavenOTClient/modules/game_actionbar/actionbar.otui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								SabrehavenOTClient/modules/game_actionbar/actionbar.otui
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| ActionButton < Panel | ||||
|   font: cipsoftFont | ||||
|   anchors.top: parent.top | ||||
|   anchors.bottom: parent.bottom | ||||
|   width: 40 | ||||
|   padding: 1 1 1 1 | ||||
|   margin-left: 1 | ||||
|    | ||||
|   $first: | ||||
|     anchors.left: parent.left | ||||
|  | ||||
|   $!first: | ||||
|     anchors.left: prev.right | ||||
|      | ||||
|   Item | ||||
|     id: item | ||||
|     anchors.fill: parent | ||||
|     &selectable: true | ||||
|     &editable: false | ||||
|     virtual: true | ||||
|     border-width: 1 | ||||
|      | ||||
|     border-color: #00000000 | ||||
|      | ||||
|     $!on: | ||||
|       image-source: /images/game/actionbarslot | ||||
|  | ||||
|   Label | ||||
|     id: text | ||||
|     anchors.fill: parent | ||||
|     text-auto-resize: true | ||||
|     text-wrap: true | ||||
|     phantom: true | ||||
|     text-align: center | ||||
|     font: verdana-9px | ||||
|  | ||||
|   Label | ||||
|     id: hotkeyLabel | ||||
|     anchors.top: parent.top | ||||
|     anchors.left: parent.left | ||||
|     margin: 2 3 3 3 | ||||
|     text-auto-resize: true | ||||
|     text-wrap: false | ||||
|     phantom: true | ||||
|     font: small-9px | ||||
|     color: yellow | ||||
|  | ||||
|   UIProgressRect | ||||
|     id: cooldown | ||||
|     background: #585858AA | ||||
|     percent: 100 | ||||
|     focusable: false | ||||
|     phantom: true | ||||
|     anchors.fill: parent | ||||
|     margin: 1 1 1 1 | ||||
|      | ||||
| Panel | ||||
|   id: actionBar | ||||
|   focusable: false | ||||
|   image-source: /images/ui/panel_side | ||||
|   image-border: 4 | ||||
|   margin-top: -1 | ||||
|    | ||||
|   $on: | ||||
|     height: 40 | ||||
|     visible: true | ||||
|      | ||||
|   $!on: | ||||
|     height: 0 | ||||
|     visible: false | ||||
|  | ||||
|   TabButton | ||||
|     id: prevButton | ||||
|     icon: /images/game/console/leftarrow | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.bottom: parent.bottom | ||||
|     margin-left: 1 | ||||
|     margin-top: 1 | ||||
|     margin-bottom: 2 | ||||
|      | ||||
|   Panel | ||||
|     id: tabBar | ||||
|     anchors.top: parent.top | ||||
|     anchors.bottom: parent.bottom | ||||
|     anchors.left: prev.right | ||||
|     anchors.right: next.left | ||||
|     margin-right: 3 | ||||
|     clipping: true | ||||
|      | ||||
|   TabButton | ||||
|     id: nextButton | ||||
|     icon: /images/game/console/rightarrow | ||||
|     anchors.right: parent.right | ||||
|     anchors.top: parent.top | ||||
|     anchors.bottom: parent.bottom | ||||
|     margin-right: 1 | ||||
|     margin-top: 1 | ||||
|     margin-bottom: 2 | ||||
|  | ||||
|  | ||||
| ActionAssignWindow < MainWindow | ||||
|   id: assignWindow | ||||
|   !text: tr('Button Assign') | ||||
|   size: 360 150 | ||||
|   @onEscape: self:destroy() | ||||
|  | ||||
|   Label | ||||
|     !text: tr('Please, press the key you wish to use for action') | ||||
|     anchors.left: parent.left | ||||
|     anchors.right: parent.right | ||||
|     anchors.top: parent.top | ||||
|     text-auto-resize: true | ||||
|     text-align: left | ||||
|  | ||||
|   Label | ||||
|     id: comboPreview | ||||
|     !text: tr('Current action hotkey: %s', 'none') | ||||
|     anchors.horizontalCenter: parent.horizontalCenter | ||||
|     anchors.top: prev.bottom | ||||
|     margin-top: 10 | ||||
|     text-auto-resize: true | ||||
|  | ||||
|   HorizontalSeparator | ||||
|     id: separator | ||||
|     anchors.left: parent.left | ||||
|     anchors.right: parent.right | ||||
|     anchors.bottom: next.top | ||||
|     margin-bottom: 10 | ||||
|  | ||||
|   Button | ||||
|     id: addButton | ||||
|     !text: tr('Add') | ||||
|     width: 64 | ||||
|     anchors.right: next.left | ||||
|     anchors.bottom: parent.bottom | ||||
|     margin-right: 10 | ||||
|  | ||||
|   Button | ||||
|     id: cancelButton | ||||
|     !text: tr('Cancel') | ||||
|     width: 64 | ||||
|     anchors.right: parent.right | ||||
|     anchors.bottom: parent.bottom | ||||
|     @onClick: self:getParent():destroy() | ||||
		Reference in New Issue
	
	Block a user
	 ErikasKontenis
					ErikasKontenis