User File #638388940010273093

Upload All User Files

#638388940010273093 - SM64DS Controls Fix script

sm64ds_controls_fix.lua
130 downloads
Uploaded 12/23/2023 2:06 AM by YoshiRulz (see all 2)
revision 2023.11.24 for EmuHawk ≥ 2.9.2
While this script is running, you'll be able to use the left analog stick on your gamepad to more precisely control Yoshi and the plumbers, similar to the original SM64. It also lets you control the camera with either the bumpers or the right analog stick. As a bonus, you can exit course without a mouse since it's in the same spot as the camera controls.
At time of writing, the default binds have the DPad bound to the left stick and L/R bound to the bumpers—you'll need to rebind those to the DPad and triggers. And make sure your controls are set to the default 'Standard Mode' in the pause menu. You don't need to unbind X or Y, though you are free to rearrange the face buttons.
-- SM64DS Controls Fix script by YoshiRulz
-- revision 2023.11.24 for EmuHawk >= 2.9.2
-- This program is published under the GNU General Public License, version 3, or (at your option) any later version. Full text at gnu.org/licenses.

-- While this script is running, you'll be able to use the left analog stick on your gamepad to more precisely control Yoshi and the plumbers, similar to the original SM64.
-- It also lets you control the camera with either the bumpers or the right analog stick. As a bonus, you can exit course without a mouse since it's in the same spot as the camera controls.
-- At time of writing, the default binds have the DPad bound to the left stick and L/R bound to the bumpers--you'll need to rebind those to the DPad and triggers.
-- And make sure your controls are set to the default 'Standard Mode' in the pause menu. You don't need to unbind X or Y, though you are free to rearrange the face buttons.

-- config:
local GAMEPAD_PFX = "X1 "; -- with trailing space; easiest way to find this is to bind something to your gamepad
-- keeping in mind sticks have a radius of 10000 (i.e. each axis is -10000..10000)
local DEADZONE = 1000; -- "radius" of the inner square; when any individual stick axis is less than this (in the range -DEADZONE..DEADZONE), it's clipped to 0
local WALK_THRESHOLD = 6000; -- "radius" of the outer square; when the stick is outside the square you'll run, within, you'll walk
INVERT_LSTICK_X = false; INVERT_LSTICK_Y = false; -- self-explanatory
INVERT_RSTICK_X = false; INVERT_RSTICK_Y = false; -- self-explanatory, though inverting Y is pointless, and inverting X also applies to the bumpers since they do the same thing
-- That's it! Past here is all implementation.

--TODO detect control scheme
--TODO work under the other 2 control schemes
--TODO use touch screen for movement

local AXES = {
	["LSTICK_X"] = GAMEPAD_PFX.."LeftThumbX Axis",
	["LSTICK_Y"] = GAMEPAD_PFX.."LeftThumbY Axis",
	["RSTICK_X"] = GAMEPAD_PFX.."RightThumbX Axis",
	["RSTICK_Y"] = GAMEPAD_PFX.."RightThumbY Axis",
};
local BUTTONS = {
	["LB"] = GAMEPAD_PFX.."LeftShoulder",
	["RB"] = GAMEPAD_PFX.."RightShoulder",
};
local buttons_down_prev = {};
event.onloadstate(function() buttons_down_prev = {}; end);
local rotate = 0;

while true do
	local axis_values = input.get_pressed_axes();
	local buttons_down = input.get();
	for k, v in pairs(AXES) do
		local axis_val = axis_values[v] or 0;
		if math.abs(axis_val) < DEADZONE then
			axis_values[v] = 0;
		elseif _G["INVERT_"..k] then -- galaxy brain code golf
			axis_values[v] = -axis_val;
		end
	end
	if INVERT_RSTICK_X then
		buttons_down[BUTTONS.LB], buttons_down[BUTTONS.RB] = buttons_down[BUTTONS.RB], buttons_down[BUTTONS.LB];
	end
	local to_set = {};
	local to_set_axes = {};

	local x, y = axis_values[AXES.LSTICK_X], axis_values[AXES.LSTICK_Y];
	local run_x, run_y = WALK_THRESHOLD < math.abs(x), WALK_THRESHOLD < math.abs(y);
	if run_x or run_y then to_set.Y = true; end
	to_set[(run_x or not run_y) and (x < 0 and "Left" or x > 0 and "Right")] = true;
	to_set[(run_y or not run_x) and (y < 0 and "Down" or y > 0 and "Up")] = true;

	local x, y = axis_values[AXES.RSTICK_X], axis_values[AXES.RSTICK_Y];
	if WALK_THRESHOLD < math.abs(y) then to_set.X = true; end
	if not buttons_down[BUTTONS.LB] and x < -WALK_THRESHOLD then buttons_down[BUTTONS.LB] = true; end
	if not buttons_down[BUTTONS.RB] and WALK_THRESHOLD < x then buttons_down[BUTTONS.RB] = true; end
	if buttons_down[BUTTONS.LB] then
		if not (buttons_down[BUTTONS.RB] and buttons_down_prev[BUTTONS.LB]) then
			rotate = -1;
		elseif not buttons_down_prev[BUTTONS.RB] then
			rotate = 1;
		end
	elseif buttons_down[BUTTONS.RB] then
		rotate = 1;
	else
		if rotate ~= 0 then client.clearautohold(); end -- warning: possibly conflicts with other scripts
		rotate = 0;
	end
	if rotate < 0 then
		to_set_axes["Touch X"] = 0x1F;
		to_set_axes["Touch Y"] = 0x9F;
		to_set.Touch = true;
	elseif rotate ~= 0 then
		to_set_axes["Touch X"] = 0x3F;
		to_set_axes["Touch Y"] = 0x9F;
		to_set.Touch = true;
	end

	joypad.set(to_set);
	joypad.setanalog(to_set_axes);
	buttons_down_prev = buttons_down;
	emu.frameadvance();
end