mirror of
https://github.com/XProger/OpenLara.git
synced 2025-04-21 11:31:56 +02:00
323 lines
9.6 KiB
C++
323 lines
9.6 KiB
C++
#ifndef H_INPUT
|
|
#define H_INPUT
|
|
|
|
#include "core.h"
|
|
#include "utils.h"
|
|
|
|
#define MAX_PLAYERS COUNT(Core::settings.controls)
|
|
|
|
#define INPUT_JOY_COUNT 4
|
|
#define INPUT_JOY_DZ_STICK 0.2f
|
|
#define INPUT_JOY_DZ_TRIGGER 0.01f
|
|
|
|
namespace Input {
|
|
InputKey lastKey;
|
|
bool down[ikMAX];
|
|
bool state[MAX_PLAYERS][cMAX];
|
|
ControlKey lastState[MAX_PLAYERS];
|
|
|
|
struct Mouse {
|
|
vec2 pos;
|
|
struct {
|
|
vec2 L, R, M;
|
|
} start;
|
|
} mouse;
|
|
|
|
struct Joystick {
|
|
vec2 L, R;
|
|
float LT, RT;
|
|
JoyKey lastKey;
|
|
bool down[jkMAX];
|
|
} joy[INPUT_JOY_COUNT];
|
|
|
|
struct Touch {
|
|
int id;
|
|
vec2 start;
|
|
vec2 pos;
|
|
} touch[6];
|
|
|
|
struct HMD {
|
|
mat4 head;
|
|
mat4 eye[2];
|
|
mat4 proj[2];
|
|
mat4 controllers[2];
|
|
vec3 zero;
|
|
bool ready;
|
|
bool state[cMAX];
|
|
|
|
void setView(const mat4 &pL, const mat4 &pR, const mat4 &vL, const mat4 &vR) {
|
|
proj[0] = pL;
|
|
proj[1] = pR;
|
|
eye[0] = vL;
|
|
eye[1] = vR;
|
|
}
|
|
|
|
void reset() {
|
|
eye[0].identity();
|
|
eye[1].identity();
|
|
proj[0].identity();
|
|
proj[1].identity();
|
|
}
|
|
} hmd;
|
|
|
|
enum TouchButton { bMove, bWeapon, bWalk, bAction, bJump, bInventory, bMAX };
|
|
enum TouchZone { zMove, zLook, zButton, zMAX };
|
|
|
|
float touchTimerVis, touchTimerTap;
|
|
InputKey touchKey[zMAX];
|
|
|
|
TouchButton btn;
|
|
vec2 btnPos[bMAX];
|
|
bool btnEnable[bMAX];
|
|
float btnRadius;
|
|
bool doubleTap;
|
|
|
|
void setDown(InputKey key, bool value, int index = 0) {
|
|
if (down[key] == value)
|
|
return;
|
|
|
|
if (value)
|
|
switch (key) {
|
|
case ikMouseL : mouse.start.L = mouse.pos; break;
|
|
case ikMouseR : mouse.start.R = mouse.pos; break;
|
|
case ikMouseM : mouse.start.M = mouse.pos; break;
|
|
case ikTouchA :
|
|
case ikTouchB :
|
|
case ikTouchC :
|
|
case ikTouchD :
|
|
case ikTouchE :
|
|
case ikTouchF : touch[key - ikTouchA].start = touch[key - ikTouchA].pos; break;
|
|
default : ;
|
|
}
|
|
down[key] = value;
|
|
|
|
if (value && key <= ikBack) {
|
|
lastKey = key;
|
|
touchTimerVis = 0.0f;
|
|
}
|
|
}
|
|
|
|
void setPos(InputKey key, const vec2 &pos) {
|
|
switch (key) {
|
|
case ikMouseL :
|
|
case ikMouseR :
|
|
case ikMouseM : mouse.pos = pos; return;
|
|
case ikTouchA :
|
|
case ikTouchB :
|
|
case ikTouchC :
|
|
case ikTouchD :
|
|
case ikTouchE :
|
|
case ikTouchF : touch[key - ikTouchA].pos = pos; return;
|
|
default : return;
|
|
}
|
|
}
|
|
|
|
void setJoyDown(int index, JoyKey key, bool value) {
|
|
if (joy[index].down[key] == value)
|
|
return;
|
|
|
|
joy[index].down[key] = value;
|
|
|
|
if (value) {
|
|
joy[index].lastKey = key;
|
|
touchTimerVis = 0.0f;
|
|
}
|
|
}
|
|
|
|
void setJoyPos(int index, JoyKey key, const vec2 &pos) {
|
|
switch (key) {
|
|
case jkL : joy[index].L = pos; return;
|
|
case jkR : joy[index].R = pos; return;
|
|
case jkLT : joy[index].LT = pos.x; break;
|
|
case jkRT : joy[index].RT = pos.x; break;
|
|
default : return;
|
|
}
|
|
setJoyDown(index, key, pos.x > EPS); // gamepad LT, RT auto-down state
|
|
}
|
|
|
|
void setJoyVibration(int playerIndex, float L, float R) {
|
|
if (!Core::settings.controls[playerIndex].vibration)
|
|
return;
|
|
osJoyVibrate(Core::settings.controls[playerIndex].joyIndex, L, R);
|
|
}
|
|
|
|
void stopJoyVibration() {
|
|
osJoyVibrate(Core::settings.controls[0].joyIndex, 0.0f, 0.0f);
|
|
osJoyVibrate(Core::settings.controls[1].joyIndex, 0.0f, 0.0f);
|
|
}
|
|
|
|
InputKey getTouch(int id) {
|
|
for (int i = 0; i < COUNT(touch); i++)
|
|
if (down[ikTouchA + i] && touch[i].id == id)
|
|
return InputKey(ikTouchA + i);
|
|
|
|
for (int i = 0; i < COUNT(touch); i++)
|
|
if (!down[ikTouchA + i]) {
|
|
touch[i].id = id;
|
|
return InputKey(ikTouchA + i);
|
|
}
|
|
|
|
return ikNone;
|
|
}
|
|
|
|
void reset() {
|
|
memset(down, 0, sizeof(down));
|
|
memset(&mouse, 0, sizeof(mouse));
|
|
memset(&joy, 0, sizeof(joy));
|
|
memset(&touch, 0, sizeof(touch));
|
|
hmd.reset();
|
|
}
|
|
|
|
void init() {
|
|
reset();
|
|
hmd.ready = false;
|
|
hmd.zero = vec3(INF, INF, INF);
|
|
touchTimerVis = 0.0f;
|
|
touchTimerTap = 0.0f;
|
|
doubleTap = false;
|
|
touchKey[zMove] = touchKey[zLook] = touchKey[zButton] = ikNone;
|
|
memset(btnEnable, 1, sizeof(btnEnable));
|
|
}
|
|
|
|
bool checkTouchZone(TouchZone zone) {
|
|
InputKey &t = touchKey[zone];
|
|
if (t != ikNone && !down[t]) {
|
|
t = ikNone;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void getTouchDir(InputKey key, vec2 &dir) {
|
|
vec2 delta = vec2(0.0f);
|
|
if (key == ikNone)
|
|
return;
|
|
|
|
Touch &t = touch[key - ikTouchA];
|
|
vec2 d = t.pos - t.start;
|
|
float len = d.length();
|
|
if (len > EPS)
|
|
delta = d * (min(len / 100.0f, 1.0f) / len);
|
|
|
|
dir = delta;
|
|
}
|
|
|
|
void setState(int playerIndex, ControlKey key, bool down) {
|
|
if (down && !state[playerIndex][key])
|
|
lastState[playerIndex] = key;
|
|
state[playerIndex][key] = down;
|
|
}
|
|
|
|
void update() {
|
|
bool newState[MAX_PLAYERS][cMAX];
|
|
|
|
for (int j = 0; j < COUNT(Core::settings.controls); j++) {
|
|
lastState[j] = cMAX;
|
|
Core::Settings::Controls &ctrl = Core::settings.controls[j];
|
|
for (int i = 0; i < cMAX; i++) {
|
|
KeySet &c = ctrl.keys[i];
|
|
newState[j][i] = (c.key != ikNone && down[c.key]) || (c.joy != jkNone && joy[ctrl.joyIndex].down[c.joy]) || hmd.state[i];
|
|
}
|
|
}
|
|
|
|
// update touch controls
|
|
if (touchTimerTap > 0.0f)
|
|
touchTimerTap = max(0.0f, touchTimerTap - Core::deltaTime);
|
|
|
|
if (touchKey[zMove] != ikNone || touchKey[zLook] != ikNone || touchKey[zButton] != ikNone)
|
|
touchTimerVis = 30.0f;
|
|
else
|
|
if (touchTimerVis > 0.0f)
|
|
touchTimerVis = max(0.0f, touchTimerVis - Core::deltaTime);
|
|
|
|
// update buttons
|
|
float offset = Core::height * 0.25f;
|
|
float radius = offset;
|
|
vec2 center = vec2(Core::width - offset * 0.7f, Core::height - offset * 0.7f);
|
|
|
|
btnRadius = Core::height * (25.0f / 1080.0f);
|
|
btnPos[bWeapon] = center;
|
|
btnPos[bJump] = center + vec2(cosf(-PI * 0.5f), sinf(-PI * 0.5f)) * radius;
|
|
btnPos[bAction] = center + vec2(cosf(-PI * 3.0f / 4.0f), sinf(-PI * 3.0f / 4.0f)) * radius;
|
|
btnPos[bWalk] = center + vec2(cosf(-PI), sinf(-PI)) * radius;
|
|
btnPos[bInventory] = vec2(Core::width - btnRadius * 8.0f, btnRadius * 4.0f);
|
|
|
|
// touch update
|
|
Joystick &joy = Input::joy[Core::settings.controls[0].joyIndex];
|
|
|
|
if (checkTouchZone(zMove))
|
|
joy.L = vec2(0.0f);
|
|
|
|
if (checkTouchZone(zLook))
|
|
joy.L = vec2(0.0f);
|
|
|
|
if (checkTouchZone(zButton))
|
|
btn = bMove; // no active buttons == bNone
|
|
|
|
if (doubleTap)
|
|
doubleTap = false;
|
|
|
|
float zoneSize = Core::width / 3.0f;
|
|
|
|
for (int i = 0; i < COUNT(touch); i++) {
|
|
InputKey key = InputKey(i + ikTouchA);
|
|
|
|
if (!down[key]) continue;
|
|
if (key == touchKey[zMove] || key == touchKey[zLook] || key == touchKey[zButton]) continue;
|
|
|
|
int zone = clamp(int(touch[i].pos.x / zoneSize), 0, 2);
|
|
InputKey &t = touchKey[zone];
|
|
|
|
if (t == ikNone) {
|
|
t = key;
|
|
if (zone == zMove) {
|
|
if (touchTimerTap > 0.0f && touchTimerTap < 0.3f) { // 100 ms gap to reduce false positives (bad touch sensors)
|
|
doubleTap = true;
|
|
touchTimerTap = 0.0f;
|
|
} else
|
|
touchTimerTap = 0.3f;
|
|
}
|
|
}
|
|
}
|
|
|
|
newState[0][cLook] |= touchKey[zLook] != ikNone;
|
|
|
|
// set active touches as gamepad controls
|
|
getTouchDir(touchKey[zMove], joy.L);
|
|
getTouchDir(touchKey[zLook], joy.R);
|
|
|
|
if (touchKey[zButton] != ikNone) {
|
|
vec2 pos = touch[touchKey[zButton] - ikTouchA].pos;
|
|
|
|
btn = bMAX;
|
|
float minDist = btnRadius * 8.0f;
|
|
for (int i = 0; i < bMAX; i++)
|
|
if (btnEnable[i]) {
|
|
float d = (pos - btnPos[i]).length();
|
|
if (d < minDist) {
|
|
minDist = d;
|
|
btn = TouchButton(i);
|
|
}
|
|
}
|
|
|
|
switch (btn) {
|
|
case bWeapon : newState[0][cWeapon ] = true; break;
|
|
case bWalk : newState[0][cWalk ] = true; break;
|
|
case bAction : newState[0][cAction ] = true; break;
|
|
case bJump : newState[0][cJump ] = true; break;
|
|
case bInventory : newState[0][cInventory] = true; break;
|
|
default : ;
|
|
}
|
|
}
|
|
|
|
if (doubleTap)
|
|
newState[0][cRoll] = true;
|
|
|
|
for (int j = 0; j < COUNT(Core::settings.controls); j++)
|
|
for (int i = 0; i < cMAX; i++)
|
|
setState(j, ControlKey(i), newState[j][i]);
|
|
}
|
|
}
|
|
|
|
#endif
|