mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-07 05:37:01 +02:00
#15 gamepad support on web platform, PSX-like Lara controls
This commit is contained in:
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
@@ -225,6 +225,9 @@ struct Camera : Controller {
|
||||
angleAdv.y += delta.x * 0.01f;
|
||||
Input::mouse.start.R = Input::mouse.pos;
|
||||
}
|
||||
|
||||
angleAdv.x -= Input::joy.L.y * 2.0f * Core::deltaTime;
|
||||
angleAdv.y += Input::joy.L.x * 2.0f * Core::deltaTime;
|
||||
|
||||
angle = owner->angle + angleAdv;
|
||||
angle.z = 0.0f;
|
||||
|
@@ -303,7 +303,7 @@ namespace Debug {
|
||||
|
||||
vec3 v = ((Controller*)level.entities[0].controller)->getDir();
|
||||
vec3 p = v - n * n.dot(v);
|
||||
vec3 c = b + p.normal() * 256.0f;
|
||||
vec3 d = b + p.normal() * 256.0f;
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1, 1, 1);
|
||||
@@ -312,7 +312,7 @@ namespace Debug {
|
||||
|
||||
glColor3f(1, 1, 0);
|
||||
glVertex3fv((GLfloat*)&b);
|
||||
glVertex3fv((GLfloat*)&c);
|
||||
glVertex3fv((GLfloat*)&d);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
27
src/input.h
27
src/input.h
@@ -14,7 +14,7 @@ enum InputKey { ikNone,
|
||||
// touch
|
||||
ikTouchA, ikTouchB,
|
||||
// gamepad
|
||||
ikJoyA, ikJoyB, ikJoyX, ikJoyY, ikJoyLB, ikJoyRB, ikJoyL, ikJoyR, ikJoySelect, ikJoyStart, ikJoyLT, ikJoyRT, ikJoyDP,
|
||||
ikJoyA, ikJoyB, ikJoyX, ikJoyY, ikJoyLB, ikJoyRB, ikJoyL, ikJoyR, ikJoySelect, ikJoyStart, ikJoyLT, ikJoyRT, ikJoyPOV,
|
||||
ikMAX };
|
||||
|
||||
namespace Input {
|
||||
@@ -30,7 +30,8 @@ namespace Input {
|
||||
|
||||
struct {
|
||||
vec2 L, R;
|
||||
float LT, RT, DP;
|
||||
float LT, RT;
|
||||
int POV;
|
||||
} joy;
|
||||
|
||||
struct {
|
||||
@@ -58,10 +59,9 @@ namespace Input {
|
||||
case ikMouseR : mouse.start.R = mouse.pos; break;
|
||||
case ikMouseM : mouse.start.M = mouse.pos; break;
|
||||
case ikTouchA : touch.start.A = touch.A; break;
|
||||
case ikTouchB : touch.start.B = touch.B; break;
|
||||
case ikTouchB : touch.start.B = touch.B; break;
|
||||
default : ;
|
||||
}
|
||||
|
||||
down[key] = value;
|
||||
}
|
||||
|
||||
@@ -69,16 +69,17 @@ namespace Input {
|
||||
switch (key) {
|
||||
case ikMouseL :
|
||||
case ikMouseR :
|
||||
case ikMouseM : mouse.pos = pos; break;
|
||||
case ikJoyL : joy.L = pos; break;
|
||||
case ikJoyR : joy.R = pos; break;
|
||||
case ikJoyLT : joy.LT = pos.x; break;
|
||||
case ikJoyRT : joy.RT = pos.x; break;
|
||||
case ikJoyDP : joy.DP = pos.x; break;
|
||||
case ikTouchA : touch.A = pos; break;
|
||||
case ikTouchB : touch.B = pos; break;
|
||||
default : ;
|
||||
case ikMouseM : mouse.pos = pos; return;
|
||||
case ikJoyL : joy.L = pos; return;
|
||||
case ikJoyR : joy.R = pos; return;
|
||||
case ikTouchA : touch.A = pos; return;
|
||||
case ikTouchB : touch.B = pos; return;
|
||||
case ikJoyLT : joy.LT = pos.x; break;
|
||||
case ikJoyRT : joy.RT = pos.x; break;
|
||||
case ikJoyPOV : joy.POV = (int)pos.x; break;
|
||||
default : return;
|
||||
}
|
||||
setDown(key, pos.x > 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
18
src/lara.h
18
src/lara.h
@@ -359,7 +359,7 @@ struct Lara : Controller {
|
||||
TR::Level::FloorInfo info;
|
||||
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info);
|
||||
|
||||
if (abs(info.floor - (p.y - 768.0f + 64.0f)) < 32) {
|
||||
if (abs(int(info.floor - (p.y - 768.0f + 64.0f))) < 32) {
|
||||
turnToWall();
|
||||
pos = pos - getDir() * 128.0f; // TODO: collision wall offset
|
||||
pos.y = info.floor + 768.0f - 64.0f;
|
||||
@@ -556,18 +556,23 @@ struct Lara : Controller {
|
||||
case STAND_HANG : return STATE_HANG;
|
||||
case STAND_ONWATER : return STATE_SURF_TREAD;
|
||||
case STAND_UNDERWATER : return STATE_TREAD;
|
||||
default : ;
|
||||
}
|
||||
return STATE_FALL;
|
||||
}
|
||||
|
||||
virtual int getInputMask() {
|
||||
mask = 0;
|
||||
if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH;
|
||||
if (Input::down[ikS] || Input::joy.L.y > 0) mask |= BACK;
|
||||
if (Input::down[ikA] || Input::joy.L.x < 0) mask |= LEFT;
|
||||
if (Input::down[ikD] || Input::joy.L.x > 0) mask |= RIGHT;
|
||||
int &p = Input::joy.POV;
|
||||
if (Input::down[ikW] || p == 8 || p == 1 || p == 2) mask |= FORTH;
|
||||
if (Input::down[ikD] || p == 2 || p == 3 || p == 4) mask |= RIGHT;
|
||||
if (Input::down[ikS] || p == 4 || p == 5 || p == 6) mask |= BACK;
|
||||
if (Input::down[ikA] || p == 6 || p == 7 || p == 8) mask |= LEFT;
|
||||
if (Input::down[ikJoyB]) mask = FORTH | BACK; // roll
|
||||
if (Input::down[ikJoyRT]) mask = WALK | RIGHT; // step right
|
||||
if (Input::down[ikJoyLT]) mask = WALK | LEFT; // step left
|
||||
if (Input::down[ikSpace] || Input::down[ikJoyX]) mask |= JUMP;
|
||||
if (Input::down[ikShift] || Input::down[ikJoyLT]) mask |= WALK;
|
||||
if (Input::down[ikShift] || Input::down[ikJoyLB]) mask |= WALK;
|
||||
if (Input::down[ikE] || Input::down[ikMouseL] || Input::down[ikJoyA]) mask |= ACTION;
|
||||
if (Input::down[ikQ] || Input::down[ikMouseR] || Input::down[ikJoyY]) mask |= WEAPON;
|
||||
if (health <= 0) mask = DEATH;
|
||||
@@ -839,7 +844,6 @@ struct Lara : Controller {
|
||||
pos.y += DESCENT_SPEED * Core::deltaTime;
|
||||
}
|
||||
|
||||
|
||||
updateEntity();
|
||||
}
|
||||
}
|
||||
|
@@ -1022,11 +1022,10 @@ static float float32_unpack(uint32 x)
|
||||
}
|
||||
*/
|
||||
float ldexpi(int m, int e) {
|
||||
return m * pow(2, e);
|
||||
return (float)(m * pow(2, e));
|
||||
}
|
||||
|
||||
static float float32_unpack(uint32 x)
|
||||
{
|
||||
static float float32_unpack(uint32 x) {
|
||||
// from the specification
|
||||
uint32 s = x & 0x80000000;
|
||||
int32 m = x & 0x1fffff;
|
||||
@@ -1223,7 +1222,7 @@ static int lookup1_values(int a, int b)
|
||||
int r = 0, p = 0;
|
||||
do {
|
||||
r++;
|
||||
p = pow(r, b);
|
||||
p = (int)pow(r, b);
|
||||
} while (p <= a);
|
||||
return r - 1;
|
||||
}
|
||||
|
@@ -1,2 +0,0 @@
|
||||
clang++ -std=c++11 -Os -s -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -Wl,--gc-sections -DNDEBUG main.cpp ../libs/stb_vorbis/stb_vorbis.c -I../ -o../../bin/OpenLara -lX11 -lGL -lm -lpthread -lpulse-simple -lpulse
|
||||
strip ../../bin/OpenLara --strip-all --remove-section=.comment --remove-section=.note
|
2
src/platform/nix/build.sh
Executable file
2
src/platform/nix/build.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
clang++ -std=c++11 -Os -s -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -Wl,--gc-sections -DNDEBUG main.cpp ../../libs/stb_vorbis/stb_vorbis.c -I../../ -o../../../bin/OpenLara -lX11 -lGL -lm -lpthread -lpulse-simple -lpulse
|
||||
strip ../../../bin/OpenLara --strip-all --remove-section=.comment --remove-section=.note
|
@@ -1,4 +1,6 @@
|
||||
sudo apt-get install git clang libx11-dev libgl1-mesa-dev libpulse-dev
|
||||
git clone https://github.com/XProger/OpenLara
|
||||
cd OpenLara/src/nix
|
||||
cd OpenLara/src/platform/nix
|
||||
./build.sh
|
||||
cd ../../../bin/
|
||||
./OpenLara
|
9
src/platform/web/build.bat
Normal file
9
src/platform/web/build.bat
Normal file
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
cls
|
||||
set SRC=main.cpp
|
||||
set PROJ=OpenLara
|
||||
set FLAGS=-O3 -Wno-deprecated-register --llvm-opts 2 -std=c++11 -I../../
|
||||
set PRELOAD=./LEVEL2_DEMO.PHD
|
||||
echo.
|
||||
call em++ %SRC% %FLAGS% -o %PROJ%.js --preload-file %PRELOAD%
|
||||
gzip.exe -9 -f %PROJ%.data %PROJ%.js %PROJ%.js.mem
|
@@ -18,7 +18,72 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
|
||||
InputKey joyToInputKey(int code) {
|
||||
static const int codes[] = { 0, 1, 2, 3, 4, 5, 10, 11, 8, 9, 6, 7 };
|
||||
|
||||
for (int i = 0; i < sizeof(codes) / sizeof(codes[0]); i++)
|
||||
if (codes[i] == code)
|
||||
return (InputKey)(ikJoyA + i);
|
||||
|
||||
return ikNone;
|
||||
}
|
||||
|
||||
int joyGetPOV(int mask) {
|
||||
switch (mask) {
|
||||
case 0b0001 : return 1;
|
||||
case 0b1001 : return 2;
|
||||
case 0b1000 : return 3;
|
||||
case 0b1010 : return 4;
|
||||
case 0b0010 : return 5;
|
||||
case 0b0110 : return 6;
|
||||
case 0b0100 : return 7;
|
||||
case 0b0101 : return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define JOY_DEAD_ZONE_STICK 0.3f
|
||||
#define JOY_DEAD_ZONE_TRIGGER 0.01f
|
||||
|
||||
vec2 joyAxis(float x, float y) {
|
||||
if (fabsf(x) > JOY_DEAD_ZONE_STICK || fabsf(y) > JOY_DEAD_ZONE_STICK)
|
||||
return vec2(x, y);
|
||||
return vec2(0.0f);
|
||||
}
|
||||
|
||||
vec2 joyTrigger(float x) {
|
||||
return vec2(x > JOY_DEAD_ZONE_TRIGGER ? x : 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void joyUpdate() {
|
||||
int count = emscripten_get_num_gamepads();
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
EmscriptenGamepadEvent state;
|
||||
if (emscripten_get_gamepad_status(0, &state) != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < max(state.numButtons, 12); i++) {
|
||||
InputKey key = joyToInputKey(i);
|
||||
Input::setDown(key, state.digitalButton[i]);
|
||||
if (key == ikJoyLT || key == ikJoyRT)
|
||||
Input::setPos(key, joyTrigger(state.analogButton[i]));
|
||||
}
|
||||
|
||||
if (state.numButtons > 15) { // get POV
|
||||
auto &b = state.digitalButton;
|
||||
int pov = joyGetPOV(b[12] | (b[13] << 1) | (b[14] << 2) | (b[15] << 3));
|
||||
Input::setPos(ikJoyPOV, vec2((float)pov, 0.0f));
|
||||
}
|
||||
|
||||
if (state.numAxes > 1) Input::setPos(ikJoyL, joyAxis(state.axis[0], state.axis[1]));
|
||||
if (state.numAxes > 3) Input::setPos(ikJoyR, joyAxis(state.axis[2], state.axis[3]));
|
||||
}
|
||||
|
||||
void main_loop() {
|
||||
joyUpdate();
|
||||
|
||||
int time = getTime();
|
||||
|
||||
if (time - lastTime <= 0)
|
||||
@@ -82,7 +147,7 @@ void freeGL() {
|
||||
}
|
||||
|
||||
InputKey keyToInputKey(int code) {
|
||||
int codes[] = {
|
||||
static const int codes[] = {
|
||||
0x25, 0x27, 0x26, 0x28, 0x20, 0x0D, 0x1B, 0x10, 0x11, 0x12,
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
@@ -41,14 +41,14 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>..\..\bin\</OutDir>
|
||||
<IncludePath>..\;$(VC_IncludePath);$(WindowsSdk_71A_IncludePath);</IncludePath>
|
||||
<OutDir>..\..\..\bin\</OutDir>
|
||||
<IncludePath>..\..\;$(VC_IncludePath);$(WindowsSdk_71A_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>..\..\bin\</OutDir>
|
||||
<OutDir>..\..\..\bin\</OutDir>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<IncludePath>..\;$(VC_IncludePath);$(WindowsSdk_71A_IncludePath);</IncludePath>
|
||||
<IncludePath>..\..\;$(VC_IncludePath);$(WindowsSdk_71A_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -97,32 +97,32 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\libs\minimp3\minimp3.cpp" />
|
||||
<ClCompile Include="..\libs\stb_vorbis\stb_vorbis.c" />
|
||||
<ClCompile Include="..\..\libs\minimp3\minimp3.cpp" />
|
||||
<ClCompile Include="..\..\libs\stb_vorbis\stb_vorbis.c" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\camera.h" />
|
||||
<ClInclude Include="..\controller.h" />
|
||||
<ClInclude Include="..\core.h" />
|
||||
<ClInclude Include="..\debug.h" />
|
||||
<ClInclude Include="..\enemy.h" />
|
||||
<ClInclude Include="..\game.h" />
|
||||
<ClInclude Include="..\input.h" />
|
||||
<ClInclude Include="..\lara.h" />
|
||||
<ClInclude Include="..\level.h" />
|
||||
<ClInclude Include="..\libs\minimp3\libc.h" />
|
||||
<ClInclude Include="..\libs\minimp3\minimp3.h" />
|
||||
<ClInclude Include="..\mesh.h" />
|
||||
<ClInclude Include="..\shader.h" />
|
||||
<ClInclude Include="..\sound.h" />
|
||||
<ClInclude Include="..\texture.h" />
|
||||
<ClInclude Include="..\format.h" />
|
||||
<ClInclude Include="..\trigger.h" />
|
||||
<ClInclude Include="..\utils.h" />
|
||||
<ClInclude Include="..\..\camera.h" />
|
||||
<ClInclude Include="..\..\controller.h" />
|
||||
<ClInclude Include="..\..\core.h" />
|
||||
<ClInclude Include="..\..\debug.h" />
|
||||
<ClInclude Include="..\..\enemy.h" />
|
||||
<ClInclude Include="..\..\game.h" />
|
||||
<ClInclude Include="..\..\input.h" />
|
||||
<ClInclude Include="..\..\lara.h" />
|
||||
<ClInclude Include="..\..\level.h" />
|
||||
<ClInclude Include="..\..\libs\minimp3\libc.h" />
|
||||
<ClInclude Include="..\..\libs\minimp3\minimp3.h" />
|
||||
<ClInclude Include="..\..\mesh.h" />
|
||||
<ClInclude Include="..\..\shader.h" />
|
||||
<ClInclude Include="..\..\sound.h" />
|
||||
<ClInclude Include="..\..\texture.h" />
|
||||
<ClInclude Include="..\..\format.h" />
|
||||
<ClInclude Include="..\..\trigger.h" />
|
||||
<ClInclude Include="..\..\utils.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\shader.glsl" />
|
||||
<None Include="..\..\shader.glsl" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
@@ -27,7 +27,7 @@ DWORD getTime() {
|
||||
|
||||
// common input functions
|
||||
InputKey keyToInputKey(int code) {
|
||||
int codes[] = {
|
||||
static const int codes[] = {
|
||||
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_SPACE, VK_RETURN, VK_ESCAPE, VK_SHIFT, VK_CONTROL, VK_MENU,
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
@@ -61,7 +61,7 @@ void joyInit() {
|
||||
void joyFree() {
|
||||
joyReady = false;
|
||||
memset(&Input::joy, 0, sizeof(Input::joy));
|
||||
for (int ik = ikJoyA; ik <= ikJoyDP; ik++)
|
||||
for (int ik = ikJoyA; ik <= ikJoyPOV; ik++)
|
||||
Input::down[ik] = false;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ float joyAxis(int x, int xMin, int xMax) {
|
||||
vec2 joyDir(float ax, float ay) {
|
||||
vec2 dir = vec2(ax, ay);
|
||||
float dist = min(1.0f, dir.length());
|
||||
if (dist < JOY_DEAD_ZONE_STICK) dist = 0;
|
||||
if (dist < JOY_DEAD_ZONE_STICK) dist = 0.0f;
|
||||
|
||||
return dir.normal() * dist;
|
||||
}
|
||||
@@ -96,13 +96,22 @@ void joyUpdate() {
|
||||
joyAxis(info.dwRpos, caps.wRmin, caps.wRmax)));
|
||||
|
||||
if (caps.wCaps & JOYCAPS_HASZ) {
|
||||
float z = joyAxis(info.dwZpos, caps.wZmin, caps.wZmax);
|
||||
if (fabsf(z) > JOY_DEAD_ZONE_TRIGGER)
|
||||
Input::setPos(z > 0.0f ? ikJoyLT : ikJoyRT, vec2(fabsf(z), 0.0f));
|
||||
float z = joyAxis(info.dwZpos, caps.wZmin, caps.wZmax);
|
||||
InputKey key = z > JOY_DEAD_ZONE_TRIGGER ? ikJoyLT : (z < -JOY_DEAD_ZONE_TRIGGER ? ikJoyRT : ikNone);
|
||||
if (key != ikNone) {
|
||||
Input::setPos(key, vec2(fabsf(z), 0.0f));
|
||||
Input::setPos(key == ikJoyLT ? ikJoyRT : ikJoyLT, vec2(0.0f)); // release opposite trigger
|
||||
} else {
|
||||
Input::setPos(ikJoyLT, vec2(0.0f));
|
||||
Input::setPos(ikJoyRT, vec2(0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (caps.wCaps & JOYCAPS_HASPOV && info.dwPOV != JOY_POVCENTERED)
|
||||
Input::setPos(ikJoyDP, vec2((float)(1 + info.dwPOV / 4500), 0));
|
||||
if (caps.wCaps & JOYCAPS_HASPOV)
|
||||
if (info.dwPOV == JOY_POVCENTERED)
|
||||
Input::setPos(ikJoyPOV, vec2(0.0f));
|
||||
else
|
||||
Input::setPos(ikJoyPOV, vec2(float(1 + info.dwPOV / 4500), 0.0f));
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
Input::setDown((InputKey)(ikJoyA + i), (info.dwButtons & (1 << i)) > 0);
|
@@ -1,3 +0,0 @@
|
||||
set SRC=main.cpp
|
||||
call em++ %SRC% -O2 -s ASSERTIONS=1 -Wno-deprecated-register --llvm-opts 2 --closure 1 -std=c++11 -o OpenLara.js --preload-file ./LEVEL2_DEMO.PHD -I..\
|
||||
gzip.exe -9 -f OpenLara.data OpenLara.js OpenLara.js.mem
|
Reference in New Issue
Block a user