mirror of
https://github.com/blawar/ooot.git
synced 2024-07-02 09:03:36 +00:00
TAS support and input cleanup (#154)
* Cleaned up the input stuff. * Put all the input related classes into the namespace `hid` and did some further cleanup. * TAS support is now working. Generates `last-run.tas`. * Added *.tas to .gitignore. * Put `hid` into namespace `oot`. * Added a trivial form of compression for TAS files. Co-authored-by: DaMarkov <DaMarkovZED@gmail.com>
This commit is contained in:
parent
418dd1b650
commit
0dec26fce0
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,6 +27,7 @@ docs/doxygen/
|
|||
out.txt
|
||||
*.log
|
||||
*.sav
|
||||
*.tas
|
||||
|
||||
|
||||
# Tool artifacts
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include "tas.h"
|
||||
#include "../options.h"
|
||||
|
||||
#ifdef __SWITCH__
|
||||
|
@ -11,240 +12,269 @@
|
|||
#define TAS_DIR "tas"
|
||||
#endif
|
||||
|
||||
namespace sm64::hid
|
||||
using namespace oot::hid;
|
||||
|
||||
|
||||
N64Controller::State::State()
|
||||
{
|
||||
State::State()
|
||||
{
|
||||
mouse_x = 0;
|
||||
mouse_y = 0;
|
||||
has_mouse = false;
|
||||
mouse_x = 0;
|
||||
mouse_y = 0;
|
||||
has_mouse = false;
|
||||
|
||||
reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
void N64Controller::State::reset()
|
||||
{
|
||||
button = 0;
|
||||
stick_x = 0;
|
||||
stick_y = 0;
|
||||
errnum = 0;
|
||||
r_stick_x = 0;
|
||||
r_stick_y = 0;
|
||||
has_mouse = false;
|
||||
}
|
||||
|
||||
N64Controller::N64Controller(bool isLocal) : rawStickX(0), rawStickY(0), stickX(0), stickY(0), stickMag(0),
|
||||
buttonDown(0), buttonPressed(0), r_rawStickX(0), r_rawStickY(0), r_stickX(0), r_stickY(0), r_stickMag(0),
|
||||
m_isLocal(isLocal), m_state(), m_motorEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
N64Controller::~N64Controller()
|
||||
{
|
||||
if (m_tasFile)//Tas file is open
|
||||
fclose(m_tasFile);
|
||||
m_tasFile = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void N64Controller::merge(const N64Controller& controller)
|
||||
{
|
||||
m_state.button |= controller.m_state.button;
|
||||
|
||||
if (abs(m_state.stick_x) < abs(controller.m_state.stick_x))
|
||||
m_state.stick_x = controller.m_state.stick_x;
|
||||
|
||||
if (abs(m_state.stick_y) < abs(controller.m_state.stick_y))
|
||||
m_state.stick_y = controller.m_state.stick_y;
|
||||
|
||||
if (abs(m_state.r_stick_x) < abs(controller.m_state.r_stick_x))
|
||||
m_state.r_stick_x = controller.m_state.r_stick_x;
|
||||
|
||||
if (abs(m_state.r_stick_y) < abs(controller.m_state.r_stick_y))
|
||||
m_state.r_stick_y = controller.m_state.r_stick_y;
|
||||
|
||||
if (controller.hasMouse())
|
||||
{
|
||||
m_state.mouse_x = controller.m_state.mouse_x;
|
||||
m_state.mouse_y = controller.m_state.mouse_y;
|
||||
}
|
||||
|
||||
void State::reset()
|
||||
{
|
||||
button = 0;
|
||||
stick_x = 0;
|
||||
stick_y = 0;
|
||||
errnum = 0;
|
||||
r_stick_x = 0;
|
||||
r_stick_y = 0;
|
||||
has_mouse = false;
|
||||
}
|
||||
m_state.has_mouse |= controller.m_state.has_mouse;
|
||||
}
|
||||
|
||||
Controller::Controller(bool isLocal) :
|
||||
rawStickX(0), rawStickY(0), stickX(0), stickY(0), stickMag(0), buttonDown(0), buttonPressed(0), r_rawStickX(0), r_rawStickY(0), r_stickX(0), r_stickY(0), r_stickMag(0), m_isLocal(isLocal), m_state(), m_motorEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Controller::SendMotorEvent(short time, short level)
|
||||
{
|
||||
}
|
||||
|
||||
void Controller::SendMotorDecay(short level)
|
||||
{
|
||||
}
|
||||
static std::string getTasFileName()
|
||||
{
|
||||
return "last_run.tas";
|
||||
/*std::error_code error;
|
||||
std::filesystem::create_directory(TAS_DIR, error);
|
||||
|
||||
void Controller::ResetMotorPack()
|
||||
{
|
||||
}
|
||||
time_t now = time(0);
|
||||
tm* time = localtime(&now);
|
||||
|
||||
void Controller::SendMotorVib(int level)
|
||||
{
|
||||
}
|
||||
if (!time)
|
||||
return TAS_DIR"/record.tas";
|
||||
|
||||
void Controller::update()
|
||||
{
|
||||
}
|
||||
char buf[64] = { 0 };
|
||||
sprintf(buf, TAS_DIR"/%04d.%02d.%02d-%04d.tas", time->tm_year, time->tm_mon + 1, time->tm_mday, time->tm_hour * 60 + time->tm_min);
|
||||
return buf;*/
|
||||
}
|
||||
|
||||
void Controller::merge(const Controller& controller)
|
||||
{
|
||||
m_state.button |= controller.m_state.button;
|
||||
|
||||
if(abs(m_state.stick_x) < abs(controller.m_state.stick_x))
|
||||
static std::ofstream g_tas;
|
||||
|
||||
static std::ofstream& stream()
|
||||
{
|
||||
/*if (!g_tas)
|
||||
{
|
||||
auto name = getTasFileName();
|
||||
g_tas = std::ofstream(name, std::ofstream::binary);
|
||||
if (g_tas)
|
||||
g_tas.write((const char*)&oot::config(), sizeof(oot::config()));
|
||||
}*/
|
||||
|
||||
return g_tas;
|
||||
}
|
||||
|
||||
void N64Controller::resolveInputs()
|
||||
{
|
||||
#define SRAM_SIZE 0x8000//But this in ultra_reimplementation.h?
|
||||
|
||||
//Recording TAS?
|
||||
if (!Tas::isTasPlaying() && oot::config().game().recordTas())
|
||||
{
|
||||
//stream().write((const char*)&m_state, sizeof(m_state));
|
||||
|
||||
//Write TAS data
|
||||
|
||||
if (!m_tasFile)//Not open yet?
|
||||
{
|
||||
m_state.stick_x = controller.m_state.stick_x;
|
||||
}
|
||||
fopen_s(&m_tasFile, "last-run.tas", "wb");
|
||||
|
||||
if(abs(m_state.stick_y) < abs(controller.m_state.stick_y))
|
||||
{
|
||||
m_state.stick_y = controller.m_state.stick_y;
|
||||
}
|
||||
if (!m_tasFile)
|
||||
return;
|
||||
|
||||
if(abs(m_state.r_stick_x) < abs(controller.m_state.r_stick_x))
|
||||
{
|
||||
m_state.r_stick_x = controller.m_state.r_stick_x;
|
||||
}
|
||||
//Write our current config
|
||||
//fwrite(&oot::config(), sizeof(oot::config()), 1, m_tasOutput);
|
||||
|
||||
if(abs(m_state.r_stick_y) < abs(controller.m_state.r_stick_y))
|
||||
{
|
||||
m_state.r_stick_y = controller.m_state.r_stick_y;
|
||||
}
|
||||
|
||||
if(controller.hasMouse())
|
||||
{
|
||||
m_state.mouse_x = controller.m_state.mouse_x;
|
||||
m_state.mouse_y = controller.m_state.mouse_y;
|
||||
}
|
||||
|
||||
m_state.has_mouse |= controller.m_state.has_mouse;
|
||||
}
|
||||
|
||||
bool Controller::hasMouse() const
|
||||
{
|
||||
return m_state.has_mouse;
|
||||
}
|
||||
|
||||
static std::string getTasFileName()
|
||||
{
|
||||
|
||||
std::error_code error;
|
||||
std::filesystem::create_directory(TAS_DIR, error);
|
||||
|
||||
time_t now = time(0);
|
||||
tm* ltm = localtime(&now);
|
||||
|
||||
if (!ltm)
|
||||
{
|
||||
return TAS_DIR"/record.tas";
|
||||
}
|
||||
|
||||
char buf[64] = { 0 };
|
||||
sprintf(buf, TAS_DIR"/%04d.%02d.%02d-%04d.tas", ltm->tm_year, ltm->tm_mon + 1, ltm->tm_mday, ltm->tm_hour * 60 + ltm->tm_min);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TAS
|
||||
static std::ofstream* g_tas = nullptr;
|
||||
|
||||
static std::ofstream& stream()
|
||||
{
|
||||
if (!g_tas)
|
||||
{
|
||||
auto name = getTasFileName();
|
||||
g_tas = new std::ofstream(name, std::ifstream::binary);
|
||||
g_tas->write((const char*)&config(), sizeof(config()));
|
||||
}
|
||||
|
||||
return *g_tas;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Controller::resolveInputs()
|
||||
{
|
||||
#ifdef ENABLE_TAS
|
||||
if (!hid::isTasPlaying() && config().game().recordTas())
|
||||
{
|
||||
stream().write((const char*)&m_state, sizeof(m_state));
|
||||
|
||||
if (m_state.button)
|
||||
//Write save data
|
||||
uint8_t* sram = new uint8_t[SRAM_SIZE];
|
||||
FILE* save = nullptr;
|
||||
fopen_s(&save, "oot.sav", "wb");
|
||||
if (save)
|
||||
{
|
||||
int y = 0;
|
||||
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, save);
|
||||
fclose(save);
|
||||
}
|
||||
|
||||
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, m_tasFile);
|
||||
delete[] sram;
|
||||
}
|
||||
|
||||
if (m_tasFile)//Tas file is open
|
||||
{
|
||||
//fwrite(&m_state, sizeof(m_state), 1, m_tasFile);
|
||||
StateCompressed compressed(m_state);//Compress the state
|
||||
compressed.write(m_tasFile);//Write to TAS file
|
||||
#ifdef _DEBUG
|
||||
fflush(m_tasFile);//Flush every frame to keep all the data when the game crashes
|
||||
#endif
|
||||
|
||||
rawStickX = m_state.stick_x;
|
||||
rawStickY = m_state.stick_y;
|
||||
r_rawStickX = m_state.r_stick_x;
|
||||
r_rawStickY = m_state.r_stick_y;
|
||||
buttonPressed = m_state.button & (m_state.button ^ buttonDown);
|
||||
// 0.5x A presses are a good meme
|
||||
buttonDown = m_state.button;
|
||||
|
||||
if(oot::config().game().mirror())
|
||||
{
|
||||
rawStickX = -rawStickX;
|
||||
r_rawStickX = -r_rawStickX;
|
||||
}
|
||||
|
||||
// reset the controller's x and y floats.
|
||||
this->stickX = 0;
|
||||
this->stickY = 0;
|
||||
|
||||
// modulate the rawStickX and rawStickY to be the new f32 values by adding/subtracting 6.
|
||||
if(this->rawStickX <= -8)
|
||||
{
|
||||
this->stickX = this->rawStickX + 6;
|
||||
}
|
||||
|
||||
if(this->rawStickX >= 8)
|
||||
{
|
||||
this->stickX = this->rawStickX - 6;
|
||||
}
|
||||
|
||||
if(this->rawStickY <= -8)
|
||||
{
|
||||
this->stickY = this->rawStickY + 6;
|
||||
}
|
||||
|
||||
if(this->rawStickY >= 8)
|
||||
{
|
||||
this->stickY = this->rawStickY - 6;
|
||||
}
|
||||
|
||||
// calculate f32 magnitude from the center by vector length.
|
||||
this->stickMag = sqrtf(this->stickX * this->stickX + this->stickY * this->stickY);
|
||||
|
||||
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
|
||||
// flatten the values down to the allowed maximum value.
|
||||
if(this->stickMag > 64)
|
||||
{
|
||||
this->stickX *= 64 / this->stickMag;
|
||||
this->stickY *= 64 / this->stickMag;
|
||||
this->stickMag = 64;
|
||||
}
|
||||
|
||||
this->r_stickX = 0;
|
||||
this->r_stickY = 0;
|
||||
|
||||
if(this->r_rawStickX <= -8)
|
||||
{
|
||||
this->r_stickX = this->r_rawStickX + 6;
|
||||
}
|
||||
|
||||
if(this->r_rawStickX >= 8)
|
||||
{
|
||||
this->r_stickX = this->r_rawStickX - 6;
|
||||
}
|
||||
|
||||
if(this->r_rawStickY <= -8)
|
||||
{
|
||||
this->r_stickY = this->r_rawStickY + 6;
|
||||
}
|
||||
|
||||
if(this->r_rawStickY >= 8)
|
||||
{
|
||||
this->r_stickY = this->r_rawStickY - 6;
|
||||
}
|
||||
|
||||
// calculate f32 magnitude from the center by vector length.
|
||||
this->r_stickMag = sqrtf(this->r_stickX * this->r_stickX + this->r_stickY * this->r_stickY);
|
||||
|
||||
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
|
||||
// flatten the values down to the allowed maximum value.
|
||||
if(this->r_stickMag > 64)
|
||||
{
|
||||
this->r_stickX *= 64 / this->r_stickMag;
|
||||
this->r_stickY *= 64 / this->r_stickMag;
|
||||
this->r_stickMag = 64;
|
||||
}
|
||||
}
|
||||
|
||||
s64 Controller::mouse_x() const
|
||||
//Playing back a TAS?
|
||||
else if (Tas::isTasPlaying())
|
||||
{
|
||||
return m_state.mouse_x * (oot::config().camera().mousexInvert() ? -1 : 1) * oot::config().camera().mousexScaler();
|
||||
if (!m_tasFile)
|
||||
{
|
||||
m_tasFile = fopen("last-run.tas", "rb");
|
||||
|
||||
if (!m_tasFile)
|
||||
return;
|
||||
|
||||
//fread(&oot::config(), 1, sizeof(oot::config()), fp);
|
||||
|
||||
uint8_t* sram = new uint8_t[SRAM_SIZE];
|
||||
fread(sram, sizeof(uint8_t), SRAM_SIZE, m_tasFile);
|
||||
|
||||
FILE* save = nullptr;
|
||||
fopen_s(&save, "oot.sav", "wb");
|
||||
if (save)
|
||||
{
|
||||
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, save);
|
||||
fclose(save);
|
||||
}
|
||||
|
||||
delete[] sram;
|
||||
}
|
||||
|
||||
if (m_tasFile)
|
||||
{
|
||||
//fread(&m_state, sizeof(m_state), 1, m_tasFile);//Uncompressed read
|
||||
StateCompressed compressed;
|
||||
compressed.read(m_tasFile);//Read the compressed state
|
||||
m_state = compressed;//Uncompress
|
||||
}
|
||||
}
|
||||
|
||||
s64 Controller::mouse_y() const
|
||||
rawStickX = m_state.stick_x;
|
||||
rawStickY = m_state.stick_y;
|
||||
r_rawStickX = m_state.r_stick_x;
|
||||
r_rawStickY = m_state.r_stick_y;
|
||||
buttonPressed = m_state.button & (m_state.button ^ buttonDown);
|
||||
|
||||
buttonDown = m_state.button;
|
||||
|
||||
if (oot::config().game().mirror())
|
||||
{
|
||||
return m_state.mouse_y * (oot::config().camera().mouseyInvert() ? -1 : 1) * oot::config().camera().mouseyScaler();
|
||||
rawStickX = -rawStickX;
|
||||
r_rawStickX = -r_rawStickX;
|
||||
}
|
||||
|
||||
bool Controller::updateRebind(int input)
|
||||
// reset the controller's x and y floats.
|
||||
this->stickX = 0;
|
||||
this->stickY = 0;
|
||||
|
||||
// modulate the rawStickX and rawStickY to be the new f32 values by adding/subtracting 6.
|
||||
if (this->rawStickX <= -8)
|
||||
this->stickX = this->rawStickX + 6;
|
||||
|
||||
if (this->rawStickX >= 8)
|
||||
this->stickX = this->rawStickX - 6;
|
||||
|
||||
if (this->rawStickY <= -8)
|
||||
this->stickY = this->rawStickY + 6;
|
||||
|
||||
if (this->rawStickY >= 8)
|
||||
this->stickY = this->rawStickY - 6;
|
||||
|
||||
// calculate f32 magnitude from the center by vector length.
|
||||
this->stickMag = sqrtf(this->stickX * this->stickX + this->stickY * this->stickY);
|
||||
|
||||
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
|
||||
// flatten the values down to the allowed maximum value.
|
||||
if (this->stickMag > 64)
|
||||
{
|
||||
return false;
|
||||
this->stickX *= 64 / this->stickMag;
|
||||
this->stickY *= 64 / this->stickMag;
|
||||
this->stickMag = 64;
|
||||
}
|
||||
|
||||
} // namespace sm64::hid
|
||||
this->r_stickX = 0;
|
||||
this->r_stickY = 0;
|
||||
|
||||
if (this->r_rawStickX <= -8)
|
||||
this->r_stickX = this->r_rawStickX + 6;
|
||||
|
||||
if (this->r_rawStickX >= 8)
|
||||
this->r_stickX = this->r_rawStickX - 6;
|
||||
|
||||
if (this->r_rawStickY <= -8)
|
||||
this->r_stickY = this->r_rawStickY + 6;
|
||||
|
||||
if (this->r_rawStickY >= 8)
|
||||
this->r_stickY = this->r_rawStickY - 6;
|
||||
|
||||
// calculate f32 magnitude from the center by vector length.
|
||||
this->r_stickMag = sqrtf(this->r_stickX * this->r_stickX + this->r_stickY * this->r_stickY);
|
||||
|
||||
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
|
||||
// flatten the values down to the allowed maximum value.
|
||||
if (this->r_stickMag > 64)
|
||||
{
|
||||
this->r_stickX *= 64 / this->r_stickMag;
|
||||
this->r_stickY *= 64 / this->r_stickMag;
|
||||
this->r_stickMag = 64;
|
||||
}
|
||||
}
|
||||
|
||||
s64 N64Controller::mouse_x() const
|
||||
{
|
||||
return m_state.mouse_x * (oot::config().camera().mousexInvert() ? -1 : 1) * oot::config().camera().mousexScaler();
|
||||
}
|
||||
|
||||
s64 N64Controller::mouse_y() const
|
||||
{
|
||||
return m_state.mouse_y * (oot::config().camera().mouseyInvert() ? -1 : 1) * oot::config().camera().mouseyScaler();
|
||||
}
|
||||
|
||||
/*bool N64Controller::updateRebind(int input)
|
||||
{
|
||||
return false;
|
||||
}*/
|
|
@ -1,111 +1,130 @@
|
|||
#pragma once
|
||||
#include "ultra64/types.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace sm64::hid
|
||||
|
||||
|
||||
namespace oot
|
||||
{
|
||||
enum Button
|
||||
namespace hid
|
||||
{
|
||||
CONT_A = 0x8000,
|
||||
CONT_B = 0x4000,
|
||||
CONT_G = 0x2000,
|
||||
CONT_START = 0x1000,
|
||||
CONT_UP = 0x0800,
|
||||
CONT_DOWN = 0x0400,
|
||||
CONT_LEFT = 0x0200,
|
||||
CONT_RIGHT = 0x0100,
|
||||
CONT_L = 0x0020,
|
||||
CONT_R = 0x0010,
|
||||
CONT_E = 0x0008,
|
||||
CONT_D = 0x0004,
|
||||
CONT_C = 0x0002,
|
||||
CONT_F = 0x0001,
|
||||
|
||||
A_BUTTON = CONT_A,
|
||||
B_BUTTON = CONT_B,
|
||||
L_TRIG = CONT_L,
|
||||
R_TRIG = CONT_R,
|
||||
Z_TRIG = CONT_G,
|
||||
START_BUTTON = CONT_START,
|
||||
U_JPAD = CONT_UP,
|
||||
L_JPAD = CONT_LEFT,
|
||||
R_JPAD = CONT_RIGHT,
|
||||
D_JPAD = CONT_DOWN,
|
||||
U_CBUTTONS = CONT_E,
|
||||
L_CBUTTONS = CONT_C,
|
||||
R_CBUTTONS = CONT_F,
|
||||
D_CBUTTONS = CONT_D,
|
||||
STICK_X_LEFT = 0x10000,
|
||||
STICK_X_RIGHT = 0x20000,
|
||||
STICK_X_DOWN = 0x80000,
|
||||
STICK_X_UP = 0x40000,
|
||||
WALK_BUTTON = 0x100000
|
||||
};
|
||||
|
||||
class State
|
||||
{
|
||||
public:
|
||||
State();
|
||||
void reset();
|
||||
|
||||
u16 button;
|
||||
s8 stick_x; /* -80 <= stick_x <= 80 */
|
||||
s8 stick_y; /* -80 <= stick_y <= 80 */
|
||||
u8 errnum;
|
||||
s8 r_stick_x; /* -80 <= stick_x <= 80 */
|
||||
s8 r_stick_y; /* -80 <= stick_y <= 80 */
|
||||
|
||||
s64 mouse_x;
|
||||
s64 mouse_y;
|
||||
bool has_mouse;
|
||||
};
|
||||
|
||||
class Controller
|
||||
{
|
||||
public:
|
||||
s16 rawStickX;
|
||||
s16 rawStickY;
|
||||
float stickX; // [-64, 64] positive is right
|
||||
float stickY; // [-64, 64] positive is up
|
||||
float stickMag; // distance from center [0, 64]
|
||||
u16 buttonDown;
|
||||
u16 buttonPressed;
|
||||
s16 r_rawStickX; //
|
||||
s16 r_rawStickY; //
|
||||
float r_stickX; // [-64, 64] positive is right
|
||||
float r_stickY; // [-64, 64] positive is up
|
||||
float r_stickMag; // distance from center [0, 64]
|
||||
|
||||
|
||||
s64 mouse_x() const;
|
||||
s64 mouse_y() const;
|
||||
|
||||
Controller(bool isLocal = true);
|
||||
virtual void update();
|
||||
virtual void resolveInputs();
|
||||
virtual bool isLocal() const
|
||||
class N64Controller
|
||||
{
|
||||
return m_isLocal;
|
||||
}
|
||||
State& state()
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
public:
|
||||
class State
|
||||
{
|
||||
public:
|
||||
State();
|
||||
void reset();
|
||||
|
||||
virtual void merge(const Controller& controller);
|
||||
virtual bool hasMouse() const;
|
||||
u16 button;
|
||||
s8 stick_x; /* -80 <= stick_x <= 80 */
|
||||
s8 stick_y; /* -80 <= stick_y <= 80 */
|
||||
u8 errnum;
|
||||
s8 r_stick_x; /* -80 <= stick_x <= 80 */
|
||||
s8 r_stick_y; /* -80 <= stick_y <= 80 */
|
||||
|
||||
virtual void SendMotorEvent(short time, short level);
|
||||
virtual void SendMotorDecay(short level);
|
||||
virtual void ResetMotorPack();
|
||||
virtual void SendMotorVib(int level);
|
||||
s64 mouse_x;
|
||||
s64 mouse_y;
|
||||
bool has_mouse;
|
||||
};
|
||||
|
||||
virtual bool updateRebind(int input);
|
||||
class StateCompressed
|
||||
{
|
||||
public:
|
||||
StateCompressed() = default;
|
||||
StateCompressed(State state)
|
||||
{
|
||||
m_data.button = state.button;
|
||||
m_data.stick_x = state.stick_x;
|
||||
m_data.stick_y = state.stick_y;
|
||||
|
||||
//Is there any real data?
|
||||
m_isUsed = m_data.button || m_data.stick_x || m_data.stick_y;
|
||||
}
|
||||
|
||||
operator State ()
|
||||
{
|
||||
State ret;
|
||||
ret.button = m_data.button;
|
||||
ret.stick_x = m_data.stick_x;
|
||||
ret.stick_y = m_data.stick_y;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void read(FILE* in) {
|
||||
fread(&m_isUsed, sizeof(u8), 1, in);//Read the flag
|
||||
|
||||
if (m_isUsed)//Is there even any input?
|
||||
fread(&m_data, sizeof(InputFrame), 1, in);//Write the full state
|
||||
}
|
||||
|
||||
void write(FILE* out) {
|
||||
fwrite(&m_isUsed, sizeof(u8), 1, out);//Write if there is data
|
||||
|
||||
if (m_isUsed)//Is there even any input?
|
||||
fwrite(&m_data, sizeof(InputFrame), 1, out);//Write the full state
|
||||
}
|
||||
|
||||
private:
|
||||
u8 m_isUsed = 0;//0 when all input is zero
|
||||
struct InputFrame {
|
||||
u16 button = 0x0000;
|
||||
s8 stick_x = 0; /* -80 <= stick_x <= 80 */
|
||||
s8 stick_y = 0; /* -80 <= stick_y <= 80 */
|
||||
} m_data;
|
||||
};
|
||||
|
||||
|
||||
s16 rawStickX;
|
||||
s16 rawStickY;
|
||||
float stickX; // [-64, 64] positive is right
|
||||
float stickY; // [-64, 64] positive is up
|
||||
float stickMag; // distance from center [0, 64]
|
||||
u16 buttonDown;
|
||||
u16 buttonPressed;
|
||||
s16 r_rawStickX; //
|
||||
s16 r_rawStickY; //
|
||||
float r_stickX; // [-64, 64] positive is right
|
||||
float r_stickY; // [-64, 64] positive is up
|
||||
float r_stickMag; // distance from center [0, 64]
|
||||
|
||||
|
||||
s64 mouse_x() const;
|
||||
s64 mouse_y() const;
|
||||
|
||||
N64Controller(bool isLocal = true);
|
||||
~N64Controller();
|
||||
|
||||
virtual void update() {}
|
||||
virtual void resolveInputs();
|
||||
virtual bool isLocal() const {
|
||||
return m_isLocal;
|
||||
}
|
||||
State& state() {
|
||||
return m_state;
|
||||
}
|
||||
const State state() const {
|
||||
return m_state;
|
||||
}
|
||||
|
||||
virtual void merge(const N64Controller& controller);
|
||||
virtual bool hasMouse() const { return m_state.has_mouse; }
|
||||
|
||||
virtual void SendMotorEvent(short time, short level) {}
|
||||
virtual void SendMotorDecay(short level) {}
|
||||
virtual void ResetMotorPack() {}
|
||||
virtual void SendMotorVib(int level) {}
|
||||
|
||||
//virtual bool updateRebind(Button input);
|
||||
|
||||
protected:
|
||||
State m_state;
|
||||
bool m_isLocal;
|
||||
bool m_motorEnabled;
|
||||
};
|
||||
State m_state;
|
||||
bool m_isLocal;
|
||||
bool m_motorEnabled;
|
||||
|
||||
bool isTasPlaying();
|
||||
} // namespace sm64::hid
|
||||
private:
|
||||
FILE* m_tasFile = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -5,138 +5,107 @@
|
|||
#include <stdexcept>
|
||||
#include "../options.h"
|
||||
|
||||
namespace sm64::hid
|
||||
|
||||
using namespace oot::hid;
|
||||
|
||||
static InputDeviceManager* g_deviceManager = nullptr;
|
||||
|
||||
|
||||
|
||||
InputDeviceManager& InputDeviceManager::get()
|
||||
{
|
||||
static Controllers* g_controllers;
|
||||
if (!g_deviceManager)
|
||||
g_deviceManager = new InputDeviceManager();
|
||||
return *g_deviceManager;
|
||||
}
|
||||
|
||||
Controllers& controllers()
|
||||
|
||||
|
||||
N64Controller& InputDevice::controller(const u64 index)
|
||||
{
|
||||
if (index >= m_controllers.size())
|
||||
throw std::runtime_error("invalid controller index");
|
||||
return *m_controllers[index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool InputDevice::updateRebind(Button input)
|
||||
{
|
||||
bool result = 0;
|
||||
for (auto& controller : m_controllers)
|
||||
{
|
||||
if(!g_controllers)
|
||||
{
|
||||
g_controllers = new Controllers();
|
||||
}
|
||||
return *g_controllers;
|
||||
controller->state().reset();
|
||||
//result |= controller->updateRebind(input);
|
||||
controller->resolveInputs();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Driver::Driver()
|
||||
|
||||
void InputDevice::update()
|
||||
{
|
||||
for(auto& controller : m_controllers)
|
||||
{
|
||||
controller->state().reset();
|
||||
controller->update();
|
||||
controller->resolveInputs();
|
||||
}
|
||||
}
|
||||
|
||||
Driver::~Driver()
|
||||
{
|
||||
}
|
||||
|
||||
const u64 Driver::size() const
|
||||
{
|
||||
return m_controllers.size();
|
||||
}
|
||||
|
||||
Controller& Driver::controller(const u64 index)
|
||||
{
|
||||
if(index >= m_controllers.size())
|
||||
{
|
||||
throw std::runtime_error("invalid controller index");
|
||||
}
|
||||
return *m_controllers[index];
|
||||
}
|
||||
|
||||
bool Driver::updateRebind(int input)
|
||||
{
|
||||
bool result = 0;
|
||||
for (auto& controller : m_controllers)
|
||||
{
|
||||
controller->state().reset();
|
||||
result |= controller->updateRebind(input);
|
||||
controller->resolveInputs();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Driver::update()
|
||||
{
|
||||
for(auto& controller : m_controllers)
|
||||
{
|
||||
controller->state().reset();
|
||||
controller->update();
|
||||
controller->resolveInputs();
|
||||
}
|
||||
}
|
||||
|
||||
void Driver::scan(class Controllers* controllers)
|
||||
{
|
||||
}
|
||||
|
||||
Controllers::Controllers() : m_rebindInput(0)
|
||||
{
|
||||
m_drivers.push_back(new SDL());
|
||||
InputDeviceManager::InputDeviceManager() : m_rebindInput(Button::NONE)
|
||||
{
|
||||
m_drivers.push_back(new Joypad());
|
||||
|
||||
#ifdef ENABLE_MOUSE
|
||||
m_drivers.push_back(new Keyboard());
|
||||
m_drivers.push_back(new Keyboard());
|
||||
#endif
|
||||
//if (Tas::isTasPlaying())
|
||||
//m_drivers.push_back(new Tas());
|
||||
}
|
||||
}
|
||||
|
||||
Controllers::~Controllers()
|
||||
|
||||
|
||||
const u64 InputDeviceManager::size() const
|
||||
{
|
||||
u64 count = 0;
|
||||
|
||||
for (auto& driver : m_drivers)
|
||||
count += driver->size();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void InputDeviceManager::update()
|
||||
{
|
||||
if (isRebindMode())
|
||||
{
|
||||
}
|
||||
bool result = 0;
|
||||
|
||||
const u64 Controllers::size() const
|
||||
for (auto& driver : m_drivers)
|
||||
result |= driver->updateRebind(m_rebindInput);
|
||||
|
||||
if (result)
|
||||
m_rebindInput = Button::NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 count = 0;
|
||||
|
||||
for(auto& driver : m_drivers)
|
||||
{
|
||||
count += driver->size();
|
||||
}
|
||||
|
||||
return count;
|
||||
for (auto& driver : m_drivers)
|
||||
driver->update();
|
||||
}
|
||||
}
|
||||
|
||||
void Controllers::update()
|
||||
|
||||
|
||||
void InputDeviceManager::scan()
|
||||
{
|
||||
for (auto& driver : m_drivers)
|
||||
{
|
||||
if (isRebindMode())
|
||||
{
|
||||
bool result = 0;
|
||||
|
||||
for (auto& driver : m_drivers)
|
||||
{
|
||||
result |= driver->updateRebind(m_rebindInput);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
m_rebindInput = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& driver : m_drivers)
|
||||
{
|
||||
driver->update();
|
||||
}
|
||||
}
|
||||
//if(!driver->defaultOnly() || !found)
|
||||
driver->scan();
|
||||
}
|
||||
|
||||
void Controllers::scan()
|
||||
{
|
||||
u64 found = 0;
|
||||
for(auto& driver : m_drivers)
|
||||
{
|
||||
//if(!driver->defaultOnly() || !found)
|
||||
{
|
||||
driver->scan(this);
|
||||
found += driver->size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controllers::rebind(int input)
|
||||
{
|
||||
m_rebindInput = input;
|
||||
}
|
||||
|
||||
bool Controllers::isRebindMode() const
|
||||
{
|
||||
return m_rebindInput > 0;
|
||||
}
|
||||
} // namespace sm64::hid
|
||||
}
|
|
@ -1,55 +1,97 @@
|
|||
#pragma once
|
||||
|
||||
#include "ultra64/types.h"
|
||||
#include "controller.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace sm64::hid
|
||||
|
||||
|
||||
namespace oot
|
||||
{
|
||||
class Driver
|
||||
namespace hid
|
||||
{
|
||||
public:
|
||||
Driver();
|
||||
virtual ~Driver();
|
||||
virtual const u64 size() const;
|
||||
virtual Controller& controller(const u64 index);
|
||||
virtual void update();
|
||||
virtual bool updateRebind(int input);
|
||||
virtual void scan(class Controllers* controllers);
|
||||
virtual bool defaultOnly()
|
||||
enum class Button : uint32_t
|
||||
{
|
||||
return false;
|
||||
}
|
||||
NONE = 0x0000,
|
||||
CONT_A = 0x8000,
|
||||
CONT_B = 0x4000,
|
||||
CONT_G = 0x2000,
|
||||
CONT_START = 0x1000,
|
||||
CONT_UP = 0x0800,
|
||||
CONT_DOWN = 0x0400,
|
||||
CONT_LEFT = 0x0200,
|
||||
CONT_RIGHT = 0x0100,
|
||||
CONT_L = 0x0020,
|
||||
CONT_R = 0x0010,
|
||||
CONT_E = 0x0008,
|
||||
CONT_D = 0x0004,
|
||||
CONT_C = 0x0002,
|
||||
CONT_F = 0x0001,
|
||||
|
||||
std::vector<std::shared_ptr<Controller>>& controllers()
|
||||
A_BUTTON = CONT_A,
|
||||
B_BUTTON = CONT_B,
|
||||
L_TRIG = CONT_L,
|
||||
R_TRIG = CONT_R,
|
||||
Z_TRIG = CONT_G,
|
||||
START_BUTTON = CONT_START,
|
||||
U_JPAD = CONT_UP,
|
||||
L_JPAD = CONT_LEFT,
|
||||
R_JPAD = CONT_RIGHT,
|
||||
D_JPAD = CONT_DOWN,
|
||||
U_CBUTTONS = CONT_E,
|
||||
L_CBUTTONS = CONT_C,
|
||||
R_CBUTTONS = CONT_F,
|
||||
D_CBUTTONS = CONT_D,
|
||||
STICK_X_LEFT = 0x10000,
|
||||
STICK_X_RIGHT = 0x20000,
|
||||
STICK_X_DOWN = 0x80000,
|
||||
STICK_X_UP = 0x40000,
|
||||
WALK_BUTTON = 0x100000
|
||||
};
|
||||
|
||||
|
||||
class InputDevice;
|
||||
|
||||
|
||||
class InputDeviceManager
|
||||
{
|
||||
return m_controllers;
|
||||
}
|
||||
public:
|
||||
static InputDeviceManager& get();
|
||||
|
||||
protected:
|
||||
std::vector<std::shared_ptr<Controller>> m_controllers;
|
||||
};
|
||||
InputDeviceManager();
|
||||
virtual ~InputDeviceManager() {}
|
||||
const u64 size() const;
|
||||
void update();
|
||||
void scan();
|
||||
bool isRebindMode() const { return m_rebindInput != Button::NONE; }
|
||||
void rebind(Button input) { m_rebindInput = input; }
|
||||
std::vector<InputDevice*>& drivers() { return m_drivers; }
|
||||
|
||||
class Controllers
|
||||
{
|
||||
public:
|
||||
Controllers();
|
||||
virtual ~Controllers();
|
||||
const u64 size() const;
|
||||
void update();
|
||||
void scan();
|
||||
bool isRebindMode() const;
|
||||
void rebind(int input);
|
||||
std::vector<class Driver*>& drivers()
|
||||
protected:
|
||||
std::vector<InputDevice*> m_drivers;
|
||||
Button m_rebindInput;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class InputDevice
|
||||
{
|
||||
return m_drivers;
|
||||
}
|
||||
public:
|
||||
InputDevice() = default;
|
||||
virtual ~InputDevice() {}
|
||||
|
||||
protected:
|
||||
std::vector<class Driver*> m_drivers;
|
||||
int m_rebindInput;
|
||||
};
|
||||
virtual void scan() = 0;
|
||||
|
||||
Controllers& controllers();
|
||||
} // namespace sm64::hid
|
||||
virtual const u64 size() const { return m_controllers.size(); }
|
||||
virtual N64Controller& controller(const u64 index);
|
||||
virtual void update();
|
||||
virtual bool updateRebind(Button input);
|
||||
virtual bool defaultOnly() { return false; }
|
||||
|
||||
std::vector<std::shared_ptr<N64Controller>>& controllers() { return m_controllers; }
|
||||
|
||||
protected:
|
||||
std::vector<std::shared_ptr<N64Controller>> m_controllers;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -15,11 +15,16 @@
|
|||
#endif
|
||||
#include "../player/players.h"
|
||||
#include "../options.h"
|
||||
#include "tas.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void set_fullscreen(bool value);
|
||||
}
|
||||
|
||||
|
||||
using namespace oot::hid;
|
||||
|
||||
u8 Get_Language();
|
||||
void Set_Language(u8 language_id);
|
||||
|
||||
|
@ -41,399 +46,347 @@ bool saveJson(rapidjson::Document& doc, const std::string& jsonFilePath)
|
|||
}
|
||||
#endif
|
||||
|
||||
namespace sm64::hid
|
||||
|
||||
const char* Keyboard::getInputName(Button input)
|
||||
{
|
||||
namespace controller
|
||||
switch (input)
|
||||
{
|
||||
const char* getInputName(int input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case STICK_X_UP: return "STICK_X_UP";
|
||||
case STICK_X_LEFT: return "STICK_X_LEFT";
|
||||
case STICK_X_DOWN: return "STICK_X_DOWN";
|
||||
case STICK_X_RIGHT: return "STICK_X_RIGHT";
|
||||
case A_BUTTON: return "A_BUTTON";
|
||||
case B_BUTTON: return "B_BUTTON";
|
||||
case Z_TRIG: return "Z_TRIG";
|
||||
case U_CBUTTONS: return "U_CBUTTONS";
|
||||
case L_CBUTTONS: return "L_CBUTTONS";
|
||||
case D_CBUTTONS: return "D_CBUTTONS";
|
||||
case R_CBUTTONS: return "R_CBUTTONS";
|
||||
case R_TRIG: return "R_TRIG";
|
||||
case START_BUTTON: return "START_BUTTON";
|
||||
case WALK_BUTTON: return "WALK_BUTTON";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
case Button::STICK_X_UP: return "STICK_X_UP";
|
||||
case Button::STICK_X_LEFT: return "STICK_X_LEFT";
|
||||
case Button::STICK_X_DOWN: return "STICK_X_DOWN";
|
||||
case Button::STICK_X_RIGHT: return "STICK_X_RIGHT";
|
||||
case Button::A_BUTTON: return "A_BUTTON";
|
||||
case Button::B_BUTTON: return "B_BUTTON";
|
||||
case Button::Z_TRIG: return "Z_TRIG";
|
||||
case Button::U_CBUTTONS: return "U_CBUTTONS";
|
||||
case Button::L_CBUTTONS: return "L_CBUTTONS";
|
||||
case Button::D_CBUTTONS: return "D_CBUTTONS";
|
||||
case Button::R_CBUTTONS: return "R_CBUTTONS";
|
||||
case Button::R_TRIG: return "R_TRIG";
|
||||
case Button::START_BUTTON: return "START_BUTTON";
|
||||
case Button::WALK_BUTTON: return "WALK_BUTTON";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
int getInputValue(const std::string& input)
|
||||
{
|
||||
if (input == "STICK_X_UP") return STICK_X_UP;
|
||||
if (input == "STICK_X_LEFT") return STICK_X_LEFT;
|
||||
if (input == "STICK_X_DOWN") return STICK_X_DOWN;
|
||||
if (input == "STICK_X_RIGHT") return STICK_X_RIGHT;
|
||||
if (input == "A_BUTTON") return A_BUTTON;
|
||||
if (input == "B_BUTTON") return B_BUTTON;
|
||||
if (input == "Z_TRIG") return Z_TRIG;
|
||||
if (input == "U_CBUTTONS") return U_CBUTTONS;
|
||||
if (input == "L_CBUTTONS") return L_CBUTTONS;
|
||||
if (input == "D_CBUTTONS") return D_CBUTTONS;
|
||||
if (input == "R_CBUTTONS") return R_CBUTTONS;
|
||||
if (input == "R_TRIG") return R_TRIG;
|
||||
if (input == "START_BUTTON") return START_BUTTON;
|
||||
if (input == "WALK_BUTTON") return WALK_BUTTON;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class Keyboard : public Controller
|
||||
{
|
||||
public:
|
||||
Keyboard() : Controller()
|
||||
{
|
||||
memset(m_lastKeyState, 0, sizeof(m_lastKeyState));
|
||||
m_keyBindings[SDL_SCANCODE_W] = STICK_X_UP;
|
||||
m_keyBindings[SDL_SCANCODE_A] = STICK_X_LEFT;
|
||||
m_keyBindings[SDL_SCANCODE_S] = STICK_X_DOWN;
|
||||
m_keyBindings[SDL_SCANCODE_D] = STICK_X_RIGHT;
|
||||
m_keyBindings[SDL_SCANCODE_SPACE] = A_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_F] = B_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_O] = A_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_P] = B_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_LSHIFT] = Z_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_C] = Z_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_I] = U_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_J] = L_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_K] = D_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_L] = R_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_UP] = U_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_LEFT] = L_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_DOWN] = D_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_RIGHT] = R_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_X] = L_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_V] = R_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_RSHIFT] = R_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_RETURN] = START_BUTTON;
|
||||
Button Keyboard::getInputValue(const std::string& input)
|
||||
{
|
||||
if (input == "STICK_X_UP") return Button::STICK_X_UP;
|
||||
if (input == "STICK_X_LEFT") return Button::STICK_X_LEFT;
|
||||
if (input == "STICK_X_DOWN") return Button::STICK_X_DOWN;
|
||||
if (input == "STICK_X_RIGHT") return Button::STICK_X_RIGHT;
|
||||
if (input == "A_BUTTON") return Button::A_BUTTON;
|
||||
if (input == "B_BUTTON") return Button::B_BUTTON;
|
||||
if (input == "Z_TRIG") return Button::Z_TRIG;
|
||||
if (input == "U_CBUTTONS") return Button::U_CBUTTONS;
|
||||
if (input == "L_CBUTTONS") return Button::L_CBUTTONS;
|
||||
if (input == "D_CBUTTONS") return Button::D_CBUTTONS;
|
||||
if (input == "R_CBUTTONS") return Button::R_CBUTTONS;
|
||||
if (input == "R_TRIG") return Button::R_TRIG;
|
||||
if (input == "START_BUTTON") return Button::START_BUTTON;
|
||||
if (input == "WALK_BUTTON") return Button::WALK_BUTTON;
|
||||
|
||||
return (Button)0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Keyboard::Keyboard() : N64Controller()
|
||||
{
|
||||
memset(m_lastKeyState, 0, sizeof(m_lastKeyState));
|
||||
m_keyBindings[SDL_SCANCODE_W] = Button::STICK_X_UP;
|
||||
m_keyBindings[SDL_SCANCODE_A] = Button::STICK_X_LEFT;
|
||||
m_keyBindings[SDL_SCANCODE_S] = Button::STICK_X_DOWN;
|
||||
m_keyBindings[SDL_SCANCODE_D] = Button::STICK_X_RIGHT;
|
||||
m_keyBindings[SDL_SCANCODE_SPACE] = Button::A_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_F] = Button::B_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_O] = Button::A_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_P] = Button::B_BUTTON;
|
||||
m_keyBindings[SDL_SCANCODE_LSHIFT] = Button::Z_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_C] = Button::Z_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_I] = Button::U_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_J] = Button::L_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_K] = Button::D_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_L] = Button::R_JPAD;
|
||||
m_keyBindings[SDL_SCANCODE_UP] = Button::U_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_LEFT] = Button::L_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_DOWN] = Button::D_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_RIGHT] = Button::R_CBUTTONS;
|
||||
m_keyBindings[SDL_SCANCODE_X] = Button::L_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_V] = Button::R_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_RSHIFT] = Button::R_TRIG;
|
||||
m_keyBindings[SDL_SCANCODE_RETURN] = Button::START_BUTTON;
|
||||
|
||||
#ifndef __SWITCH__
|
||||
loadKeyBindings();
|
||||
loadKeyBindings();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void loadKeyBindings()
|
||||
{
|
||||
|
||||
|
||||
void Keyboard::loadKeyBindings()
|
||||
{
|
||||
#ifdef ENABLE_JSON
|
||||
try
|
||||
try
|
||||
{
|
||||
std::ifstream ifs("keyboard1.bindings.json", std::ifstream::in);
|
||||
|
||||
|
||||
if (ifs.is_open())
|
||||
{
|
||||
rapidjson::IStreamWrapper isw(ifs);
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(isw);
|
||||
|
||||
if (d.IsObject())
|
||||
{
|
||||
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
|
||||
{
|
||||
std::ifstream ifs("keyboard1.bindings.json", std::ifstream::in);
|
||||
|
||||
|
||||
if (ifs.is_open())
|
||||
if (itr->name.IsString() && itr->value.IsString())
|
||||
{
|
||||
rapidjson::IStreamWrapper isw(ifs);
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(isw);
|
||||
auto key = SDL_GetScancodeFromName(itr->name.GetString());
|
||||
auto value = getInputValue(itr->value.GetString());
|
||||
|
||||
if (d.IsObject())
|
||||
if (key != SDL_SCANCODE_UNKNOWN && value) {
|
||||
m_keyBindings[key] = value;
|
||||
else
|
||||
{
|
||||
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
|
||||
{
|
||||
if (itr->name.IsString() && itr->value.IsString())
|
||||
{
|
||||
auto key = SDL_GetScancodeFromName(itr->name.GetString());
|
||||
auto value = getInputValue(itr->value.GetString());
|
||||
|
||||
if (key != SDL_SCANCODE_UNKNOWN && value)
|
||||
{
|
||||
m_keyBindings[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO FIX sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO FIX sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void saveKeyBindings()
|
||||
{
|
||||
|
||||
|
||||
void Keyboard::saveKeyBindings()
|
||||
{
|
||||
#ifdef ENABLE_JSON
|
||||
try
|
||||
{
|
||||
rapidjson::Document d;
|
||||
d.SetObject();
|
||||
|
||||
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
|
||||
|
||||
for (const auto i : m_keyBindings)
|
||||
{
|
||||
rapidjson::Value value(getInputName(i.second), allocator);
|
||||
rapidjson::Value key(SDL_GetScancodeName(i.first), allocator);
|
||||
d.AddMember(key, value, allocator);
|
||||
}
|
||||
|
||||
saveJson(d, "keyboard1.bindings.json");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void clearRebindMode()
|
||||
{
|
||||
}
|
||||
|
||||
void resetRebinds()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool hasMouse() const
|
||||
{
|
||||
return true;
|
||||
return m_state.has_mouse;
|
||||
}
|
||||
|
||||
int keyboard_buttons_down;
|
||||
|
||||
int keyboard_map_scancode(SDL_Scancode scancode)
|
||||
{
|
||||
if (m_keyBindings.count(scancode))
|
||||
{
|
||||
return m_keyBindings[scancode];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool on_key_down(SDL_Scancode scancode)
|
||||
{
|
||||
int mapped = keyboard_map_scancode(scancode);
|
||||
keyboard_buttons_down |= mapped;
|
||||
return mapped != 0;
|
||||
}
|
||||
|
||||
bool on_key_up(SDL_Scancode scancode)
|
||||
{
|
||||
int mapped = keyboard_map_scancode(scancode);
|
||||
keyboard_buttons_down &= ~mapped;
|
||||
return mapped != 0;
|
||||
}
|
||||
|
||||
void on_all_keys_up()
|
||||
{
|
||||
keyboard_buttons_down = 0;
|
||||
}
|
||||
|
||||
void enableMouse()
|
||||
{
|
||||
this->state().has_mouse = true;
|
||||
}
|
||||
|
||||
bool canRebind(SDL_Scancode scancode, int input)
|
||||
{
|
||||
if (m_keyBindings.count(scancode) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto replacingInput = m_keyBindings[scancode];
|
||||
u64 count = 0;
|
||||
|
||||
for (auto i : m_keyBindings)
|
||||
{
|
||||
if (i.second == replacingInput)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count != 1;
|
||||
}
|
||||
|
||||
bool updateRebind(int input) override
|
||||
{
|
||||
int count = 0;
|
||||
auto state = SDL_GetKeyboardState(&count);
|
||||
u64 changed = 0;
|
||||
|
||||
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
|
||||
{
|
||||
m_lastKeyState[i] = (state[i] ^ m_lastKeyState[i]) & state[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
|
||||
{
|
||||
if (m_lastKeyState[i] && canRebind((SDL_Scancode)i, input))
|
||||
{
|
||||
m_keyBindings[(SDL_Scancode)i] = input;
|
||||
changed++;
|
||||
saveKeyBindings();
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
|
||||
return changed != 0;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
bool walk = false;
|
||||
int count = 0;
|
||||
auto state = SDL_GetKeyboardState(&count);
|
||||
|
||||
if (state[SDL_SCANCODE_F10] && (m_lastKeyState[SDL_SCANCODE_F10] ^ state[SDL_SCANCODE_F10]))
|
||||
{
|
||||
oot::config().game().fullscreen() = !oot::config().game().fullscreen();
|
||||
set_fullscreen(oot::config().game().fullscreen());
|
||||
}
|
||||
|
||||
if(state[SDL_SCANCODE_F9] && (m_lastKeyState[SDL_SCANCODE_F9] ^ state[SDL_SCANCODE_F9]))
|
||||
{
|
||||
Set_Language(Get_Language()+1);
|
||||
}
|
||||
|
||||
if (state[SDL_SCANCODE_ESCAPE] && (m_lastKeyState[SDL_SCANCODE_ESCAPE] ^ state[SDL_SCANCODE_ESCAPE]))
|
||||
{
|
||||
if (oot::config().game().fullscreen())
|
||||
{
|
||||
oot::config().game().fullscreen() = false;
|
||||
set_fullscreen(oot::config().game().fullscreen());
|
||||
}
|
||||
}
|
||||
|
||||
//When F5 is pressed. Mark all dpad button as pressed. Used to open map select
|
||||
if (state[SDL_SCANCODE_F5] && (m_lastKeyState[SDL_SCANCODE_F5] ^ state[SDL_SCANCODE_F5]))
|
||||
{
|
||||
m_state.button |= U_JPAD | D_JPAD | L_JPAD | R_JPAD;
|
||||
}
|
||||
|
||||
if (hid::isTasPlaying())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto x : m_keyBindings)
|
||||
{
|
||||
const auto scancode = x.first;
|
||||
const auto input = x.second;
|
||||
|
||||
if (scancode < count)
|
||||
{
|
||||
if (state[scancode])
|
||||
{
|
||||
if (input > 0xFFFF)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case STICK_X_DOWN:
|
||||
m_state.stick_y = -128;
|
||||
break;
|
||||
case STICK_X_UP:
|
||||
m_state.stick_y = 127;
|
||||
break;
|
||||
case STICK_X_LEFT:
|
||||
m_state.stick_x = -128;
|
||||
break;
|
||||
case STICK_X_RIGHT:
|
||||
m_state.stick_x = 127;
|
||||
break;
|
||||
case WALK_BUTTON:
|
||||
walk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->state().button |= input;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MOUSE
|
||||
int mouse_delta_x = 0;
|
||||
int mouse_delta_y = 0;
|
||||
|
||||
auto buttons = SDL_GetRelativeMouseState(&mouse_delta_x, &mouse_delta_y);
|
||||
this->enableMouse();
|
||||
|
||||
if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT))
|
||||
{
|
||||
m_state.button |= B_BUTTON;
|
||||
}
|
||||
|
||||
if (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT))
|
||||
{
|
||||
m_state.button |= A_BUTTON;
|
||||
}
|
||||
|
||||
if (buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE))
|
||||
{
|
||||
walk = true;
|
||||
}
|
||||
|
||||
m_state.mouse_x += mouse_delta_x * 4;
|
||||
m_state.mouse_y += mouse_delta_y * 4;
|
||||
|
||||
if (mouse_delta_x > 10)
|
||||
{
|
||||
int yy = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (walk)
|
||||
{
|
||||
m_state.stick_x *= 0.25f;
|
||||
m_state.stick_y *= 0.25f;
|
||||
}
|
||||
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unordered_map<SDL_Scancode, int> m_keyBindings;
|
||||
u8 m_lastKeyState[MAX_KEY_STATE];
|
||||
};
|
||||
} // namespace controller
|
||||
|
||||
Keyboard::Keyboard() : Driver()
|
||||
try
|
||||
{
|
||||
}
|
||||
rapidjson::Document d;
|
||||
d.SetObject();
|
||||
|
||||
Keyboard::~Keyboard()
|
||||
{
|
||||
}
|
||||
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
|
||||
|
||||
void Keyboard::scan(class Controllers* controllers)
|
||||
{
|
||||
if (!size())
|
||||
for (const auto i : m_keyBindings)
|
||||
{
|
||||
#ifdef ENABLE_MOUSE
|
||||
//SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
#endif
|
||||
auto controller = std::make_shared<controller::Keyboard>();
|
||||
#ifdef ENABLE_MOUSE
|
||||
rapidjson::Value value(getInputName(i.second), allocator);
|
||||
rapidjson::Value key(SDL_GetScancodeName(i.first), allocator);
|
||||
d.AddMember(key, value, allocator);
|
||||
}
|
||||
|
||||
controller->enableMouse();
|
||||
saveJson(d, "keyboard1.bindings.json");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
m_controllers.push_back(controller);
|
||||
players().attach(controller, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Keyboard::hasMouse() const
|
||||
{
|
||||
return true;
|
||||
return m_state.has_mouse;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Button Keyboard::keyboard_map_scancode(SDL_Scancode scancode)
|
||||
{
|
||||
if (m_keyBindings.count(scancode))
|
||||
return m_keyBindings[scancode];
|
||||
return Button::NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Keyboard::on_key_down(SDL_Scancode scancode)
|
||||
{
|
||||
Button mapped = keyboard_map_scancode(scancode);
|
||||
keyboard_buttons_down |= (int)mapped;
|
||||
return (int)mapped != 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Keyboard::on_key_up(SDL_Scancode scancode)
|
||||
{
|
||||
Button mapped = keyboard_map_scancode(scancode);
|
||||
keyboard_buttons_down &= ~(int)mapped;
|
||||
return (int)mapped != 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Keyboard::canRebind(SDL_Scancode scancode, Button input)
|
||||
{
|
||||
if (m_keyBindings.count(scancode) == 0)
|
||||
return true;
|
||||
|
||||
auto replacingInput = m_keyBindings[scancode];
|
||||
u64 count = 0;
|
||||
|
||||
for (auto i : m_keyBindings)
|
||||
{
|
||||
if (i.second == replacingInput)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count != 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Keyboard::updateRebind(Button input)
|
||||
{
|
||||
int count = 0;
|
||||
auto state = SDL_GetKeyboardState(&count);
|
||||
u64 changed = 0;
|
||||
|
||||
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
|
||||
m_lastKeyState[i] = (state[i] ^ m_lastKeyState[i]) & state[i];
|
||||
|
||||
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
|
||||
{
|
||||
if (m_lastKeyState[i] && canRebind((SDL_Scancode)i, input))
|
||||
{
|
||||
m_keyBindings[(SDL_Scancode)i] = input;
|
||||
changed++;
|
||||
saveKeyBindings();
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::update()
|
||||
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
|
||||
return changed != 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Keyboard::update()
|
||||
{
|
||||
bool walk = false;
|
||||
int count = 0;
|
||||
auto state = SDL_GetKeyboardState(&count);
|
||||
|
||||
if (state[SDL_SCANCODE_F10] && (m_lastKeyState[SDL_SCANCODE_F10] ^ state[SDL_SCANCODE_F10]))
|
||||
{
|
||||
for (auto& keyboard : m_controllers)
|
||||
oot::config().game().fullscreen() = !oot::config().game().fullscreen();
|
||||
set_fullscreen(oot::config().game().fullscreen());
|
||||
}
|
||||
|
||||
if (state[SDL_SCANCODE_F9] && (m_lastKeyState[SDL_SCANCODE_F9] ^ state[SDL_SCANCODE_F9]))
|
||||
Set_Language(Get_Language()+1);
|
||||
|
||||
if (state[SDL_SCANCODE_ESCAPE] && (m_lastKeyState[SDL_SCANCODE_ESCAPE] ^ state[SDL_SCANCODE_ESCAPE]))
|
||||
{
|
||||
if (oot::config().game().fullscreen())
|
||||
{
|
||||
((controller::Keyboard*)keyboard.get())->update();
|
||||
oot::config().game().fullscreen() = false;
|
||||
set_fullscreen(oot::config().game().fullscreen());
|
||||
}
|
||||
}
|
||||
} // namespace sm64::hid
|
||||
|
||||
//When F5 is pressed. Mark all dpad button as pressed. Used to open map select
|
||||
if (state[SDL_SCANCODE_F5] && (m_lastKeyState[SDL_SCANCODE_F5] ^ state[SDL_SCANCODE_F5]))
|
||||
m_state.button |= (uint16_t)Button::U_JPAD | (uint16_t)Button::D_JPAD | (uint16_t)Button::L_JPAD | (uint16_t)Button::R_JPAD;
|
||||
|
||||
if (Tas::isTasPlaying())
|
||||
return;
|
||||
|
||||
for (const auto& [scancode, input] : m_keyBindings)
|
||||
{
|
||||
if (scancode < count && state[scancode])
|
||||
{
|
||||
if ((uint32_t)input <= 0xFFFF)
|
||||
m_state.button |= (uint32_t)input;
|
||||
else
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case Button::STICK_X_DOWN:
|
||||
m_state.stick_y = -128;
|
||||
break;
|
||||
case Button::STICK_X_UP:
|
||||
m_state.stick_y = 127;
|
||||
break;
|
||||
case Button::STICK_X_LEFT:
|
||||
m_state.stick_x = -128;
|
||||
break;
|
||||
case Button::STICK_X_RIGHT:
|
||||
m_state.stick_x = 127;
|
||||
break;
|
||||
case Button::WALK_BUTTON:
|
||||
walk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MOUSE
|
||||
int mouse_delta_x = 0;
|
||||
int mouse_delta_y = 0;
|
||||
|
||||
auto buttons = SDL_GetRelativeMouseState(&mouse_delta_x, &mouse_delta_y);
|
||||
enableMouse();
|
||||
|
||||
if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT))
|
||||
m_state.button |= (uint16_t)Button::B_BUTTON;
|
||||
|
||||
if (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT))
|
||||
m_state.button |= (uint16_t)Button::A_BUTTON;
|
||||
|
||||
if (buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE))
|
||||
walk = true;
|
||||
|
||||
m_state.mouse_x += mouse_delta_x * 4;
|
||||
m_state.mouse_y += mouse_delta_y * 4;
|
||||
#endif
|
||||
|
||||
if (walk)
|
||||
{
|
||||
m_state.stick_x *= 0.25f;
|
||||
m_state.stick_y *= 0.25f;
|
||||
}
|
||||
|
||||
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Keyboard::scan()
|
||||
{
|
||||
if (!size())
|
||||
{
|
||||
#ifdef ENABLE_MOUSE
|
||||
//SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
#endif
|
||||
auto controller = std::make_shared<Keyboard>();
|
||||
#ifdef ENABLE_MOUSE
|
||||
|
||||
controller->enableMouse();
|
||||
#endif
|
||||
m_controllers.push_back(controller);
|
||||
Players::get().attach(controller, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*void Keyboard::update()
|
||||
{
|
||||
for (auto& keyboard : m_controllers)
|
||||
((Keyboard*)keyboard.get())->update();
|
||||
}*/
|
|
@ -1,23 +1,50 @@
|
|||
#pragma once
|
||||
#include <SDL2/SDL.h>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include "controllers.h"
|
||||
|
||||
#define MAX_KEY_STATE (16*1024)
|
||||
|
||||
namespace sm64::hid
|
||||
{
|
||||
class Keyboard : public Driver
|
||||
{
|
||||
public:
|
||||
Keyboard();
|
||||
virtual ~Keyboard();
|
||||
void scan(class Controllers* controllers) override;
|
||||
void update() override;
|
||||
bool defaultOnly() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
};
|
||||
} // namespace sm64::hid
|
||||
namespace oot
|
||||
{
|
||||
namespace hid
|
||||
{
|
||||
class Keyboard : public N64Controller, public InputDevice
|
||||
{
|
||||
public:
|
||||
Keyboard();
|
||||
virtual ~Keyboard() {}
|
||||
void scan();
|
||||
void update() override;
|
||||
bool defaultOnly() override { return true; }
|
||||
bool updateRebind(Button input) override;
|
||||
|
||||
void clearRebindMode() {}
|
||||
void resetRebinds() {}
|
||||
bool hasMouse() const;
|
||||
|
||||
void enableMouse() { state().has_mouse = true; }
|
||||
bool canRebind(SDL_Scancode scancode, Button input);
|
||||
|
||||
Button keyboard_map_scancode(SDL_Scancode scancode);
|
||||
|
||||
bool on_key_down(SDL_Scancode scancode);
|
||||
bool on_key_up(SDL_Scancode scancode);
|
||||
void on_all_keys_up() { keyboard_buttons_down = 0; }
|
||||
|
||||
void loadKeyBindings();
|
||||
void saveKeyBindings();
|
||||
|
||||
int keyboard_buttons_down;
|
||||
|
||||
private:
|
||||
const char* getInputName(Button input);
|
||||
Button getInputValue(const std::string& input);
|
||||
|
||||
std::unordered_map<SDL_Scancode, Button> m_keyBindings;
|
||||
u8 m_lastKeyState[MAX_KEY_STATE];
|
||||
};
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
#include <fstream>
|
||||
#include "../options.h"
|
||||
#include "../player/players.h"
|
||||
#include "tas.h"
|
||||
|
||||
#ifndef __SWITCH__
|
||||
#define DEADZONE 20
|
||||
|
@ -27,8 +28,6 @@
|
|||
#define RDEADZONE 0
|
||||
#endif
|
||||
|
||||
static bool init_ok;
|
||||
|
||||
#define INITIAL_PEAK 0x8000
|
||||
|
||||
static int g_lstickX_peak = INITIAL_PEAK;
|
||||
|
@ -36,510 +35,462 @@ static int g_lstickY_peak = INITIAL_PEAK;
|
|||
static int g_rstickX_peak = INITIAL_PEAK;
|
||||
static int g_rstickY_peak = INITIAL_PEAK;
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <time.h>
|
||||
extern struct Object* gMarioObject;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_JSON
|
||||
bool saveJson(rapidjson::Document& doc, const std::string& jsonFilePath);
|
||||
#endif
|
||||
|
||||
namespace sm64::hid
|
||||
using namespace oot::hid;
|
||||
|
||||
static bool g_haptics = false;
|
||||
|
||||
|
||||
//const char* getInputName(int input);
|
||||
//int getInputValue(const std::string& input);
|
||||
|
||||
|
||||
|
||||
Joypad::~Joypad()
|
||||
{
|
||||
static bool g_haptics = false;
|
||||
closeHaptics();
|
||||
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
|
||||
}
|
||||
|
||||
namespace controller
|
||||
{
|
||||
const char* getInputName(int input);
|
||||
int getInputValue(const std::string& input);
|
||||
|
||||
class SDL : public Controller
|
||||
{
|
||||
public:
|
||||
SDL(SDL_GameController* controller) : Controller(), m_context(controller), m_haptic(nullptr)
|
||||
{
|
||||
m_motorEnabled = initHaptics();
|
||||
Joypad::Joypad() : N64Controller()
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0)
|
||||
fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
|
||||
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_START] = START_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = Z_TRIG;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = R_TRIG;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_A] = A_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_X] = B_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_B] = A_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_Y] = B_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = L_CBUTTONS;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = R_CBUTTONS;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_UP] = U_CBUTTONS;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = D_CBUTTONS;
|
||||
|
||||
g_haptics = SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0;
|
||||
|
||||
|
||||
#ifndef __SWITCH__
|
||||
loadKeyBindings();
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual ~SDL()
|
||||
{
|
||||
closeHaptics();
|
||||
}
|
||||
|
||||
void loadKeyBindings()
|
||||
{
|
||||
#ifdef ENABLE_JSON
|
||||
try
|
||||
{
|
||||
std::ifstream ifs("gamepad1.bindings.json", std::ifstream::in);
|
||||
|
||||
|
||||
if (ifs.is_open())
|
||||
{
|
||||
rapidjson::IStreamWrapper isw(ifs);
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(isw);
|
||||
|
||||
if (d.IsObject())
|
||||
{
|
||||
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
|
||||
{
|
||||
if (itr->name.IsString() && itr->value.IsString())
|
||||
{
|
||||
auto key = SDL_GameControllerGetButtonFromString(itr->name.GetString());
|
||||
auto value = getInputValue(itr->value.GetString());
|
||||
|
||||
if (key != SDL_CONTROLLER_BUTTON_INVALID && value)
|
||||
{
|
||||
m_keyBindings[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void saveKeyBindings()
|
||||
{
|
||||
#ifdef ENABLE_JSON
|
||||
try
|
||||
{
|
||||
rapidjson::Document d;
|
||||
d.SetObject();
|
||||
|
||||
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
|
||||
|
||||
for (const auto i : m_keyBindings)
|
||||
{
|
||||
rapidjson::Value value(getInputName(i.second), allocator);
|
||||
rapidjson::Value key(SDL_GameControllerGetStringForButton(i.first), allocator);
|
||||
d.AddMember(key, value, allocator);
|
||||
}
|
||||
|
||||
saveJson(d, "gamepad1.bindings.json");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool initHaptics()
|
||||
{
|
||||
if(!g_haptics)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(m_context));
|
||||
|
||||
if(!m_haptic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(SDL_HapticRumbleSupported(m_haptic) != 1)
|
||||
{
|
||||
closeHaptics();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(SDL_HapticRumbleInit(m_haptic) != 0)
|
||||
{
|
||||
closeHaptics();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void closeHaptics()
|
||||
{
|
||||
if(m_haptic)
|
||||
{
|
||||
SDL_HapticClose(m_haptic);
|
||||
m_haptic = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SendMotorEvent(short time, short level) override
|
||||
{
|
||||
if(m_motorEnabled)
|
||||
{
|
||||
SDL_HapticRumblePlay(m_haptic, level / 100.0f, time * 10);
|
||||
}
|
||||
}
|
||||
|
||||
void SendMotorDecay(short level) override
|
||||
{
|
||||
}
|
||||
|
||||
void ResetMotorPack() override
|
||||
{
|
||||
if(m_motorEnabled)
|
||||
{
|
||||
SDL_HapticRumbleStop(m_haptic);
|
||||
}
|
||||
}
|
||||
|
||||
void SendMotorVib(int level) override
|
||||
{
|
||||
SendMotorEvent(30, level);
|
||||
}
|
||||
|
||||
static inline int8_t convertToByte(int value, int max)
|
||||
{
|
||||
int8_t result = value * 0x7F / max;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int8_t invert(const int8_t value)
|
||||
{
|
||||
if(value <= -128)
|
||||
{
|
||||
return 127;
|
||||
}
|
||||
|
||||
return -value;
|
||||
}
|
||||
|
||||
inline int16_t readAxis(SDL_GameControllerAxis axis)
|
||||
{
|
||||
return SDL_GameControllerGetAxis(m_context, axis);
|
||||
}
|
||||
|
||||
inline int8_t stickLeftX()
|
||||
{
|
||||
auto value = readAxis(SDL_CONTROLLER_AXIS_LEFTX);
|
||||
|
||||
if(abs(value) > g_lstickX_peak)
|
||||
{
|
||||
g_lstickX_peak = abs(value);
|
||||
}
|
||||
|
||||
return convertToByte(value, g_lstickX_peak);
|
||||
}
|
||||
|
||||
inline int8_t stickLeftY()
|
||||
{
|
||||
auto value = readAxis(SDL_CONTROLLER_AXIS_LEFTY);
|
||||
|
||||
if(abs(value) > g_lstickY_peak)
|
||||
{
|
||||
g_lstickY_peak = abs(value);
|
||||
}
|
||||
|
||||
return convertToByte(value, g_lstickY_peak);
|
||||
}
|
||||
|
||||
inline int8_t stickRightX()
|
||||
{
|
||||
auto value = readAxis(SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
|
||||
if(abs(value) > g_rstickX_peak)
|
||||
{
|
||||
g_rstickX_peak = abs(value);
|
||||
}
|
||||
|
||||
return convertToByte(value, g_rstickX_peak);
|
||||
}
|
||||
|
||||
inline int8_t stickRightY()
|
||||
{
|
||||
auto value = readAxis(SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
|
||||
if(abs(value) > g_rstickY_peak)
|
||||
{
|
||||
g_rstickY_peak = abs(value);
|
||||
}
|
||||
|
||||
return convertToByte(value, g_rstickY_peak);
|
||||
}
|
||||
|
||||
bool canRebind(SDL_GameControllerButton button, int input)
|
||||
{
|
||||
if (m_keyBindings.count(button) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto replacingInput = m_keyBindings[button];
|
||||
u64 count = 0;
|
||||
|
||||
if (replacingInput != START_BUTTON && replacingInput != A_BUTTON && replacingInput != B_BUTTON)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (replacingInput == input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto i : m_keyBindings)
|
||||
{
|
||||
if (i.second == replacingInput)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return count != 1;
|
||||
}
|
||||
|
||||
bool updateRebind(int input) override
|
||||
{
|
||||
u8 state[SDL_CONTROLLER_BUTTON_MAX];
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
|
||||
{
|
||||
bool newState = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
|
||||
state[i] = (m_buttonState[i] ^ newState) & newState;
|
||||
m_buttonState[i] = newState;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
|
||||
{
|
||||
if (state[i] && canRebind((SDL_GameControllerButton)i, input))
|
||||
{
|
||||
m_keyBindings[(SDL_GameControllerButton)i] = input;
|
||||
saveKeyBindings();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void update() override
|
||||
{
|
||||
if(!init_ok || !m_context || hid::isTasPlaying())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool walk = false;
|
||||
SDL_GameControllerUpdate();
|
||||
|
||||
if(m_context != NULL && !SDL_GameControllerGetAttached(m_context))
|
||||
{
|
||||
SDL_GameControllerClose(m_context);
|
||||
m_context = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
|
||||
{
|
||||
m_buttonState[i] = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
|
||||
}
|
||||
|
||||
m_state.stick_x = stickLeftX();
|
||||
m_state.stick_y = invert(stickLeftY());
|
||||
m_state.r_stick_x = stickRightX();
|
||||
m_state.r_stick_y = stickRightY();
|
||||
|
||||
for (const auto& i : m_keyBindings)
|
||||
{
|
||||
if (m_buttonState[i.first])
|
||||
{
|
||||
if (i.second > 0xFFFF)
|
||||
{
|
||||
switch (i.second)
|
||||
{
|
||||
case STICK_X_DOWN:
|
||||
m_state.stick_y = -128;
|
||||
break;
|
||||
case STICK_X_UP:
|
||||
m_state.stick_y = 127;
|
||||
break;
|
||||
case STICK_X_LEFT:
|
||||
m_state.stick_x = -128;
|
||||
break;
|
||||
case STICK_X_RIGHT:
|
||||
m_state.stick_x = 127;
|
||||
break;
|
||||
case WALK_BUTTON:
|
||||
walk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state.button |= i.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(oot::config().camera().useClassicCamera())
|
||||
{
|
||||
int16_t leftx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
int16_t lefty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
int16_t rightx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
int16_t righty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
|
||||
if(abs(righty) > 0x7000)
|
||||
{
|
||||
if(righty > 0)
|
||||
{
|
||||
m_state.button |= D_CBUTTONS;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state.button |= U_CBUTTONS;
|
||||
}
|
||||
}
|
||||
|
||||
if(abs(rightx) > 0x7000)
|
||||
{
|
||||
if(rightx > 1)
|
||||
{
|
||||
m_state.button |= R_CBUTTONS;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state.button |= L_CBUTTONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t ltrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||
int16_t rtrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||
|
||||
if(ltrig > 30 * 256)
|
||||
m_state.button |= Z_TRIG;
|
||||
//if(rtrig > 30 * 256)
|
||||
//m_state.button |= R_TRIG;
|
||||
|
||||
if (walk)
|
||||
{
|
||||
m_state.stick_x *= 0.25f;
|
||||
m_state.stick_y *= 0.25f;
|
||||
}
|
||||
|
||||
uint32_t magnitude_sq = (uint32_t)(m_state.stick_x * m_state.stick_x) + (uint32_t)(m_state.stick_y * m_state.stick_y);
|
||||
|
||||
if(magnitude_sq < (uint32_t)(DEADZONE * DEADZONE))
|
||||
{
|
||||
m_state.stick_x = 0;
|
||||
m_state.stick_y = 0;
|
||||
}
|
||||
|
||||
magnitude_sq = (uint32_t)(m_state.r_stick_x * m_state.r_stick_x) + (uint32_t)(m_state.r_stick_y * m_state.r_stick_y);
|
||||
|
||||
if(magnitude_sq < (uint32_t)(RDEADZONE * RDEADZONE))
|
||||
{
|
||||
m_state.r_stick_x = 0;
|
||||
m_state.r_stick_y = 0;
|
||||
}
|
||||
|
||||
//When the right trigger is pressed, interpret the c buttons as the dpad
|
||||
if (rtrig > 30 * 256)
|
||||
{
|
||||
if (m_state.button & U_CBUTTONS)
|
||||
m_state.button |= U_JPAD;
|
||||
if (m_state.button & D_CBUTTONS)
|
||||
m_state.button |= D_JPAD;
|
||||
if (m_state.button & L_CBUTTONS)
|
||||
m_state.button |= L_JPAD;
|
||||
if (m_state.button & R_CBUTTONS)
|
||||
m_state.button |= R_JPAD;
|
||||
|
||||
m_state.button &= ~(U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS);//Unpress the c buttons
|
||||
}
|
||||
|
||||
//When the back button is pressed. Mark all dpad button as pressed. Used to open map select
|
||||
if (m_buttonState[SDL_CONTROLLER_BUTTON_BACK])
|
||||
m_state.button |= U_JPAD | D_JPAD | L_JPAD | R_JPAD;
|
||||
}
|
||||
|
||||
protected:
|
||||
SDL_GameController* m_context;
|
||||
SDL_Haptic* m_haptic;
|
||||
std::unordered_map<SDL_GameControllerButton, int> m_keyBindings;
|
||||
u8 m_buttonState[SDL_CONTROLLER_BUTTON_MAX];
|
||||
};
|
||||
} // namespace controller
|
||||
SDL::SDL()
|
||||
{
|
||||
if(SDL_Init(SDL_INIT_GAMECONTROLLER) != 0)
|
||||
{
|
||||
fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
g_haptics = SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0;
|
||||
}
|
||||
|
||||
SDL::~SDL()
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
|
||||
}
|
||||
|
||||
void SDL::scan(class Controllers* controllers)
|
||||
{
|
||||
#ifdef __SWITCH__
|
||||
// swap A, B and X, Y to correct positions
|
||||
SDL_GameControllerAddMapping("53776974636820436F6E74726F6C6C65,Switch Controller,"
|
||||
"a:b0,b:b1,back:b11,"
|
||||
"dpdown:b15,dpleft:b12,dpright:b14,dpup:b13,"
|
||||
"leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,"
|
||||
"rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,"
|
||||
"start:b10,x:b2,y:b3");
|
||||
// swap A, B and X, Y to correct positions
|
||||
SDL_GameControllerAddMapping("53776974636820436F6E74726F6C6C65,Switch Controller,"
|
||||
"a:b0,b:b1,back:b11,"
|
||||
"dpdown:b15,dpleft:b12,dpright:b14,dpup:b13,"
|
||||
"leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,"
|
||||
"rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,"
|
||||
"start:b10,x:b2,y:b3");
|
||||
#endif
|
||||
|
||||
init_ok = true;
|
||||
|
||||
for(int i = 0; i < SDL_NumJoysticks(); i++)
|
||||
for (int i = 0; i < SDL_NumJoysticks(); i++)
|
||||
{
|
||||
if (SDL_IsGameController(i))
|
||||
{
|
||||
if(SDL_IsGameController(i))
|
||||
{
|
||||
auto context = SDL_GameControllerOpen(i);
|
||||
m_context = SDL_GameControllerOpen(i);
|
||||
|
||||
if(context)
|
||||
{
|
||||
auto controller = std::make_shared<controller::SDL>(context);
|
||||
m_controllers.push_back(controller);
|
||||
players().attach(controller, 0);
|
||||
}
|
||||
}
|
||||
if (m_context)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MOUSE
|
||||
// temporarily change mouse mode to not take over the cursor
|
||||
// SDL_SetRelativeMouseMode(m_controllers.size() ? SDL_FALSE : SDL_TRUE);
|
||||
// temporarily change mouse mode to not take over the cursor
|
||||
// SDL_SetRelativeMouseMode(m_controllers.size() ? SDL_FALSE : SDL_TRUE);
|
||||
#endif
|
||||
}
|
||||
} // namespace sm64::hid
|
||||
|
||||
|
||||
m_motorEnabled = initHaptics();
|
||||
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_START] = Button::START_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = Button::Z_TRIG;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = Button::R_TRIG;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_A] = Button::A_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_X] = Button::B_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_B] = Button::A_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_Y] = Button::B_BUTTON;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = Button::L_CBUTTONS;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = Button::R_CBUTTONS;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_UP] = Button::U_CBUTTONS;
|
||||
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = Button::D_CBUTTONS;
|
||||
|
||||
#ifndef __SWITCH__
|
||||
loadKeyBindings();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::scan()
|
||||
{
|
||||
auto controller = std::make_shared<Joypad>();
|
||||
m_controllers.push_back(controller);
|
||||
Players::get().attach(controller, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::loadKeyBindings()
|
||||
{
|
||||
#ifdef ENABLE_JSON
|
||||
try
|
||||
{
|
||||
std::ifstream ifs("gamepad1.bindings.json", std::ifstream::in);
|
||||
|
||||
|
||||
if (ifs.is_open())
|
||||
{
|
||||
rapidjson::IStreamWrapper isw(ifs);
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(isw);
|
||||
|
||||
if (d.IsObject())
|
||||
{
|
||||
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
|
||||
{
|
||||
if (itr->name.IsString() && itr->value.IsString())
|
||||
{
|
||||
auto key = SDL_GameControllerGetButtonFromString(itr->name.GetString());
|
||||
auto value = getInputValue(itr->value.GetString());
|
||||
|
||||
if (key != SDL_CONTROLLER_BUTTON_INVALID && value)
|
||||
m_keyBindings[key] = value;
|
||||
else
|
||||
{
|
||||
//sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::saveKeyBindings()
|
||||
{
|
||||
#ifdef ENABLE_JSON
|
||||
try
|
||||
{
|
||||
rapidjson::Document d;
|
||||
d.SetObject();
|
||||
|
||||
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
|
||||
|
||||
for (const auto i : m_keyBindings)
|
||||
{
|
||||
rapidjson::Value value(getInputName(i.second), allocator);
|
||||
rapidjson::Value key(SDL_GameControllerGetStringForButton(i.first), allocator);
|
||||
d.AddMember(key, value, allocator);
|
||||
}
|
||||
|
||||
saveJson(d, "gamepad1.bindings.json");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Joypad::initHaptics()
|
||||
{
|
||||
if (!g_haptics)
|
||||
return false;
|
||||
|
||||
m_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(m_context));
|
||||
|
||||
if (!m_haptic)
|
||||
return false;
|
||||
|
||||
if (SDL_HapticRumbleSupported(m_haptic) != 1)
|
||||
{
|
||||
closeHaptics();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_HapticRumbleInit(m_haptic) != 0)
|
||||
{
|
||||
closeHaptics();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::closeHaptics()
|
||||
{
|
||||
if (m_haptic)
|
||||
{
|
||||
SDL_HapticClose(m_haptic);
|
||||
m_haptic = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::SendMotorEvent(short time, short level)
|
||||
{
|
||||
if (m_motorEnabled)
|
||||
SDL_HapticRumblePlay(m_haptic, level / 100.0f, time * 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::ResetMotorPack()
|
||||
{
|
||||
if (m_motorEnabled)
|
||||
SDL_HapticRumbleStop(m_haptic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::SendMotorVib(int level)
|
||||
{
|
||||
SendMotorEvent(30, level);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int8_t Joypad::convertToByte(int value, int max)
|
||||
{
|
||||
int8_t result = value * 0x7F / max;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int8_t Joypad::invert(const int8_t value)
|
||||
{
|
||||
if (value <= -128)
|
||||
return 127;
|
||||
return -value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int8_t Joypad::stickLeftX()
|
||||
{
|
||||
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
|
||||
if (abs(value) > g_lstickX_peak)
|
||||
g_lstickX_peak = abs(value);
|
||||
|
||||
return convertToByte(value, g_lstickX_peak);
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline int8_t Joypad::stickLeftY()
|
||||
{
|
||||
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
|
||||
if (abs(value) > g_lstickY_peak)
|
||||
g_lstickY_peak = abs(value);
|
||||
|
||||
return convertToByte(value, g_lstickY_peak);
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline int8_t Joypad::stickRightX()
|
||||
{
|
||||
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
|
||||
if (abs(value) > g_rstickX_peak)
|
||||
g_rstickX_peak = abs(value);
|
||||
|
||||
return convertToByte(value, g_rstickX_peak);
|
||||
}
|
||||
|
||||
inline int8_t Joypad::stickRightY()
|
||||
{
|
||||
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
|
||||
if (abs(value) > g_rstickY_peak)
|
||||
g_rstickY_peak = abs(value);
|
||||
|
||||
return convertToByte(value, g_rstickY_peak);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Joypad::canRebind(SDL_GameControllerButton button, Button input)
|
||||
{
|
||||
if (m_keyBindings.count(button) == 0)
|
||||
return true;
|
||||
|
||||
auto replacingInput = m_keyBindings[button];
|
||||
u64 count = 0;
|
||||
|
||||
if (replacingInput != Button::START_BUTTON && replacingInput != Button::A_BUTTON && replacingInput != Button::B_BUTTON)
|
||||
return true;
|
||||
|
||||
if (replacingInput == input)
|
||||
return true;
|
||||
|
||||
for (auto& [input, button] : m_keyBindings)
|
||||
{
|
||||
if (button == replacingInput)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 1)
|
||||
return false;
|
||||
return count != 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Joypad::updateRebind(Button input)
|
||||
{
|
||||
u8 state[SDL_CONTROLLER_BUTTON_MAX];
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
|
||||
{
|
||||
bool newState = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
|
||||
state[i] = (m_buttonState[i] ^ newState) & newState;
|
||||
m_buttonState[i] = newState;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
|
||||
{
|
||||
if (state[i] && canRebind((SDL_GameControllerButton)i, input))
|
||||
{
|
||||
m_keyBindings[(SDL_GameControllerButton)i] = input;
|
||||
saveKeyBindings();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Joypad::update()
|
||||
{
|
||||
if (!m_context || Tas::isTasPlaying())
|
||||
return;
|
||||
|
||||
bool walk = false;
|
||||
SDL_GameControllerUpdate();
|
||||
|
||||
if (m_context != NULL && !SDL_GameControllerGetAttached(m_context))
|
||||
{
|
||||
SDL_GameControllerClose(m_context);
|
||||
m_context = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
|
||||
m_buttonState[i] = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
|
||||
|
||||
m_state.stick_x = stickLeftX();
|
||||
m_state.stick_y = invert(stickLeftY());
|
||||
m_state.r_stick_x = stickRightX();
|
||||
m_state.r_stick_y = stickRightY();
|
||||
|
||||
for (const auto& [scancode, input] : m_keyBindings)
|
||||
{
|
||||
if (m_buttonState[scancode])
|
||||
{
|
||||
if ((uint32_t)input <= 0xFFFF)
|
||||
m_state.button |= (uint16_t)input;
|
||||
else
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case Button::STICK_X_DOWN:
|
||||
m_state.stick_y = -128;
|
||||
break;
|
||||
case Button::STICK_X_UP:
|
||||
m_state.stick_y = 127;
|
||||
break;
|
||||
case Button::STICK_X_LEFT:
|
||||
m_state.stick_x = -128;
|
||||
break;
|
||||
case Button::STICK_X_RIGHT:
|
||||
m_state.stick_x = 127;
|
||||
break;
|
||||
case Button::WALK_BUTTON:
|
||||
walk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (oot::config().camera().useClassicCamera())
|
||||
{
|
||||
int16_t leftx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
int16_t lefty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
int16_t rightx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
int16_t righty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
|
||||
if (abs(righty) > 0x7000)
|
||||
{
|
||||
if (righty > 0)
|
||||
m_state.button |= (uint16_t)Button::D_CBUTTONS;
|
||||
else
|
||||
m_state.button |= (uint16_t)Button::U_CBUTTONS;
|
||||
}
|
||||
|
||||
if (abs(rightx) > 0x7000)
|
||||
{
|
||||
if (rightx > 1)
|
||||
m_state.button |= (uint16_t)Button::R_CBUTTONS;
|
||||
else
|
||||
m_state.button |= (uint16_t)Button::L_CBUTTONS;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t ltrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||
int16_t rtrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||
|
||||
if (ltrig > 30 * 256)
|
||||
m_state.button |= (uint16_t)Button::Z_TRIG;
|
||||
//if(rtrig > 30 * 256)
|
||||
//m_state.button |= (uint16_t)Button::R_TRIG;
|
||||
|
||||
if (walk)
|
||||
{
|
||||
m_state.stick_x *= 0.25f;
|
||||
m_state.stick_y *= 0.25f;
|
||||
}
|
||||
|
||||
uint32_t magnitude_sq = (uint32_t)(m_state.stick_x * m_state.stick_x) + (uint32_t)(m_state.stick_y * m_state.stick_y);
|
||||
|
||||
if (magnitude_sq < (uint32_t)(DEADZONE * DEADZONE))
|
||||
{
|
||||
m_state.stick_x = 0;
|
||||
m_state.stick_y = 0;
|
||||
}
|
||||
|
||||
magnitude_sq = (uint32_t)(m_state.r_stick_x * m_state.r_stick_x) + (uint32_t)(m_state.r_stick_y * m_state.r_stick_y);
|
||||
|
||||
if (magnitude_sq < (uint32_t)(RDEADZONE * RDEADZONE))
|
||||
{
|
||||
m_state.r_stick_x = 0;
|
||||
m_state.r_stick_y = 0;
|
||||
}
|
||||
|
||||
//When the right trigger is pressed, interpret the c buttons as the dpad
|
||||
if (rtrig > 30 * 256)
|
||||
{
|
||||
if (m_state.button & (uint16_t)Button::U_CBUTTONS)
|
||||
m_state.button |= (uint16_t)Button::U_JPAD;
|
||||
if (m_state.button & (uint16_t)Button::D_CBUTTONS)
|
||||
m_state.button |= (uint16_t)Button::D_JPAD;
|
||||
if (m_state.button & (uint16_t)Button::L_CBUTTONS)
|
||||
m_state.button |= (uint16_t)Button::L_JPAD;
|
||||
if (m_state.button & (uint16_t)Button::R_CBUTTONS)
|
||||
m_state.button |= (uint16_t)Button::R_JPAD;
|
||||
|
||||
m_state.button &= ~((uint16_t)Button::U_CBUTTONS | (uint16_t)Button::D_CBUTTONS | (uint16_t)Button::L_CBUTTONS | (uint16_t)Button::R_CBUTTONS);//Unpress the c buttons
|
||||
}
|
||||
|
||||
//When the back button is pressed. Mark all dpad button as pressed. Used to open map select
|
||||
if (m_buttonState[SDL_CONTROLLER_BUTTON_BACK])
|
||||
m_state.button |= (uint16_t)Button::U_JPAD | (uint16_t)Button::D_JPAD | (uint16_t)Button::L_JPAD | (uint16_t)Button::R_JPAD;
|
||||
}
|
||||
#endif
|
|
@ -1,16 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <SDL2/SDL.h>
|
||||
#include "controllers.h"
|
||||
|
||||
namespace sm64::hid
|
||||
{
|
||||
class SDL : public Driver
|
||||
{
|
||||
public:
|
||||
SDL();
|
||||
virtual ~SDL();
|
||||
void scan(class Controllers* controllers) override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace sm64::hid
|
||||
|
||||
namespace oot
|
||||
{
|
||||
namespace hid
|
||||
{
|
||||
class Joypad : public N64Controller, public InputDevice
|
||||
{
|
||||
public:
|
||||
Joypad();
|
||||
virtual ~Joypad();
|
||||
|
||||
virtual void scan() override;
|
||||
|
||||
void loadKeyBindings();
|
||||
void saveKeyBindings();
|
||||
|
||||
void ResetMotorPack() override;
|
||||
|
||||
void SendMotorVib(int level) override;
|
||||
|
||||
static inline int8_t convertToByte(int value, int max);
|
||||
|
||||
static inline int8_t invert(const int8_t value);
|
||||
|
||||
inline int8_t stickLeftX();
|
||||
inline int8_t stickLeftY();
|
||||
inline int8_t stickRightX();
|
||||
inline int8_t stickRightY();
|
||||
|
||||
bool canRebind(SDL_GameControllerButton button, Button input);
|
||||
|
||||
bool updateRebind(Button input) override;
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
bool initHaptics();
|
||||
void closeHaptics();
|
||||
|
||||
void SendMotorEvent(short time, short level) override;
|
||||
void SendMotorDecay(short level) override {};
|
||||
|
||||
SDL_GameController* m_context = nullptr;
|
||||
SDL_Haptic* m_haptic = nullptr;
|
||||
std::unordered_map<SDL_GameControllerButton, Button> m_keyBindings;
|
||||
u8 m_buttonState[SDL_CONTROLLER_BUTTON_MAX];
|
||||
};
|
||||
}
|
||||
}
|
|
@ -2,74 +2,83 @@
|
|||
#include <stdio.h>
|
||||
#include "../player/players.h"
|
||||
#include "../options.h"
|
||||
#include "tas.h"
|
||||
|
||||
static u64 g_counter = 0;
|
||||
static bool g_tasPlaying = false;
|
||||
static bool g_tasPlaying = false;
|
||||
|
||||
namespace sm64::hid
|
||||
using namespace oot::hid;
|
||||
|
||||
|
||||
bool Tas::isTasPlaying()
|
||||
{
|
||||
bool isTasPlaying()
|
||||
{
|
||||
return g_tasPlaying;
|
||||
}
|
||||
return g_tasPlaying;
|
||||
}
|
||||
|
||||
namespace controller
|
||||
|
||||
|
||||
void Tas::playTas(bool enabled)
|
||||
{
|
||||
g_tasPlaying = enabled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Tas::Tas() : N64Controller()
|
||||
{
|
||||
fp = fopen("last-run.tas", "rb");
|
||||
|
||||
if (fp)
|
||||
{
|
||||
class Tas : public Controller
|
||||
//fread(&oot::config(), 1, sizeof(oot::config()), fp);
|
||||
|
||||
#define SRAM_SIZE 0x8000//But this in ultra_reimplementation.h?
|
||||
|
||||
uint8_t* sram = new uint8_t[SRAM_SIZE];
|
||||
fread(sram, sizeof(uint8_t), SRAM_SIZE, fp);
|
||||
|
||||
FILE* save = nullptr;
|
||||
fopen_s(&save, "oot.sav", "wb");
|
||||
if (save)
|
||||
{
|
||||
public:
|
||||
Tas() : Controller()
|
||||
{
|
||||
fp = fopen("cont.tas", "rb");
|
||||
|
||||
if(fp != NULL)
|
||||
{
|
||||
fread(&oot::config(), 1, sizeof(oot::config()), fp);
|
||||
g_tasPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Tas()
|
||||
{
|
||||
if(fp)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
if(fp != NULL)
|
||||
{
|
||||
auto r = fread(&m_state, 1, sizeof(m_state), fp);
|
||||
if (m_state.button)
|
||||
{
|
||||
int x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
FILE* fp;
|
||||
};
|
||||
} // namespace controller
|
||||
|
||||
Tas::Tas() : Driver()
|
||||
{
|
||||
}
|
||||
|
||||
Tas::~Tas()
|
||||
{
|
||||
}
|
||||
|
||||
void Tas::scan(class Controllers* controllers)
|
||||
{
|
||||
if(!size())
|
||||
{
|
||||
auto controller = std::make_shared<controller::Tas>();
|
||||
m_controllers.push_back(controller);
|
||||
players().attach(controller, 0);
|
||||
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, save);
|
||||
fclose(save);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sm64::hid
|
||||
delete[] sram;
|
||||
|
||||
g_tasPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Tas::~Tas()
|
||||
{
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Tas::update()
|
||||
{
|
||||
if (fp)
|
||||
{
|
||||
auto r = fread(&m_state, 1, sizeof(m_state), fp);
|
||||
if (m_state.button)
|
||||
int x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Tas::scan()
|
||||
{
|
||||
if (!size())
|
||||
{
|
||||
auto controller = std::make_shared<Tas>();
|
||||
m_controllers.push_back(controller);
|
||||
Players::get().attach(controller, 0);
|
||||
}
|
||||
}
|
|
@ -1,15 +1,26 @@
|
|||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include "controllers.h"
|
||||
|
||||
namespace sm64::hid
|
||||
{
|
||||
class Tas : public Driver
|
||||
{
|
||||
public:
|
||||
Tas();
|
||||
virtual ~Tas();
|
||||
void scan(class Controllers* controllers) override;
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace sm64::hid
|
||||
|
||||
namespace oot
|
||||
{
|
||||
namespace hid
|
||||
{
|
||||
class Tas : public N64Controller, public InputDevice
|
||||
{
|
||||
public:
|
||||
static bool isTasPlaying();
|
||||
static void playTas(bool enabled);
|
||||
|
||||
Tas();
|
||||
virtual ~Tas();
|
||||
void scan() override;
|
||||
void update();
|
||||
|
||||
private:
|
||||
FILE* fp = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -288,6 +288,11 @@ namespace oot
|
|||
return m_recordTas;
|
||||
}
|
||||
|
||||
void Game::recordTas(bool enable)
|
||||
{
|
||||
m_recordTas = enable;
|
||||
}
|
||||
|
||||
bool& Game::forceMouse()
|
||||
{
|
||||
return m_forceMouse;
|
||||
|
|
|
@ -181,6 +181,7 @@ namespace oot
|
|||
bool& disableFramePacing();
|
||||
|
||||
bool& recordTas();
|
||||
void recordTas(bool enable);
|
||||
bool& forceMouse();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#define ENABLE_OPENGL
|
||||
#define USE_GLIDEN64
|
||||
#include "window.h"
|
||||
#include "options.h"
|
||||
#include "player/players.h"
|
||||
#include "controller/tas.h"
|
||||
|
||||
static std::unique_ptr<platform::window::Base> gWindow;
|
||||
|
||||
|
@ -287,6 +290,13 @@ void main_func(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG//Record TAS to capture bugs and crashes
|
||||
oot::hid::Tas::playTas(true);//Uncomment to play back TAS/crash report from end-users
|
||||
|
||||
if (!oot::hid::Tas::isTasPlaying())
|
||||
oot::config().game().recordTas(true);
|
||||
#endif
|
||||
|
||||
//static u64 pool[0x165000 / 8 / 4 * sizeof(void*)];
|
||||
//main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0]));
|
||||
//gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
|
||||
|
@ -328,6 +338,7 @@ void main_func(void)
|
|||
inited = 1;
|
||||
|
||||
Graph_ThreadEntry(0);
|
||||
|
||||
gfx_shutdown();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,72 +1,46 @@
|
|||
#include "player.h"
|
||||
#include "../options.h"
|
||||
|
||||
namespace sm64
|
||||
|
||||
|
||||
using namespace oot::hid;
|
||||
|
||||
|
||||
void Player::attach(const std::shared_ptr<N64Controller>& controller)
|
||||
{
|
||||
Player::Player() : m_rebindInput(0)
|
||||
m_controllers.push_back(controller);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Player::update()
|
||||
{
|
||||
m_controller.state().reset();
|
||||
|
||||
if (isRebindMode())
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
//for (auto& controller : m_controllers)
|
||||
//result |= controller->updateRebind(m_rebindInput);
|
||||
|
||||
if (result)
|
||||
m_rebindInput = -10;
|
||||
}
|
||||
|
||||
Player::~Player()
|
||||
else if (m_rebindInput < 0)
|
||||
m_rebindInput++;
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
void Player::attach(const std::shared_ptr<hid::Controller>& controller)
|
||||
{
|
||||
m_controllers.push_back(controller);
|
||||
}
|
||||
|
||||
void Player::detachControllers()
|
||||
{
|
||||
m_controllers.resize(0);
|
||||
}
|
||||
|
||||
void Player::update()
|
||||
{
|
||||
m_controller.state().reset();
|
||||
|
||||
if (isRebindMode())
|
||||
for (auto& controller : m_controllers)
|
||||
{
|
||||
bool result = 0;
|
||||
controller->state().reset();
|
||||
controller->update();
|
||||
|
||||
for (auto& controller : m_controllers)
|
||||
{
|
||||
result |= controller->updateRebind(m_rebindInput);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
m_rebindInput = -10;
|
||||
}
|
||||
m_controller.merge(*controller);
|
||||
}
|
||||
else if (m_rebindInput < 0)
|
||||
{
|
||||
m_rebindInput++;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& controller : m_controllers)
|
||||
{
|
||||
controller->state().reset();
|
||||
controller->update();
|
||||
|
||||
m_controller.merge(*controller);
|
||||
}
|
||||
if (m_controllers.size() > 2 && !oot::config().game().forceMouse())
|
||||
{
|
||||
m_controller.state().has_mouse = false;
|
||||
}
|
||||
}
|
||||
m_controller.resolveInputs();
|
||||
if (m_controllers.size() > 2 && !oot::config().game().forceMouse())
|
||||
m_controller.state().has_mouse = false;
|
||||
}
|
||||
|
||||
void Player::rebind(int input)
|
||||
{
|
||||
m_rebindInput = input;
|
||||
}
|
||||
|
||||
bool Player::isRebindMode() const
|
||||
{
|
||||
return m_rebindInput > 0;
|
||||
}
|
||||
} // namespace sm64
|
||||
m_controller.resolveInputs();
|
||||
}
|
|
@ -4,33 +4,35 @@
|
|||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace sm64
|
||||
|
||||
|
||||
namespace oot
|
||||
{
|
||||
class Player
|
||||
namespace hid
|
||||
{
|
||||
class Player
|
||||
{
|
||||
public:
|
||||
Player();
|
||||
virtual ~Player();
|
||||
Player() : m_rebindInput(0) {}
|
||||
virtual ~Player() {}
|
||||
|
||||
std::vector<std::shared_ptr<hid::Controller> >& controllers()
|
||||
{
|
||||
return m_controllers;
|
||||
}
|
||||
void attach(const std::shared_ptr<hid::Controller>& controller);
|
||||
void detachControllers();
|
||||
void update();
|
||||
hid::Controller& controller()
|
||||
{
|
||||
return m_controller;
|
||||
}
|
||||
std::vector<std::shared_ptr<N64Controller> >& controllers() {
|
||||
return m_controllers;
|
||||
}
|
||||
void attach(const std::shared_ptr<N64Controller>& controller);
|
||||
void detachControllers() { m_controllers.resize(0); }
|
||||
void update();
|
||||
N64Controller& controller() {
|
||||
return m_controller;
|
||||
}
|
||||
|
||||
void rebind(int input);
|
||||
bool isRebindMode() const;
|
||||
void rebind(int input) { m_rebindInput = input; }
|
||||
bool isRebindMode() const { return m_rebindInput > 0; }
|
||||
|
||||
protected:
|
||||
std::vector<std::shared_ptr<hid::Controller> > m_controllers;
|
||||
hid::Controller m_controller;
|
||||
int m_rebindInput;
|
||||
};
|
||||
|
||||
} // namespace sm64
|
||||
std::vector<std::shared_ptr<N64Controller> > m_controllers;
|
||||
N64Controller m_controller;
|
||||
int m_rebindInput;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,73 +1,82 @@
|
|||
#include <algorithm>
|
||||
#include "players.h"
|
||||
#include "../controller/controllers.h"
|
||||
#include "../options.h"
|
||||
|
||||
#include "z64.h";
|
||||
#include "padmgr.h"
|
||||
|
||||
extern PadMgr gPadMgr;
|
||||
|
||||
using namespace oot::hid;
|
||||
|
||||
Players g_players;
|
||||
|
||||
|
||||
extern "C" {
|
||||
void hid_init() {
|
||||
sm64::hid::controllers().scan();
|
||||
InputDeviceManager::get().scan();
|
||||
}
|
||||
|
||||
void hid_update() {
|
||||
sm64::players().update();
|
||||
Players::Update();
|
||||
}
|
||||
}
|
||||
|
||||
namespace sm64
|
||||
|
||||
|
||||
Players& Players::get()
|
||||
{
|
||||
Players g_players;
|
||||
return g_players;
|
||||
}
|
||||
|
||||
Players& players()
|
||||
|
||||
|
||||
void Players::Update()
|
||||
{
|
||||
g_players.update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
const N64Controller* Players::GetController()
|
||||
{
|
||||
if (g_players[0].controllers().size() == 0)
|
||||
return nullptr;
|
||||
return g_players[0].controllers()[0].get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Players::update()
|
||||
{
|
||||
memset(&gPadMgr.ctrlrIsConnected[0], 0, sizeof(gPadMgr.ctrlrIsConnected));
|
||||
|
||||
int i = 0;
|
||||
for (auto& player : m_players)
|
||||
{
|
||||
return g_players;
|
||||
player.update();
|
||||
|
||||
gPadMgr.inputs[i].cur.button = player.controller().state().button;
|
||||
gPadMgr.inputs[i].cur.stick_x = player.controller().state().stick_x;
|
||||
gPadMgr.inputs[i].cur.stick_y = player.controller().state().stick_y;
|
||||
|
||||
gPadMgr.ctrlrIsConnected[i] = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
Player& player(const u64 i)
|
||||
gPadMgr.nControllers = i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Players::attach(const std::shared_ptr<N64Controller>& controller, const u8 playerId)
|
||||
{
|
||||
if (playerId == MAX_PLAYERS)
|
||||
m_players[m_size++].attach(controller);
|
||||
else
|
||||
{
|
||||
return g_players[i];
|
||||
m_players[playerId].attach(controller);
|
||||
m_size = std::max((u64)playerId + 1, m_size);
|
||||
}
|
||||
|
||||
Players::Players() : m_players(), m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
const u64 Players::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void Players::update()
|
||||
{
|
||||
int i=0;
|
||||
memset(&gPadMgr.ctrlrIsConnected[0], 0, sizeof(gPadMgr.ctrlrIsConnected));
|
||||
for(auto& player : m_players)
|
||||
{
|
||||
player.update();
|
||||
|
||||
gPadMgr.inputs[i].cur.button = player.controller().state().button;
|
||||
gPadMgr.inputs[i].cur.stick_x = player.controller().state().stick_x;
|
||||
gPadMgr.inputs[i].cur.stick_y = player.controller().state().stick_y;
|
||||
|
||||
gPadMgr.ctrlrIsConnected[i] = true;
|
||||
i++;
|
||||
}
|
||||
gPadMgr.nControllers = i;
|
||||
}
|
||||
|
||||
void Players::attach(const std::shared_ptr<hid::Controller>& controller, const u8 playerId)
|
||||
{
|
||||
if(playerId == MAX_PLAYERS)
|
||||
{
|
||||
m_players[m_size++].attach(controller);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_players[playerId].attach(controller);
|
||||
m_size = std::max((u64)playerId + 1, m_size);
|
||||
}
|
||||
}
|
||||
} // namespace sm64
|
||||
}
|
|
@ -1,30 +1,36 @@
|
|||
#pragma once
|
||||
#include "ultra64/types.h"
|
||||
#include "player.h"
|
||||
#include <vector>
|
||||
#include "player.h"
|
||||
#include "../controller/sdl.h"
|
||||
|
||||
namespace sm64
|
||||
|
||||
static const u8 MAX_PLAYERS = 0xFF;
|
||||
|
||||
|
||||
namespace oot
|
||||
{
|
||||
static const u8 MAX_PLAYERS = 0xFF;
|
||||
class Players
|
||||
namespace hid
|
||||
{
|
||||
public:
|
||||
static const u32 MAX_PLAYERS = 1;
|
||||
|
||||
Players();
|
||||
const u64 size() const;
|
||||
void update();
|
||||
Player& operator[](u32 i)
|
||||
class Players
|
||||
{
|
||||
return m_players[i];
|
||||
}
|
||||
void attach(const std::shared_ptr<hid::Controller>& controller, const u8 playerId = MAX_PLAYERS);
|
||||
public:
|
||||
static const u32 MAX_PLAYERS = 1;
|
||||
|
||||
protected:
|
||||
Player m_players[MAX_PLAYERS];
|
||||
u64 m_size;
|
||||
};
|
||||
static Players& get();
|
||||
static void Update();
|
||||
static const N64Controller* GetController();
|
||||
|
||||
Players& players();
|
||||
Player& player(const u64 i);
|
||||
} // namespace sm64
|
||||
Players() = default;
|
||||
const u64 size() const { return m_size; }
|
||||
void update();
|
||||
void attach(const std::shared_ptr<N64Controller>& controller, const u8 playerId = MAX_PLAYERS);
|
||||
|
||||
Player& operator[](u32 i) { return m_players[i]; }
|
||||
|
||||
private:
|
||||
Player m_players[MAX_PLAYERS];
|
||||
u64 m_size = 0;
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user