|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
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") |
|
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) |
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
function CC_KeyboardAndMouse:OnKbdKeyDown(virtual_key, repeated, time) |
|
if repeated or not self.active then |
|
return "continue" |
|
end |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
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 |
|
|