mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
Fix/trigger effect state (#1132)
* Pad: report the adaptive trigger state instead of always 0 scePadGetTriggerEffectState returned 0 for both triggers. Games that wait for the state of the trigger effect they set before acting on L2/R2 never saw it change; in Astro Bot, L2/R2 actions such as punches did nothing in some levels. The state now follows from the game's last effect per trigger (already kept for the DualSense output) and the trigger's travel, 0..255 over the effect's ten positions: feedback no force / pushing, weapon not / almost / fully pressed, vibration not firing / firing, with the values 1..7 that duaLib (WujekFoliarz/duaLib) reads from a DualSense input report. SDL does not expose that report, so this is an approximation; it also works with other pads and the keyboard, where a digital L2/R2 is full travel. The trigger intensity setting changes only the output, not the reported state. Tested in Astro Bot (PPSA21567 1.018) by a player in the levels where the triggers did nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tests: controller_settings covers scePadGetTriggerEffectState Feedback, weapon and vibration states across the trigger travel, a trigger without an effect, a masked update keeping the other trigger's effect, and the intensity setting leaving the reported state unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pad: a feedback trigger reports pushing only while it is pressed A feedback effect from position 0 reported FEEDBACK_IS_PUSHING on an untouched trigger, so a game waiting for the change from no force to pushing never saw it. In Astro's Playroom the gacha capsules did not break on R2. Feedback states (modes 1, 4, 5) now need some travel (>= 8 of 255). Tested in Astro's Playroom (PPSA01325): the capsules break on R2 again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pad: a vibration trigger reports firing only while it is pressed A vibration effect from position 0 reported VIBRATION_IS_FIRING on an untouched trigger; in Astro's Playroom the gun fired by itself. Vibration states (modes 3 and 6) now need some travel, as the feedback states do. Tested in Astro's Playroom. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pad: reject invalid handles in scePadGetTriggerEffectState; test the remaining effect modes scePadGetTriggerEffectState ignored its handle and returned pad 1's state for any handle. It now returns SCE_PAD_ERROR_INVALID_HANDLE (0x80920003) for handles other than 1, like the other pad queries in libPad.cpp and duaLib. controller_settings now also covers multiple-position feedback (mode 4: pushes only at positions with a strength), slope feedback (mode 5: pushes from its start position, not on an untouched trigger) and multiple-position vibration (mode 6: fires only at positions with an amplitude and a frequency). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pad: scePadGetTriggerEffectState returns libScePad's invalid-argument code A null state returned 0x80960005, which is SCE_USER_SERVICE_ERROR_INVALID_ARGUMENT, not a pad error; libScePad's code is SCE_PAD_ERROR_INVALID_ARG (0x80920001), as scePadSetTriggerEffect returns. The call now lives with the other pad calls in controller.cpp so controller_settings tests its handle and argument checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pad: correct trigger effect state reporting --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
@@ -67,8 +67,13 @@ struct PadTriggerEffectParam {
|
||||
PadTriggerEffectCommand command[2];
|
||||
};
|
||||
|
||||
struct PadTriggerEffectStateInformation {
|
||||
int32_t state[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(PadTriggerEffectCommand) == 56);
|
||||
static_assert(sizeof(PadTriggerEffectParam) == 120);
|
||||
static_assert(sizeof(PadTriggerEffectStateInformation) == 8);
|
||||
|
||||
struct DualSenseEffects {
|
||||
uint8_t enable_bits;
|
||||
@@ -118,6 +123,7 @@ public:
|
||||
int GetActiveControllerId();
|
||||
void SetLightBar(uint8_t r, uint8_t g, uint8_t b);
|
||||
bool SetTriggerEffect(const PadTriggerEffectParam& param);
|
||||
void GetTriggerEffectState(PadTriggerEffectStateInformation* info);
|
||||
void CycleSetting(Setting setting);
|
||||
float GetSettingScale(Setting setting) const;
|
||||
void ReadState(ControllerState* state, bool* flag, int* count);
|
||||
@@ -131,6 +137,7 @@ private:
|
||||
// Apply cached output without changing its game-requested lifetime; caller holds m_mutex.
|
||||
void ApplyVibration();
|
||||
bool SendTriggerEffect(const PadTriggerEffectParam& param);
|
||||
void UpdateTriggerEffectState();
|
||||
|
||||
Common::Mutex m_mutex;
|
||||
std::vector<int> m_connected_ids;
|
||||
@@ -152,6 +159,7 @@ private:
|
||||
std::array<uint8_t, 2> m_vibration {};
|
||||
uint64_t m_vibration_until = 0;
|
||||
PadTriggerEffectParam m_trigger_effect {};
|
||||
PadTriggerEffectStateInformation m_trigger_state {};
|
||||
// Setting changes share the output lock; audio only needs an atomic scale snapshot.
|
||||
std::array<std::atomic<uint32_t>, 3> m_setting_steps {};
|
||||
};
|
||||
@@ -330,6 +338,51 @@ static bool trigger_effect_to_dualsense(const PadTriggerEffectCommand& command,
|
||||
}
|
||||
}
|
||||
|
||||
enum TriggerState : int32_t {
|
||||
TRIGGER_STATE_OFF = 0,
|
||||
TRIGGER_STATE_FEEDBACK_STANDBY = 1,
|
||||
TRIGGER_STATE_FEEDBACK_ACTIVE = 2,
|
||||
TRIGGER_STATE_WEAPON_STANDBY = 3,
|
||||
TRIGGER_STATE_WEAPON_PULLING = 4,
|
||||
TRIGGER_STATE_WEAPON_FIRED = 5,
|
||||
TRIGGER_STATE_VIBRATION_STANDBY = 6,
|
||||
TRIGGER_STATE_VIBRATION_ACTIVE = 7,
|
||||
};
|
||||
|
||||
static int32_t TriggerEffectState(const PadTriggerEffectCommand& command, int value,
|
||||
int32_t previous) {
|
||||
// Trigger positions use nonuniform travel ranges, with endpoints at 0 and 255.
|
||||
const int pos = value == 0 ? 0 : value == 255 ? 9 : 1 + value / 32;
|
||||
switch (command.mode) {
|
||||
case 1: // feedback: resistance from position data[0], strength data[1]
|
||||
return command.data[1] != 0 && pos >= command.data[0] ? TRIGGER_STATE_FEEDBACK_ACTIVE
|
||||
: TRIGGER_STATE_FEEDBACK_STANDBY;
|
||||
case 2: // weapon: resistance from data[0], released at data[1]
|
||||
if (value < command.data[0] * 32) {
|
||||
return TRIGGER_STATE_WEAPON_STANDBY;
|
||||
}
|
||||
return previous == TRIGGER_STATE_WEAPON_FIRED ||
|
||||
value >= std::min(command.data[1] * 32, 254)
|
||||
? TRIGGER_STATE_WEAPON_FIRED
|
||||
: TRIGGER_STATE_WEAPON_PULLING;
|
||||
case 3: // vibration from position data[0], amplitude data[1], frequency data[2]
|
||||
return command.data[1] != 0 && command.data[2] != 0 && pos >= command.data[0]
|
||||
? TRIGGER_STATE_VIBRATION_ACTIVE
|
||||
: TRIGGER_STATE_VIBRATION_STANDBY;
|
||||
case 4: // multiple-position feedback: strength per position
|
||||
return command.data[pos] != 0 ? TRIGGER_STATE_FEEDBACK_ACTIVE
|
||||
: TRIGGER_STATE_FEEDBACK_STANDBY;
|
||||
case 5: // slope feedback between data[0] and data[1]
|
||||
return pos >= command.data[0] && pos < command.data[1] ? TRIGGER_STATE_FEEDBACK_ACTIVE
|
||||
: TRIGGER_STATE_FEEDBACK_STANDBY;
|
||||
case 6: // multiple-position vibration: frequency data[0], amplitude per position
|
||||
return command.data[0] != 0 && command.data[1 + pos] != 0
|
||||
? TRIGGER_STATE_VIBRATION_ACTIVE
|
||||
: TRIGGER_STATE_VIBRATION_STANDBY;
|
||||
default: return TRIGGER_STATE_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
EXIT_IF(g_controller != nullptr);
|
||||
|
||||
@@ -419,6 +472,7 @@ void GameController::CheckActive() {
|
||||
m_vibration = {};
|
||||
m_vibration_until = 0;
|
||||
m_trigger_effect = {};
|
||||
m_trigger_state = {};
|
||||
}
|
||||
|
||||
void GameController::AddState() {
|
||||
@@ -466,6 +520,7 @@ void GameController::Axis(int id, Controller::Axis axis, int value) {
|
||||
}
|
||||
if (trigger != 0) {
|
||||
m_state.buttons = value > 0 ? m_state.buttons | trigger : m_state.buttons & ~trigger;
|
||||
UpdateTriggerEffectState();
|
||||
}
|
||||
|
||||
AddState();
|
||||
@@ -622,6 +677,7 @@ void GameController::ResetInputState() {
|
||||
m_state.buttons = 0;
|
||||
std::fill_n(m_state.axes, 4, 128);
|
||||
std::fill_n(m_state.axes + 4, 2, 0);
|
||||
UpdateTriggerEffectState();
|
||||
for (auto& touch: m_state.touch) {
|
||||
touch = {};
|
||||
}
|
||||
@@ -730,9 +786,25 @@ bool GameController::SetTriggerEffect(const PadTriggerEffectParam& param) {
|
||||
m_trigger_effect.command[i] = param.command[i];
|
||||
}
|
||||
}
|
||||
UpdateTriggerEffectState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameController::GetTriggerEffectState(PadTriggerEffectStateInformation* info) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
*info = m_trigger_state;
|
||||
}
|
||||
|
||||
void GameController::UpdateTriggerEffectState() {
|
||||
// Track every input event so a weapon remains fired even when the game polls after release
|
||||
// starts.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
m_trigger_state.state[i] = TriggerEffectState(
|
||||
m_trigger_effect.command[i], m_state.axes[static_cast<int>(Axis::TriggerLeft) + i],
|
||||
m_trigger_state.state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool GameController::SendTriggerEffect(const PadTriggerEffectParam& param) {
|
||||
if ((param.trigger_mask & ~0x03u) != 0) {
|
||||
return false;
|
||||
@@ -1098,4 +1170,21 @@ int KYTY_SYSV_ABI PadSetTriggerEffect(int handle, const PadTriggerEffectParam* p
|
||||
return g_controller->SetTriggerEffect(*param) ? OK : PAD_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadGetTriggerEffectState(int handle, PadTriggerEffectStateInformation* info) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t handle = %d\n", handle);
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
if (info == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
g_controller->GetTriggerEffectState(info);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace Libs::Controller
|
||||
|
||||
@@ -53,6 +53,7 @@ struct PadData;
|
||||
struct PadVibrationParam;
|
||||
struct PadLightBarParam;
|
||||
struct PadTriggerEffectParam;
|
||||
struct PadTriggerEffectStateInformation;
|
||||
|
||||
inline int controller_get_axis(int min, int max, int value) {
|
||||
int v = (255 * (value - min)) / (max - min);
|
||||
@@ -92,6 +93,7 @@ int KYTY_SYSV_ABI PadReadState(int handle, PadData* data);
|
||||
int KYTY_SYSV_ABI PadRead(int handle, PadData* data, int num);
|
||||
int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param);
|
||||
int KYTY_SYSV_ABI PadSetTriggerEffect(int handle, const PadTriggerEffectParam* param);
|
||||
int KYTY_SYSV_ABI PadGetTriggerEffectState(int handle, PadTriggerEffectStateInformation* info);
|
||||
int KYTY_SYSV_ABI PadResetLightBar(int handle);
|
||||
int KYTY_SYSV_ABI PadSetLightBar(int handle, const PadLightBarParam* param);
|
||||
|
||||
|
||||
+1
-21
@@ -22,26 +22,6 @@ static int KYTY_SYSV_ABI PadSetVibrationMode(int handle, int mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct PadTriggerEffectStateInformation {
|
||||
int32_t state[2];
|
||||
};
|
||||
|
||||
static int KYTY_SYSV_ABI PadGetTriggerEffectState(int handle,
|
||||
PadTriggerEffectStateInformation* info) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t handle = %d\n", handle);
|
||||
|
||||
if (info == nullptr) {
|
||||
return -2137653243; /* 0x80960005 */
|
||||
}
|
||||
|
||||
info->state[0] = 0;
|
||||
info->state[1] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI PadSetVibrationTriggerEffectWeakWhileEmbeddedMicInUse(bool enabled) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -214,7 +194,7 @@ LIB_DEFINE(InitPad_1) {
|
||||
LIB_FUNC("yFVnOdGxvZY", Controller::PadSetVibration);
|
||||
LIB_FUNC("W2G-yoyMF5U", PadSetVibrationMode);
|
||||
LIB_FUNC("Yq0zOH7YNOM", PadSetVibrationTriggerEffectWeakWhileEmbeddedMicInUse);
|
||||
LIB_FUNC("znaWI0gpuo8", PadGetTriggerEffectState);
|
||||
LIB_FUNC("znaWI0gpuo8", Controller::PadGetTriggerEffectState);
|
||||
LIB_FUNC("DscD1i9HX1w", Controller::PadResetLightBar);
|
||||
LIB_FUNC("RR4novUEENY", Controller::PadSetLightBar);
|
||||
LIB_FUNC("6ncge5+l5Qs", PadClose);
|
||||
|
||||
Reference in New Issue
Block a user