2019-12-22 20:04:07 +00:00
|
|
|
#include "Runtime/Input/CInputGenerator.hpp"
|
|
|
|
|
|
|
|
#include "Runtime/CArchitectureMessage.hpp"
|
|
|
|
#include "Runtime/CArchitectureQueue.hpp"
|
2022-02-22 05:53:57 +00:00
|
|
|
|
|
|
|
#include <magic_enum.hpp>
|
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
namespace metaforce {
|
2015-08-28 00:11:31 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CInputGenerator::Update(float dt, CArchitectureQueue& queue) {
|
|
|
|
if (m_firstFrame) {
|
|
|
|
m_firstFrame = false;
|
|
|
|
return;
|
|
|
|
}
|
2016-02-21 06:34:42 +00:00
|
|
|
|
2022-02-08 01:48:08 +00:00
|
|
|
const CFinalInput& kbInput = getFinalInput(0, dt);
|
|
|
|
queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, kbInput));
|
2015-08-29 01:30:47 +00:00
|
|
|
|
2022-02-09 08:54:53 +00:00
|
|
|
/* Dolphin controllers next */
|
|
|
|
// for (int i = 0; i < 4; ++i) {
|
|
|
|
// bool connected;
|
|
|
|
// EStatusChange change = m_dolphinCb.getStatusChange(i, connected);
|
|
|
|
// if (change != EStatusChange::NoChange)
|
|
|
|
// queue.Push(MakeMsg::CreateControllerStatus(EArchMsgTarget::Game, i, connected));
|
|
|
|
// if (connected) {
|
|
|
|
// CFinalInput input = m_dolphinCb.getFinalInput(i, dt, m_leftDiv, m_rightDiv);
|
|
|
|
// if (i == 0) /* Merge KB input with first controller */
|
|
|
|
// {
|
|
|
|
// input |= kbInput;
|
|
|
|
// kbUsed = true;
|
|
|
|
// }
|
|
|
|
// m_lastUpdate = input;
|
|
|
|
// queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, input));
|
|
|
|
// }
|
|
|
|
// }
|
2015-08-28 00:11:31 +00:00
|
|
|
|
2022-02-09 08:54:53 +00:00
|
|
|
// /* Send straight keyboard input if no first controller present */
|
|
|
|
// if (!kbUsed) {
|
|
|
|
// m_lastUpdate = kbInput;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
void CInputGenerator::controllerAdded(uint32_t which) noexcept {
|
|
|
|
s32 player = aurora::get_controller_player_index(which);
|
|
|
|
if (player < 0) {
|
|
|
|
player = 0;
|
|
|
|
aurora::set_controller_player_index(which, 0);
|
|
|
|
}
|
|
|
|
|
2022-03-20 00:45:04 +00:00
|
|
|
if (m_state[player].m_hasRumble && m_state[player].m_isGamecube) {
|
|
|
|
/* The GameCube controller can get stuck in a state where it's always rumbling if the game crashes
|
|
|
|
* (this can actually happen on hardware in certain cases)
|
|
|
|
* so lets toggle the motors to ensure they're off, this happens so quickly the player doesn't notice
|
|
|
|
*/
|
|
|
|
aurora::controller_rumble(which, 1, 1);
|
|
|
|
aurora::controller_rumble(which, 0, 0);
|
|
|
|
}
|
2022-02-22 09:12:15 +00:00
|
|
|
m_state[player] =
|
|
|
|
SAuroraControllerState(which, aurora::is_controller_gamecube(which), aurora::controller_has_rumble(which));
|
2022-02-09 08:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CInputGenerator::controllerRemoved(uint32_t which) noexcept {
|
2022-02-22 05:53:57 +00:00
|
|
|
auto it = std::find_if(m_state.begin(), m_state.end(), [&which](const auto& s) { return s.m_which == which; });
|
2022-02-09 08:54:53 +00:00
|
|
|
if (it == m_state.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*it) = SAuroraControllerState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CInputGenerator::controllerButton(uint32_t which, aurora::ControllerButton button, bool pressed) noexcept {
|
|
|
|
s32 player = aurora::get_controller_player_index(which);
|
|
|
|
if (player < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_state[player].m_btns.set(size_t(button), pressed);
|
2015-08-29 01:30:47 +00:00
|
|
|
}
|
2015-08-28 00:11:31 +00:00
|
|
|
|
2022-02-08 19:18:12 +00:00
|
|
|
void CInputGenerator::controllerAxis(uint32_t which, aurora::ControllerAxis axis, int16_t value) noexcept {
|
2022-02-09 08:54:53 +00:00
|
|
|
s32 player = aurora::get_controller_player_index(which);
|
|
|
|
if (player < 0) {
|
2022-02-08 19:18:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (axis) {
|
|
|
|
case aurora::ControllerAxis::LeftY:
|
|
|
|
case aurora::ControllerAxis::RightY:
|
|
|
|
/* Value is inverted compared to what we expect on the Y axis */
|
2022-02-09 03:14:14 +00:00
|
|
|
value = int16_t(-(value + 1));
|
2022-02-08 19:18:12 +00:00
|
|
|
[[fallthrough]];
|
|
|
|
case aurora::ControllerAxis::LeftX:
|
|
|
|
case aurora::ControllerAxis::RightX:
|
2022-02-09 03:14:14 +00:00
|
|
|
value /= int16_t(256);
|
2022-02-08 19:18:12 +00:00
|
|
|
break;
|
|
|
|
case aurora::ControllerAxis::TriggerLeft:
|
|
|
|
case aurora::ControllerAxis::TriggerRight:
|
2022-02-09 03:14:14 +00:00
|
|
|
value /= int16_t(128);
|
2022-02-08 19:18:12 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-02-09 08:54:53 +00:00
|
|
|
m_state[player].m_axes[size_t(axis)] = value;
|
2022-02-08 19:18:12 +00:00
|
|
|
}
|
|
|
|
|
2022-02-22 09:12:15 +00:00
|
|
|
void CInputGenerator::SetMotorState(EIOPort port, EMotorState state) {
|
|
|
|
if (m_state[size_t(port)].m_hasRumble && m_state[size_t(port)].m_isGamecube) {
|
|
|
|
if (state == EMotorState::Rumble) {
|
|
|
|
aurora::controller_rumble(m_state[size_t(port)].m_which, 1, 1);
|
|
|
|
} else if (state == EMotorState::Stop) {
|
|
|
|
aurora::controller_rumble(m_state[size_t(port)].m_which, 0, 1);
|
|
|
|
} else if (state == EMotorState::StopHard) {
|
|
|
|
aurora::controller_rumble(m_state[size_t(port)].m_which, 0, 0);
|
|
|
|
}
|
|
|
|
} // TODO: Figure out good intensity values for generic controllers with rumble, support HAPTIC?
|
|
|
|
}
|
|
|
|
|
2022-02-09 03:14:14 +00:00
|
|
|
const CFinalInput& CInputGenerator::getFinalInput(unsigned int idx, float dt) {
|
|
|
|
auto input = CFinalInput(idx, dt, m_data, m_lastUpdate);
|
|
|
|
// Merge controller input with kb/m input
|
|
|
|
auto state = m_state[idx];
|
|
|
|
state.clamp();
|
|
|
|
input |= CFinalInput(idx, dt, state, m_lastUpdate, m_leftDiv, m_rightDiv);
|
|
|
|
m_lastUpdate = input;
|
|
|
|
return m_lastUpdate;
|
|
|
|
}
|
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
} // namespace metaforce
|