if FirstLoad then LocalPlayersCount = 0 end DefineClass.CharacterControl = { __parents = { "OldTerminalTarget", "InitDone" }, active = false, character = false, camera_active = true, terminal_target_priority = -500, } function CharacterControl:Init(character) self.character = character terminal.AddTarget(self) end function CharacterControl:Done() terminal.RemoveTarget(self) self:SetActive(false) end function CharacterControl:SetActive(active) if self.active == active then return end if active then self.active = active self:OnActivate() else self.active = active self:OnInactivate() end ChangeGameState("CharacterControl", active) end function CharacterControl:SetCameraActive(active) self.camera_active = active end function CharacterControl:OnActivate() self:SyncWithCharacter() end function CharacterControl:OnInactivate() if not IsPaused() then self:SyncWithCharacter() end end function CharacterControl:GetBindingValue(binding) end function CharacterControl:GetActionBindings(action) end function CharacterControl:GetActionBinding1(action) local bindings = self:GetActionBindings(action) local binding = bindings and bindings[1] return binding and (binding.xbutton or binding.key or binding.mouse_button) end function CharacterControl:GetBindingsCombinedValue(action) local bindings = self:GetActionBindings(action) if not bindings then return end local best_value for i = 1, #bindings do local value = self:GetBindingValue(bindings[i]) if value then if value == true then return true elseif type(value) == "number" then best_value = Max(value, best_value or 0) elseif IsPoint(value) then if not best_value or value:Len2() > best_value:Len2() then best_value = value end end end end return best_value end function CharacterControl:CallBindingsDown(bindings, param, time) for i = 1, #bindings do local binding = bindings[i] if self:BindingModifiersActive(binding) then local result = binding.func(self.character, self, param, time) if result ~= "continue" then return result end end end return "continue" end function CharacterControl:CallBindingsUp(bindings) for i = 1, #bindings do local binding = bindings[i] local value = self:GetBindingsCombinedValue(binding.action) if not value then local result = binding.func(self.character, self) if result ~= "continue" then return result end end end return "continue" end function CharacterControl:SyncBindingsWithCharacter(bindings) for i = 1, #bindings do local binding = bindings[i] local value = binding.action and self:GetBindingsCombinedValue(binding.action) binding.func(self.character, self, value) end end function CharacterControl:SyncWithCharacter() end function BindToKeyboardAndMouseSync(action) local class = _G["CCA_"..action] assert(class) if class then class:BindToControllerSync(action, CC_KeyboardAndMouseSync) end end function BindToXboxControllerSync(action) local class = _G["CCA_"..action] assert(class) if class then class:BindToControllerSync(action, CC_XboxControllerSync) end end -- CharacterControlAction DefineClass.CharacterControlAction = { __parents = {}, ActionStop = false, IsKindOf = IsKindOf, HasMember = PropObjHasMember, } function CharacterControlAction:Action(character) print("No Action defined: " .. self.class) return "continue" end function OnMsg.ClassesPostprocess() ClassDescendants("CharacterControlAction", function(class_name, class) if class.GetAction == CharacterControlAction.GetAction and class.Action then local f = function(...) return class:Action(...) end class.GetAction = function() return f end end if class.GetActionSync == CharacterControlAction.GetActionSync and class.ActionStop then local action_name = string.sub(class_name, #"CCA_" + 1) local function f(character, controller) local value = controller:GetBindingsCombinedValue(action_name) if not value then class:ActionStop(character, controller) end return "continue" end class.GetActionSync = function() return f end end end) end function CharacterControlAction:GetAction() end function CharacterControlAction:GetActionSync() end function CharacterControlAction:BindToControllerSync(action, bindings) local f = self:GetActionSync() if f and not table.find(bindings, "func", f) then table.insert(bindings, { action = action, func = f }) end end function CharacterControlAction:BindKey(action, key, mod1, mod2) local f = self:GetAction() if f then if mod1 == "double-click" then BindToKeyboardEvent(action, "double-click", f, key, mod2) elseif mod1 == "hold" then BindToKeyboardEvent(action, "hold", f, key, mod2) else BindToKeyboardEvent(action, "down", f, key, mod1, mod2) end end if mod1 == "double-click" or mod1 == "hold" then mod1, mod2 = mod2, nil end f = self:GetActionSync() if f then BindToKeyboardEvent(action, "up", f, key) if mod1 then BindToKeyboardEvent(action, "up", f, mod1) end if mod2 then BindToKeyboardEvent(action, "up", f, mod2) end end end function CharacterControlAction:BindMouse(action, button, key_mod) assert(key_mod ~= "double-click" and key_mod ~= "hold", "Not supported mouse modifiers") local f = self:GetAction() if f then if button == "MouseMove" then BindToMouseEvent(action, "mouse_move", f, nil, key_mod) else BindToMouseEvent(action, "down", f, button, key_mod) end end f = self:GetActionSync() if f then if button ~= "MouseMove" then BindToMouseEvent(action, "up", f, button) end if key_mod then BindToKeyboardEvent(action, "up", f, key_mod) end end end function CharacterControlAction:BindXboxController(action, button, mod1, mod2) local f = self:GetAction() if f then if mod1 == "hold" then BindToXboxControllerEvent(action, "hold", f, button, mod2) else BindToXboxControllerEvent(action, "down", f, button, mod1, mod2) end end if mod1 == "hold" then mod1, mod2 = mod2, nil end f = self:GetActionSync() if f then BindToXboxControllerEvent(action, "up", f, button) if mod1 then BindToXboxControllerEvent(action, "up", f, mod1) end if mod2 then BindToXboxControllerEvent(action, "up", f, mod2) end end end -- Navigation if FirstLoad then UpdateCharacterNavigationThread = false end function OnMsg.DoneMap() UpdateCharacterNavigationThread = false end local function CalcNavigationVector(controller, camera_view) local pt = controller:GetBindingsCombinedValue("Move_Direction") if pt then return Rotate(pt:SetX(-pt:x()), XControlCameraGetYaw(camera_view) - 90*60) end local x = (controller:GetBindingsCombinedValue("Move_CameraRight") and 32767 or 0) + (controller:GetBindingsCombinedValue("Move_CameraLeft") and -32767 or 0) local y = (controller:GetBindingsCombinedValue("Move_CameraForward") and 32767 or 0) + (controller:GetBindingsCombinedValue("Move_CameraBackward") and -32767 or 0) if x ~= 0 or y ~= 0 then return Rotate(point(-x,y), XControlCameraGetYaw(camera_view) - 90*60) end end function UpdateCharacterNavigation(character, controller) local dir = CalcNavigationVector(controller, character.camera_view) character:SetStateContext("navigation_vector", dir) if dir and not IsValidThread(UpdateCharacterNavigationThread) then UpdateCharacterNavigationThread = CreateMapRealTimeThread(function() repeat Sleep(20) if IsPaused() then break end local update for loc_player = 1, LocalPlayersCount do local o = PlayerControlObjects[loc_player] if o and o.controller then local dir = CalcNavigationVector(o.controller, o.camera_view) o:SetStateContext("navigation_vector", dir) update = update or dir and true end end until not update UpdateCharacterNavigationThread = false end) end return "continue" end DefineClass("CCA_Navigation", "CharacterControlAction") DefineClass("CCA_Move_CameraForward", "CCA_Navigation") DefineClass("CCA_Move_CameraBackward", "CCA_Navigation") DefineClass("CCA_Move_CameraLeft", "CCA_Navigation") DefineClass("CCA_Move_CameraRight", "CCA_Navigation") function CCA_Navigation:BindKey(action, key) BindToKeyboardEvent(action, "down", UpdateCharacterNavigation, key) BindToKeyboardEvent(action, "up", UpdateCharacterNavigation, key) end function CCA_Navigation:BindXboxController(action, button) BindToXboxControllerEvent(action, "down", UpdateCharacterNavigation, button) BindToXboxControllerEvent(action, "up", UpdateCharacterNavigation, button) end function CCA_Navigation:GetActionSync() return UpdateCharacterNavigation end -- Move_Direction DefineClass("CCA_Move_Direction", "CharacterControlAction") function CCA_Move_Direction:BindKey(action, key, mod1, mod2) assert(false, "Can't bind 2D direction to a key") end function CCA_Move_Direction:BindMouse(action, button, key_mod) assert(false, "Mouse cursor could be converted to a direction. Not implemented.") end function CCA_Move_Direction:BindXboxController(action, button) assert(button == "LeftThumb" or button == "RightThumb") BindToXboxControllerEvent(action, "change", UpdateCharacterNavigation, button) end function CCA_Move_Direction:GetActionSync() return UpdateCharacterNavigation end -- RotateCamera function UpdateCameraRotate(character, controller) if not g_LookAtObjectSA then local dir = (controller:GetBindingsCombinedValue("CameraRotate_Left") and -1 or 0) + (controller:GetBindingsCombinedValue("CameraRotate_Right") and 1 or 0) camera3p.SetAutoRotate(90*60*dir) end return "continue" end DefineClass("CCA_CameraRotate", "CharacterControlAction") DefineClass("CCA_CameraRotate_Left", "CCA_CameraRotate") DefineClass("CCA_CameraRotate_Right", "CCA_CameraRotate") function CCA_CameraRotate:BindKey(action, key) BindToKeyboardEvent(action, "down", UpdateCameraRotate, key) BindToKeyboardEvent(action, "up", UpdateCameraRotate, key) end function CCA_CameraRotate:BindXboxController(action, button) BindToXboxControllerEvent(action, "down", UpdateCameraRotate, button) BindToXboxControllerEvent(action, "up", UpdateCameraRotate, button) end function CCA_CameraRotate:GetActionSync() return UpdateCameraRotate end if FirstLoad then InGameMouseCursor = false end -- CameraRotate_Mouse DefineClass("CCA_CameraRotate_Mouse", "CharacterControlAction") function CCA_CameraRotate_Mouse:Action(character) if not (character and character.controller and character.controller.camera_active) then return "continue" end if InGameMouseCursor then HideMouseCursor("InGameCursor") -- although MouseRotate(true) hides the mouse, IsMouseCursorHidden() depends on it else SetMouseDeltaMode(false) end MouseRotate(true) Msg("CameraRotateStart", "mouse") return "break" end function CCA_CameraRotate_Mouse:ActionStop(character) MouseRotate(false) if InGameMouseCursor then ShowMouseCursor("InGameCursor") else HideMouseCursor("InGameCursor") SetMouseDeltaMode(true) -- prevents the mouse to leave the game window end Msg("CameraRotateStop", "mouse") return "continue" end function CCA_CameraRotate_Mouse:GetActionSync(character, controller) local function f(character, controller) local value = not CameraLocked and (MouseRotateCamera == "always" or controller:GetBindingsCombinedValue("CameraRotate_Mouse")) if value then return self:Action(character, controller) else return self:ActionStop(character, controller) end end return f end -- KeyboardAndMouse Control DefineClass.CC_KeyboardAndMouse = { __parents = { "CharacterControl" }, KeyHoldButtonTime = 350, KeyDoubleClickTime = 300, key_hold_thread = false, key_last_double_click = false, key_last_double_click_time = 0, } function CC_KeyboardAndMouse:OnActivate() CharacterControl.OnActivate(self) if InGameMouseCursor then ShowMouseCursor("InGameCursor") end end function CC_KeyboardAndMouse:OnInactivate() CharacterControl.OnInactivate(self) DeleteThread(self.key_hold_thread) self.key_hold_thread = nil self.key_last_double_click = nil self.key_last_double_click_time = nil HideMouseCursor("InGameCursor") MouseRotate(false) end function CC_KeyboardAndMouse:SetCameraActive(active) CharacterControl.SetCameraActive(self, active) if self.active and not self.camera_active then MouseRotate(false) end end function CC_KeyboardAndMouse:GetActionBindings(action) return CC_KeyboardAndMouse_ActionBindings[action] end function CC_KeyboardAndMouse:GetBindingValue(binding) if not self.active or binding.key and not terminal.IsKeyPressed(binding.key) then return false end if binding.mouse_button then local pressed = self:IsMouseButtonPressed(binding.mouse_button) if pressed == false then return false end end if not self:BindingModifiersActive(binding) then return false end return true end function CC_KeyboardAndMouse:IsMouseButtonPressed(button) local pressed, _ if button == "LButton" then pressed = terminal.IsLRMX1X2MouseButtonPressed() elseif button == "RButton" then _, pressed = terminal.IsLRMX1X2MouseButtonPressed() elseif button == "MButton" then _, _, pressed = terminal.IsLRMX1X2MouseButtonPressed() elseif button == "XButton1" then _, _, _, pressed = terminal.IsLRMX1X2MouseButtonPressed() elseif button == "XButton2" then _, _, _, _, pressed = terminal.IsLRMX1X2MouseButtonPressed() elseif button == "MouseWheelFwd" or button == "MouseWheelBack" then return false end return pressed end function CC_KeyboardAndMouse:BindingModifiersActive(binding) local keys = binding.key_modifiers if keys then for i = 1, #keys do local key_or_button = keys[i] if key_or_button == "MouseWheelFwd" or key_or_button == "MouseWheelBack" then return false end local pressed = self:IsMouseButtonPressed(key_or_button) if pressed == nil then pressed = terminal.IsKeyPressed(key_or_button) end if not pressed then return false end end end return true end -- keyboard events function CC_KeyboardAndMouse:OnKbdKeyDown(virtual_key, repeated, time) if repeated or not self.active then return "continue" end -- double click if CC_KeyboardKeyDoubleClick[virtual_key] then if self.key_last_double_click == virtual_key and RealTime() - self.key_last_double_click_time < self.KeyDoubleClickTime then self.key_last_double_click = false self:CallBindingsDown(CC_KeyboardKeyDoubleClick[virtual_key], true, time) else self.key_last_double_click = virtual_key self.key_last_double_click_time = RealTime() end end -- hold if CC_KeyboardKeyHold[virtual_key] then DeleteThread(self.key_hold_thread) self.key_hold_thread = CreateRealTimeThread(function(self, virtual_key, time) Sleep(self.KeyHoldButtonTime) self.key_hold_thread = false if terminal.IsKeyPressed(virtual_key) then self:CallBindingsDown(CC_KeyboardKeyHold[virtual_key], true, time) end end, self, virtual_key, time) end -- down local result if CC_KeyboardKeyDown[virtual_key] then result = self:CallBindingsDown(CC_KeyboardKeyDown[virtual_key], true, time) end return result or "continue" end function CC_KeyboardAndMouse:OnKbdKeyUp(virtual_key) if not self.active then return "continue" end if CC_KeyboardKeyHold[virtual_key] and self.key_hold_thread then DeleteThread(self.key_hold_thread) self.key_hold_thread = false end if CC_KeyboardKeyUp[virtual_key] then local result = self:CallBindingsUp(CC_KeyboardKeyUp[virtual_key]) return result end return "continue" end -- mouse events function CC_KeyboardAndMouse:OnMouseButtonDown(button, pt, time) if not self.active then return "continue" end if CC_MouseButtonDown[button] then local result = self:CallBindingsDown(CC_MouseButtonDown[button], true, time) if result ~= "continue" then return result end end return "continue" end function CC_KeyboardAndMouse:OnMouseButtonUp(button, pt, time) if not self.active then return "continue" end if CC_MouseButtonUp[button] then local result = self:CallBindingsUp(CC_MouseButtonUp[button], false, time) if result ~= "continue" then return result end end return "continue" end function CC_KeyboardAndMouse:OnLButtonDown(...) return self:OnMouseButtonDown("LButton", ...) end function CC_KeyboardAndMouse:OnLButtonUp(...) return self:OnMouseButtonUp("LButton", ...) end function CC_KeyboardAndMouse:OnLButtonDoubleClick(...) return self:OnMouseButtonDown("LButton", ...) end function CC_KeyboardAndMouse:OnRButtonDown(...) return self:OnMouseButtonDown("RButton", ...) end function CC_KeyboardAndMouse:OnRButtonUp(...) return self:OnMouseButtonUp("RButton", ...) end function CC_KeyboardAndMouse:OnRButtonDoubleClick(...) return self:OnMouseButtonDown("RButton", ...) end function CC_KeyboardAndMouse:OnMButtonDown(...) return self:OnMouseButtonDown("MButton", ...) end function CC_KeyboardAndMouse:OnMButtonUp(...) return self:OnMouseButtonUp("MButton", ...) end function CC_KeyboardAndMouse:OnMButtonDoubleClick(...) return self:OnMouseButtonDown("MButton", ...) end function CC_KeyboardAndMouse:OnXButton1Down(...) return self:OnMouseButtonDown("XButton1", ...) end function CC_KeyboardAndMouse:OnXButton1Up(...) return self:OnMouseButtonUp("XButton1", ...) end function CC_KeyboardAndMouse:OnXButton1DoubleClick(...) return self:OnMouseButtonDown("XButton1", ...) end function CC_KeyboardAndMouse:OnXButton2Down(...) return self:OnMouseButtonDown("XButton2", ...) end function CC_KeyboardAndMouse:OnXButton2Up(...) return self:OnMouseButtonUp("XButton2", ...) end function CC_KeyboardAndMouse:OnXButton2DoubleClick(...) return self:OnMouseButtonDown("XButton2", ...) end function CC_KeyboardAndMouse:OnMouseWheelForward(pt, time) if not self.active then return "continue" end local result = self:CallBindingsDown(CC_MouseWheelFwd, true, time) if result ~= "break" then result = self:CallBindingsDown(CC_MouseWheel, 1, time) end return result end function CC_KeyboardAndMouse:OnMouseWheelBack(pt, time) if not self.active then return "continue" end local result = self:CallBindingsDown(CC_MouseWheelBack, true, time) if result ~= "break" then result = self:CallBindingsDown(CC_MouseWheel, -1, time) end return result end function CC_KeyboardAndMouse:OnMousePos(pt, time) if not self.active then return "continue" end local result = self:CallBindingsDown(CC_MouseMove, pt, time) return result end function CC_KeyboardAndMouse:SyncWithCharacter() self:SyncBindingsWithCharacter(CC_KeyboardAndMouseSync) end local function ResetKeyboardAndMouseBindings() CC_KeyboardKeyDown = {} CC_KeyboardKeyUp = {} CC_KeyboardKeyHold = {} CC_KeyboardKeyDoubleClick = {} CC_MouseButtonDown = {} CC_MouseButtonUp = {} CC_MouseWheel = {} CC_MouseWheelFwd = {} CC_MouseWheelBack = {} CC_MouseMove = {} CC_KeyboardAndMouse_ActionBindings = {} CC_KeyboardAndMouseSync = {} end if FirstLoad then ResetKeyboardAndMouseBindings() end function BindKey(action, key, mod1, mod2) local class = _G["CCA_"..action] assert(class) if class then class:BindKey(action, key, mod1, mod2) end end function BindMouse(action, button, key_mod) local class = _G["CCA_"..action] assert(class) if class then class:BindMouse(action, button, key_mod) end end local function ResolveRefBindings(list, bindings) for i = 1, #list do local action = list[i][1] local blist = bindings[action] for j = #blist, 1, -1 do local binding = blist[j] for k = #binding, 1, -1 do local ref = bindings[binding[k]] if ref then if #ref == 0 then table.remove(blist,j) else table.remove(binding, k) for m = 2, #ref do table.insert(blist, j, table.copy(binding)) end for m = 1, #ref do local rt = ref[m] local binding_mod = blist[j+m-1] for n = #rt, 1, -1 do table.insert(binding_mod, k+n-1, rt[n]) end end end end end end end end function ReloadKeyboardAndMouseBindings(default_bindings, predefined_bindings) ResetKeyboardAndMouseBindings() if not default_bindings then return end local bindings = {} for i = 1, #default_bindings do local default_list = default_bindings[i] local action = default_list[1] bindings[action] = {} local predefined_list = predefined_bindings and predefined_bindings[action] for j = 1, Max(predefined_list and #predefined_list or 0, #default_list-1) do local binding = predefined_list and predefined_list[j] or nil if binding == nil then binding = default_list and default_list[j+1] end if binding and #binding > 0 then local t = {} for k = 1, #binding do t[k] = type(binding[k]) == "string" and const["vk"..binding[k]] or binding[k] end table.insert(bindings[action], t) end end end ResolveRefBindings(default_bindings, bindings) for i = 1, #default_bindings do local action = default_bindings[i][1] local blist = bindings[action] for j = 1, #blist do local binding = blist[j] if type(binding[1]) == "number" then BindKey(action, binding[1], binding[2], binding[3]) else BindMouse(action, binding[1], binding[2], binding[3]) end if binding[2] then if type(binding[2]) == "number" then BindKey(action, binding[2], binding[1], binding[3]) else BindMouse(action, binding[2], binding[1], binding[3]) end end if binding[3] then if type(binding[3]) == "number" then BindKey(action, binding[3], binding[1], binding[2]) else BindMouse(action, binding[3], binding[1], binding[2]) end end end BindToKeyboardAndMouseSync(action) end end function BindToKeyboardEvent(action, event, func, key, mod1, mod2) local binding = { action = action, key = key, func = func } if mod1 or mod2 then binding.key_modifiers = {} binding.key_modifiers[#binding.key_modifiers+1] = mod1 binding.key_modifiers[#binding.key_modifiers+1] = mod2 end local list if event == "down" then list = CC_KeyboardKeyDown CC_KeyboardAndMouse_ActionBindings[action] = CC_KeyboardAndMouse_ActionBindings[action] or {} table.insert(CC_KeyboardAndMouse_ActionBindings[action], binding) elseif event == "up" then list = CC_KeyboardKeyUp elseif event == "hold" then list = CC_KeyboardKeyHold elseif event == "double-click" then list = CC_KeyboardKeyDoubleClick end list[key] = list[key] or {} table.insert(list[key], binding) end function BindToMouseEvent(action, event, func, button, key_mod) local binding = { action = action, mouse_button = button, func = func } if key_mod then binding.key_modifiers = {} binding.key_modifiers[#binding.key_modifiers+1] = key_mod end if event == "down" or button == "MouseWheel" then CC_KeyboardAndMouse_ActionBindings[action] = CC_KeyboardAndMouse_ActionBindings[action] or {} table.insert(CC_KeyboardAndMouse_ActionBindings[action], binding) end if button == "MouseWheel" then table.insert(CC_MouseWheel, binding) elseif button == "MouseWheelFwd" then table.insert(CC_MouseWheelFwd, binding) elseif button == "MouseWheelBack" then table.insert(CC_MouseWheelBack, binding) elseif event == "down" then CC_MouseButtonDown[button] = CC_MouseButtonDown[button] or {} table.insert(CC_MouseButtonDown[button], binding) elseif event == "up" then CC_MouseButtonUp[button] = CC_MouseButtonUp[button] or {} table.insert(CC_MouseButtonUp[button], binding) elseif event == "mouse_move" then table.insert(CC_MouseMove, binding) end end -- XboxController DefineClass.CC_XboxController = { __parents = { "CharacterControl" }, xbox_controller_id = false, XboxHoldButtonTime = 350, xbox_hold_thread = false, XBoxComboButtonsDelay = 100, xbox_last_combo_button = false, xbox_last_combo_button_time = 0, } function CC_XboxController:Init(character, controller_id) self.xbox_controller_id = controller_id end function CC_XboxController:OnActivate() CharacterControl.OnActivate(self) if self.xbox_controller_id and self.camera_active then camera3p.EnableController(self.xbox_controller_id) end end function CC_XboxController:SetCameraActive(active) CharacterControl.SetCameraActive(self, active) if self.xbox_controller_id and self.active then if self.camera_active then camera3p.EnableController(self.xbox_controller_id) else camera3p.DisableController(self.xbox_controller_id) end end end function CC_XboxController:OnInactivate() CharacterControl.OnInactivate(self) DeleteThread(self.xbox_hold_thread) self.xbox_hold_thread = nil if self.xbox_controller_id then XInput.SetRumble(self.xbox_controller_id, 0, 0) camera3p.DisableController(self.xbox_controller_id) end end function CC_XboxController:GetActionBindings(action) return CC_XboxController_ActionBindings[action] end function CC_XboxController:GetBindingValue(binding) if not self.active then return end local button = binding.xbutton if button and not XInput.IsCtrlButtonPressed(self.xbox_controller_id, button) then return end if not self:BindingModifiersActive(binding) then return end local value = XInput.CurrentState[self.xbox_controller_id][button] return value end function CC_XboxController:BindingModifiersActive(binding) local buttons = binding.x_modifiers if buttons then for i = 1, #buttons do if not XInput.IsCtrlButtonPressed(self.xbox_controller_id, buttons[i]) then return false end end end return true end function CC_XboxController:OnXButtonDown(button, controller_id) if not self.active or controller_id ~= self.xbox_controller_id then return "continue" end -- hold if CC_XboxButtonHold[button] then DeleteThread(self.xbox_hold_thread) self.xbox_hold_thread = CreateRealTimeThread(function(self, button, controller_id) Sleep(self.XboxHoldButtonTime) self.xbox_hold_thread = false if XInput.IsCtrlButtonPressed(self.xbox_controller_id, button) then local xstate = XInput.CurrentState[controller_id] self:CallBindingsDown(CC_XboxButtonHold[button], xstate[button]) end end, self, button, controller_id) end local result if CC_XboxButtonDown[button] then result = self:CallBindingsDown(CC_XboxButtonDown[button], true) end if CC_XboxButtonCombo[button] then local handlers = self.xbox_last_combo_button and RealTime() - self.xbox_last_combo_button_time < self.XBoxComboButtonsDelay and CC_XboxButtonCombo[button][self.xbox_last_combo_button] if handlers then local result = self:CallBindingsDown(handlers, true) if result and result ~= "continue" then self.xbox_last_combo_button = false return result end end self.xbox_last_combo_button = button self.xbox_last_combo_button_time = RealTime() end return result or "continue" end function CC_XboxController:OnXButtonUp(button, controller_id) if not self.active or controller_id ~= self.xbox_controller_id then return "continue" end if self.xbox_last_combo_button == button then self.xbox_last_combo_button = false end if CC_XboxButtonHold[button] and self.xbox_hold_thread then DeleteThread(self.xbox_hold_thread) self.xbox_hold_thread = false end if CC_XboxButtonUp[button] then local result = self:CallBindingsUp(CC_XboxButtonUp[button]) if result ~= "continue" then return result end end return "continue" end function CC_XboxController:OnXNewPacket(_, controller_id, last_state, current_state) if not self.active or controller_id ~= self.xbox_controller_id then return "continue" end for i = 1, #CC_XboxControllerNewPacket do local button = CC_XboxControllerNewPacket[i] self:CallBindingsDown(CC_XboxControllerNewPacket[button], current_state[button]) end return "continue" end function CC_XboxController:SyncWithCharacter() self:SyncBindingsWithCharacter(CC_XboxControllerSync) end local function ResetXboxControllerBindings() CC_XboxButtonDown = {} CC_XboxButtonUp = {} CC_XboxButtonHold = {} CC_XboxButtonCombo = {} CC_XboxControllerNewPacket = {} CC_XboxController_ActionBindings = {} CC_XboxControllerSync = {} table.insert(CC_XboxControllerSync,{ func = function() MouseRotate(false) end}) end if FirstLoad then ResetXboxControllerBindings() end function ReloadXboxControllerBindings(default_bindings, predefined_bindings) ResetXboxControllerBindings() if not default_bindings then return end local bindings = {} for i = 1, #default_bindings do local default_list = default_bindings[i] local action = default_list[1] bindings[action] = {} local predefined_list = predefined_bindings and predefined_bindings[action] for i = 1, Max(predefined_list and #predefined_list or 0, #default_list-1) do local binding = predefined_list and predefined_list[i] or nil if binding == nil then binding = default_list and default_list[i+1] end if binding and #binding > 0 then local t = {} for k = 1, #binding do t[k] = binding[k] end table.insert(bindings[action], t) end end end ResolveRefBindings(default_bindings, bindings) for i = 1, #default_bindings do local action = default_bindings[i][1] local blist = bindings[action] for j = 1, #blist do local binding = blist[j] BindXboxController(action, unpack_params(binding)) end BindToXboxControllerSync(action) end end function BindXboxController(action, button, mod1, mod2) local class = _G["CCA_"..action] assert(class) if class then class:BindXboxController(action, button, mod1, mod2) end end function BindToXboxControllerEvent(action, event, func, button, mod1, mod2) if event == "sync" then if action or not table.find(CC_XboxControllerSync, "func", func) then local binding = { action = action, func = func } table.insert(CC_XboxControllerSync, binding) end return end local binding = { action = action, xbutton = button, func = func } if mod1 or mod2 then binding.x_modifiers = {} binding.x_modifiers[#binding.x_modifiers+1] = mod1 binding.x_modifiers[#binding.x_modifiers+1] = mod2 end local list if event == "down" then CC_XboxController_ActionBindings[action] = CC_XboxController_ActionBindings[action] or {} table.insert(CC_XboxController_ActionBindings[action], binding) list = CC_XboxButtonDown elseif event == "up" then list = CC_XboxButtonUp table.insert_unique(CC_XboxButtonUp, button) elseif event == "hold" then list = CC_XboxButtonHold elseif event == "combo" then list = CC_XboxButtonCombo elseif event == "change" then CC_XboxController_ActionBindings[action] = CC_XboxController_ActionBindings[action] or {} table.insert(CC_XboxController_ActionBindings[action], binding) table.insert_unique(CC_XboxControllerNewPacket, button) list = CC_XboxControllerNewPacket else return end if not list[button] then list[button] = {} end table.insert(list[button], binding) end