Restore freecam mouse look and block battle mouse actions

This commit is contained in:
CAtlas0784 committed 2026-10-17 23:49:51 +07:00
1 parent 0d3881ae73
commit 1ed0fb73f9
3 files changed
+123 -20

No files matched your search

+107 -15
View File
@@ -5,8 +5,6 @@ local Time = CS.UnityEngine.Time
local Camera = CS.UnityEngine.Camera
local GameObject = CS.UnityEngine.GameObject
-- Camera control tuning, in degrees per second (independent of frame rate).
local KEYBOARD_TURN_SPEED = 90.0
_G.__FREECAM_ENABLED = _G.__FREECAM_ENABLED or false
_G.__FREECAM_SPEED = _G.__FREECAM_SPEED or 15.0
@@ -72,6 +70,88 @@ local function set_cinemachine_enabled(cam, enabled)
end
end
-- Client inspection: InControlInputModule.allowMouseInput controls UI
-- clicks. Battle shortcuts also read MouseBindingSource independently.
-- Mask only mouse button controls in memory; leave keyboard bindings intact.
-- Do not use RemoveBinding/AddBinding (which emit OnBindingsChanged).
_G.__FC_MOUSE_GATE = _G.__FC_MOUSE_GATE or {modules={}, bindings={}}
local function restore_mouse_gate()
local gate = _G.__FC_MOUSE_GATE
for _, state in ipairs(gate.modules) do
pcall(function() state.module.allowMouseInput = state.allowed end)
end
for _, state in ipairs(gate.bindings) do
pcall(function()
if state.binding.Control == CS.InControl.Mouse.None then
gate.controlField:SetValue(state.binding, state.control)
state.action:ClearInputState()
end
end)
end
gate.modules, gate.bindings, gate.sets = {}, {}, {}
gate.active = false
end
local function update_mouse_gate()
local gate = _G.__FC_MOUSE_GATE
local battleHUD = GameObject.Find("UIRoot/Page/BattleGamePhaseUI(Clone)")
local wanted = _G.__FREECAM_ENABLED and battleHUD ~= nil
-- Releasing Freecam while holding RMB must not become a battle click.
if not wanted then
if gate.active and not Input.GetMouseButton(0) and not Input.GetMouseButton(1) then
restore_mouse_gate()
end
return
end
gate.active = true
if not gate.controlField then
local flags = CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags), 60)
gate.controlField = typeof(CS.InControl.MouseBindingSource):GetField("<Control>k__BackingField", flags)
gate.setsField = typeof(CS.InControl.InputManager):GetField("playerActionSets", flags)
assert(gate.controlField and gate.setsField, "Client mouse input schema is unavailable")
end
local eventSystem = CS.UnityEngine.EventSystems.EventSystem.current
if eventSystem then
local module = eventSystem:GetComponent(typeof(CS.InControl.InControlInputModule))
if module then
local recorded = false
for _, state in ipairs(gate.modules) do
if state.module == module then recorded = true; break end
end
if not recorded then
gate.modules[#gate.modules + 1] = {module=module, allowed=module.allowMouseInput}
end
module.allowMouseInput = false
end
end
gate.sets = gate.sets or {}
local sets = gate.setsField:GetValue(nil)
-- Scan each newly created action set once, rather than reflecting/scanning
-- the assembly or all bindings on every render callback.
for i = 0, sets.Count - 1 do
local actionSet = sets[i]
if not gate.sets[actionSet] then
gate.sets[actionSet] = true
local actions = actionSet.Actions
for j = 0, actions.Count - 1 do
local action = actions[j]
local bindings = action.Bindings
for k = 0, bindings.Count - 1 do
local binding = bindings[k]
if typeof(CS.InControl.MouseBindingSource):IsInstanceOfType(binding) then
local control = binding.Control
if control == CS.InControl.Mouse.LeftButton or control == CS.InControl.Mouse.RightButton then
gate.bindings[#gate.bindings + 1] = {binding=binding, control=control, action=action}
gate.controlField:SetValue(binding, CS.InControl.Mouse.None)
action:ClearInputState()
end
end
end
end
end
end
end
function _G.__FREECAM_TICK()
pcall(function()
-- Render callbacks run for several cameras/passes in one frame. Consume
@@ -98,7 +178,7 @@ function _G.__FREECAM_TICK()
_G.__FC_EULER = cam.transform.eulerAngles
set_cinemachine_enabled(cam, false)
end
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | I/J/K/L: Look</color>")
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look</color>")
else
if cam then
set_cinemachine_enabled(cam, true)
@@ -107,6 +187,12 @@ function _G.__FREECAM_TICK()
end
end
local gateOK, gateError = pcall(update_mouse_gate)
if not gateOK and not _G.__FC_GATE_ERROR then
_G.__FC_GATE_ERROR = true
CS.UnityEngine.Debug.Log("[FreeCam] Mouse gate error: " .. tostring(gateError))
end
if _G.__FREECAM_ENABLED then
local cam = get_active_cam()
if cam then
@@ -125,23 +211,21 @@ function _G.__FREECAM_TICK()
-- Speed adjustment (+ / -)
if Input.GetKeyDown(KeyCode.Equals) or Input.GetKeyDown(KeyCode.KeypadPlus) then
_G.__FREECAM_SPEED = math.min(300.0, _G.__FREECAM_SPEED * 1.5)
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | I/J/K/L: Look</color>")
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look</color>")
end
if Input.GetKeyDown(KeyCode.Minus) or Input.GetKeyDown(KeyCode.KeypadMinus) then
_G.__FREECAM_SPEED = math.max(0.5, _G.__FREECAM_SPEED / 1.5)
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | I/J/K/L: Look</color>")
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look</color>")
end
-- Keyboard-only look: no mouse click is needed, so turning
-- the camera does not require pressing a battle action button.
local pitch, yaw = 0, 0
if Input.GetKey(KeyCode.I) then pitch = pitch - 1 end
if Input.GetKey(KeyCode.K) then pitch = pitch + 1 end
if Input.GetKey(KeyCode.J) then yaw = yaw - 1 end
if Input.GetKey(KeyCode.L) then yaw = yaw + 1 end
_G.__FC_EULER = Vector3(
_G.__FC_EULER.x + pitch * KEYBOARD_TURN_SPEED * dt,
_G.__FC_EULER.y + yaw * KEYBOARD_TURN_SPEED * dt, 0)
-- Raw Unity mouse input remains available to the camera while
-- InControl battle clicks are gated separately below.
if Input.GetMouseButton(1) then
local rotX = Input.GetAxis("Mouse X") or 0
local rotY = Input.GetAxis("Mouse Y") or 0
_G.__FC_EULER = Vector3(_G.__FC_EULER.x - rotY * 2.5,
_G.__FC_EULER.y + rotX * 2.5, 0)
end
-- Compute 3D direction vectors from camera Euler angles
local radY = _G.__FC_EULER.y * (math.pi / 180.0)
@@ -204,3 +288,11 @@ end
if not _G.__FREECAM_ENABLED then
show_ui(RAINBOW_TEXT)
end
-- Restore the in-memory controls before the Client tears down its settings.
if not _G.__FC_MOUSE_QUIT_REGISTERED then
local ok = pcall(function()
CS.UnityEngine.Application.quitting('+', function() restore_mouse_gate() end)
end)
_G.__FC_MOUSE_QUIT_REGISTERED = ok
end
+8
View File
@@ -15,6 +15,14 @@ local HUD_PATHS = {
"UIRoot/BelowPage/MazeJumpingText(Clone)",
"UIRoot/BelowPage/MazeMonsterWarningPage(Clone)",
"UIRoot/AboveDialog/BetaHintDialog(Clone)",
-- Character controls observed under AvatarMainPage/Root. Keep RankPanel
-- and dialogs intact so Eidolon illustrations are not hidden with menus.
"UIRoot/Page/AvatarMainPage(Clone)/Root/AvatarSubMenuPanel_Vertical",
"UIRoot/Page/AvatarMainPage(Clone)/Root/AvatarDetailPanel",
"UIRoot/Page/AvatarMainPage(Clone)/Root/AvatarTopListPanel",
"UIRoot/Page/AvatarMainPage(Clone)/Root/AvatarListPanel",
"UIRoot/Page/AvatarMainPage(Clone)/Root/BtnClose",
"UIRoot/Page/AvatarMainPage(Clone)/Root/TopBarArea",
"VersionText"
}