mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-03 10:01:08 -04:00
Merge pull request #343 from TwilitRealm/ira/gyro-aim
Initial implementation of gyro aim
This commit is contained in:
+6
-6
@@ -1338,17 +1338,17 @@ set(DUSK_FILES
|
||||
src/d/actor/d_a_alink_dusk.cpp
|
||||
src/dusk/asserts.cpp
|
||||
src/dusk/config.cpp
|
||||
src/dusk/settings.cpp
|
||||
src/dusk/logging.cpp
|
||||
src/dusk/frame_interpolation.cpp
|
||||
src/dusk/layout.cpp
|
||||
src/dusk/stubs.cpp
|
||||
src/dusk/endian.cpp
|
||||
src/dusk/extras.c
|
||||
src/dusk/extras.cpp
|
||||
src/dusk/io.cpp
|
||||
src/dusk/frame_interpolation.cpp
|
||||
src/dusk/globals.cpp
|
||||
src/dusk/gyro_aim.cpp
|
||||
src/dusk/io.cpp
|
||||
src/dusk/layout.cpp
|
||||
src/dusk/logging.cpp
|
||||
src/dusk/settings.cpp
|
||||
src/dusk/stubs.cpp
|
||||
#src/dusk/m_Do_ext_dusk.cpp
|
||||
src/dusk/imgui/ImGuiConfig.hpp
|
||||
src/dusk/imgui/ImGuiConsole.hpp
|
||||
|
||||
@@ -4550,6 +4550,7 @@ public:
|
||||
|
||||
#if TARGET_PC
|
||||
void handleQuickTransform();
|
||||
bool checkGyroAimItemContext();
|
||||
#endif
|
||||
}; // Size: 0x385C
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef DUSK_GYRO_AIM_H
|
||||
#define DUSK_GYRO_AIM_H
|
||||
|
||||
namespace dusk::gyro_aim {
|
||||
void read(float dt, bool context_active);
|
||||
void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad);
|
||||
bool queryGyroAimItemContext();
|
||||
} // namespace dusk::gyro_aim
|
||||
|
||||
#endif
|
||||
@@ -62,6 +62,12 @@ struct UserSettings {
|
||||
ConfigVar<bool> noLowHpSound;
|
||||
ConfigVar<bool> midnasLamentNonStop;
|
||||
|
||||
// Input
|
||||
ConfigVar<bool> enableGyroAim;
|
||||
ConfigVar<float> gyroAimSensitivity;
|
||||
ConfigVar<bool> gyroAimInvertPitch;
|
||||
ConfigVar<bool> gyroAimInvertYaw;
|
||||
|
||||
// Cheats
|
||||
ConfigVar<bool> enableFastIronBoots;
|
||||
ConfigVar<bool> canTransformAnywhere;
|
||||
|
||||
@@ -83,3 +83,32 @@ void daAlink_c::handleQuickTransform() {
|
||||
OSReport("Running quick transform!");
|
||||
procCoMetamorphoseInit();
|
||||
}
|
||||
|
||||
bool daAlink_c::checkGyroAimItemContext() {
|
||||
if (checkWolf()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mProcID) {
|
||||
case PROC_BOW_SUBJECT:
|
||||
case PROC_BOOMERANG_SUBJECT:
|
||||
case PROC_COPY_ROD_SUBJECT:
|
||||
case PROC_HOOKSHOT_SUBJECT:
|
||||
case PROC_SWIM_HOOKSHOT_SUBJECT:
|
||||
case PROC_HORSE_BOW_SUBJECT:
|
||||
case PROC_HORSE_BOOMERANG_SUBJECT:
|
||||
case PROC_HORSE_HOOKSHOT_SUBJECT:
|
||||
case PROC_CANOE_BOW_SUBJECT:
|
||||
case PROC_CANOE_BOOMERANG_SUBJECT:
|
||||
case PROC_CANOE_HOOKSHOT_SUBJECT:
|
||||
case PROC_HOOKSHOT_ROOF_WAIT:
|
||||
case PROC_HOOKSHOT_ROOF_SHOOT:
|
||||
case PROC_HOOKSHOT_WALL_WAIT:
|
||||
case PROC_HOOKSHOT_WALL_SHOOT:
|
||||
return true;
|
||||
case PROC_IRON_BALL_SUBJECT:
|
||||
return itemButton() && mItemVar0.field_0x3018 == 2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "d/actor/d_a_tag_mstop.h"
|
||||
#include "d/actor/d_a_tag_mhint.h"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/gyro_aim.h"
|
||||
#endif
|
||||
|
||||
bool daAlink_c::checkNoSubjectModeCamera() {
|
||||
return dCam_getBody()->Type() == dCam_getBody()->GetCameraTypeFromCameraName("Rotary") ||
|
||||
dCam_getBody()->Type() == dCam_getBody()->GetCameraTypeFromCameraName("Rampart2") ||
|
||||
@@ -125,6 +129,40 @@ BOOL daAlink_c::setBodyAngleToCamera() {
|
||||
sp8 = mBodyAngle.x;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableGyroAim && checkGyroAimItemContext()) {
|
||||
f32 gyro_scale = 1.0f;
|
||||
if (checkWolfEyeUp()) {
|
||||
gyro_scale *= 0.6f;
|
||||
}
|
||||
|
||||
if (dComIfGp_checkPlayerStatus0(0, 0x200000)) {
|
||||
gyro_scale /= dComIfGp_getCameraZoomScale(field_0x317c);
|
||||
}
|
||||
|
||||
f32 gy_yaw = 0.f;
|
||||
f32 gy_pitch = 0.f;
|
||||
dusk::gyro_aim::consumeAimDeltas(gy_yaw, gy_pitch);
|
||||
|
||||
if (dusk::getSettings().game.gyroAimInvertPitch) {
|
||||
gy_pitch = -gy_pitch;
|
||||
}
|
||||
if (dusk::getSettings().game.gyroAimInvertYaw) {
|
||||
gy_yaw = -gy_yaw;
|
||||
}
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
gy_yaw = -gy_yaw;
|
||||
}
|
||||
|
||||
shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale);
|
||||
sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale);
|
||||
|
||||
if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) {
|
||||
sp8 = mBodyAngle.x;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (checkNotItemSinkLimit() && sp8 > 0) {
|
||||
cLib_addCalcAngleS(&sp8, 0, 5, 0x1000, 0x400);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "dusk/gyro_aim.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include "d/actor/d_a_alink.h"
|
||||
|
||||
namespace dusk::gyro_aim {
|
||||
namespace {
|
||||
// TODO: Make deadband and smoothing configurable
|
||||
constexpr float kDeadbandRadS = 0.04f;
|
||||
constexpr float kSmoothAlpha = 0.35f;
|
||||
bool s_sensor_enabled = false;
|
||||
float s_smooth_gx = 0.0f;
|
||||
float s_smooth_gy = 0.0f;
|
||||
float s_pending_yaw_rad = 0.0f;
|
||||
float s_pending_pitch_rad = 0.0f;
|
||||
|
||||
void reset_filter_state() {
|
||||
s_smooth_gx = s_smooth_gy = 0.0f;
|
||||
s_pending_yaw_rad = s_pending_pitch_rad = 0.0f;
|
||||
}
|
||||
|
||||
float apply_deadband(float v) {
|
||||
if (v > -kDeadbandRadS && v < kDeadbandRadS) {
|
||||
return 0.0f;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void read(float dt, bool context_active) {
|
||||
if (!context_active || !static_cast<bool>(dusk::getSettings().game.enableGyroAim)) {
|
||||
SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0);
|
||||
if (pad != nullptr && s_sensor_enabled) {
|
||||
SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, false);
|
||||
s_sensor_enabled = false;
|
||||
}
|
||||
reset_filter_state();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0);
|
||||
if (pad == nullptr || !SDL_GamepadHasSensor(pad, SDL_SENSOR_GYRO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_sensor_enabled) {
|
||||
if (!SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, true)) {
|
||||
return;
|
||||
}
|
||||
s_sensor_enabled = true;
|
||||
reset_filter_state();
|
||||
}
|
||||
|
||||
float gyro[3];
|
||||
if (!SDL_GetGamepadSensorData(pad, SDL_SENSOR_GYRO, gyro, 3)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_smooth_gx += kSmoothAlpha * (gyro[0] - s_smooth_gx);
|
||||
s_smooth_gy += kSmoothAlpha * (gyro[1] - s_smooth_gy);
|
||||
float yaw_rate = apply_deadband(s_smooth_gy);
|
||||
float pitch_rate = apply_deadband(s_smooth_gx);
|
||||
|
||||
const float sens = dusk::getSettings().game.gyroAimSensitivity;
|
||||
s_pending_yaw_rad += yaw_rate * dt * sens;
|
||||
s_pending_pitch_rad += pitch_rate * dt * sens;
|
||||
}
|
||||
|
||||
void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad) {
|
||||
out_yaw_rad = s_pending_yaw_rad;
|
||||
out_pitch_rad = s_pending_pitch_rad;
|
||||
s_pending_yaw_rad = 0.0f;
|
||||
s_pending_pitch_rad = 0.0f;
|
||||
}
|
||||
|
||||
bool queryGyroAimItemContext() {
|
||||
if (!static_cast<bool>(dusk::getSettings().game.enableGyroAim)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
daAlink_c* link = daAlink_getAlinkActorClass();
|
||||
if (link == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return link->checkGyroAimItemContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10);
|
||||
}
|
||||
} // namespace dusk::gyro_aim
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "ImGuiMenuGame.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace dusk {
|
||||
void ImGuiMenuGame::windowInputViewer() {
|
||||
if (!m_showInputViewer) {
|
||||
@@ -258,9 +260,37 @@ namespace dusk {
|
||||
size.y = 130 * scale;
|
||||
ImGui::Dummy(size);
|
||||
|
||||
SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0);
|
||||
if (pad != nullptr && SDL_GamepadHasSensor(pad, SDL_SENSOR_GYRO)) {
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted("Gyro");
|
||||
|
||||
constexpr float kBarScale = 4.0f;
|
||||
auto bar = [kBarScale](const char* label, float v) {
|
||||
const float a = std::fabs(v);
|
||||
const float t = std::min(1.f, a / kBarScale);
|
||||
char overlay[32];
|
||||
snprintf(overlay, sizeof(overlay), "%s %+.3f", label, v);
|
||||
ImGui::ProgressBar(t, ImVec2(-1, 0), overlay);
|
||||
};
|
||||
|
||||
if (SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, true)) {
|
||||
float gyro[3];
|
||||
if (SDL_GetGamepadSensorData(pad, SDL_SENSOR_GYRO, gyro, 3)) {
|
||||
bar("X", gyro[0]);
|
||||
bar("Y", gyro[1]);
|
||||
bar("Z", gyro[2]);
|
||||
}
|
||||
} else {
|
||||
bar("X", 0.f);
|
||||
bar("Y", 0.f);
|
||||
bar("Z", 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
ShowCornerContextMenu(m_inputOverlayCorner, 0);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
} // namespace dusk
|
||||
} // namespace dusk
|
||||
|
||||
@@ -112,6 +112,20 @@ namespace dusk {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Input")) {
|
||||
config::ImGuiCheckbox("Gyro Aim", getSettings().game.enableGyroAim);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Enables the gyroscope on supported controllers while aiming the\n"
|
||||
"Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod.");
|
||||
}
|
||||
|
||||
config::ImGuiSliderFloat("Gyro Sensitivity", getSettings().game.gyroAimSensitivity, 0.25f, 4.0f, "%.2f");
|
||||
config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch);
|
||||
config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Cheats")) {
|
||||
config::ImGuiCheckbox("Fast Iron Boots", getSettings().game.enableFastIronBoots);
|
||||
|
||||
|
||||
+11
-1
@@ -50,6 +50,12 @@ UserSettings g_userSettings = {
|
||||
.noLowHpSound {"game.noLowHpSound", false},
|
||||
.midnasLamentNonStop {"game.midnasLamentNonStop", false},
|
||||
|
||||
// Input
|
||||
.enableGyroAim {"game.enableGyroAim", false},
|
||||
.gyroAimSensitivity {"game.gyroAimSensitivity", 1.0f},
|
||||
.gyroAimInvertPitch {"game.gyroAimInvertPitch", false},
|
||||
.gyroAimInvertYaw {"game.gyroAimInvertYaw", false},
|
||||
|
||||
// Cheats
|
||||
.enableFastIronBoots {"game.enableFastIronBoots", false},
|
||||
.canTransformAnywhere {"game.canTransformAnywhere", false},
|
||||
@@ -60,7 +66,7 @@ UserSettings g_userSettings = {
|
||||
.restoreWiiGlitches {"game.restoreWiiGlitches", false},
|
||||
|
||||
// Controls
|
||||
.enableTurboKeybind {"game.enableTurboKeybind", false},
|
||||
.enableTurboKeybind {"game.enableTurboKeybind", false}
|
||||
},
|
||||
|
||||
.backend = {
|
||||
@@ -119,6 +125,10 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.enableTurboKeybind);
|
||||
Register(g_userSettings.game.fastSpinner);
|
||||
Register(g_userSettings.game.enableFrameInterpolation);
|
||||
Register(g_userSettings.game.enableGyroAim);
|
||||
Register(g_userSettings.game.gyroAimSensitivity);
|
||||
Register(g_userSettings.game.gyroAimInvertPitch);
|
||||
Register(g_userSettings.game.gyroAimInvertYaw);
|
||||
|
||||
Register(g_userSettings.backend.isoPath);
|
||||
Register(g_userSettings.backend.graphicsBackend);
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "dusk/app_info.hpp"
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/gyro_aim.h"
|
||||
#include "dusk/imgui/ImGuiEngine.hpp"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/main.h"
|
||||
@@ -241,6 +242,9 @@ void main01(void) {
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
while (accumulator >= kSimStepSeconds) {
|
||||
mDoCPd_c::read();
|
||||
if (dusk::getSettings().game.enableGyroAim) {
|
||||
dusk::gyro_aim::read(static_cast<float>(kSimStepSeconds), dusk::gyro_aim::queryGyroAimItemContext());
|
||||
}
|
||||
fapGm_Execute();
|
||||
mDoAud_Execute();
|
||||
accumulator -= kSimStepSeconds;
|
||||
@@ -254,6 +258,9 @@ void main01(void) {
|
||||
|
||||
// Game Inputs
|
||||
mDoCPd_c::read();
|
||||
if (dusk::getSettings().game.enableGyroAim) {
|
||||
dusk::gyro_aim::read(static_cast<float>(frame_seconds), dusk::gyro_aim::queryGyroAimItemContext());
|
||||
}
|
||||
|
||||
// EXECUTE GAME LOGIC & RENDER
|
||||
// This calls mDoGph_Painter -> JFWDisplay -> GX Functions
|
||||
|
||||
Reference in New Issue
Block a user