Add demo support

This commit is contained in:
Miloslav Číž 2020-10-18 11:36:38 +02:00
parent a611bc84ff
commit f4dec90ff5
4 changed files with 353 additions and 68 deletions

9
game.h
View File

@ -71,6 +71,13 @@
#define SFG_CPU_LOAD(percent) {} ///< Can be redefined to check CPU load in %.
#endif
#ifndef SFG_GAME_STEP_COMMAND
#define SFG_GAME_STEP_COMMAND {} /**< Will be called each simlation step (good
for creating deterministic behavior such as
demos (main loop iteration calls potentially
multiple simulation steps). */
#endif
/**
Returns 1 (0) if given key is pressed (not pressed). At least the mandatory
keys have to be implemented, the optional keys don't have to ever return 1.
@ -3803,6 +3810,8 @@ void SFG_gameStepMenu()
*/
void SFG_gameStep()
{
SFG_GAME_STEP_COMMAND
SFG_game.soundsPlayedThisFrame = 0;
SFG_game.blink = (SFG_game.frame / SFG_BLINK_PERIOD_FRAMES) % 2;

View File

@ -5,6 +5,10 @@
compile a native executable or a transpiled JS browser version with
emscripten.
This front end is pretty feature rich, allowing testing etc. A much more
minimal could be made. If you want a learning example of front end, look at
another, simpler one, e.g. the terminal.
To compile with emscripten run:
emcc ./main_sdl.c -s USE_SDL=2 -O3 --shell-file HTMLshell.html -o game.html
@ -21,6 +25,35 @@
#define SFG_OS_IS_MALWARE 1
#endif
#define DEMO 0 // 0: SDL input, 1: smallinput rec demo, 2: smallinput play demo
#if DEMO
/*
Demo support is very basic and isn't mean for users, it is there for
programmers and devs (testing, recording trailer footage, ...). Normally
SDL's multiplatform input system is used, with demos enables smallinput
library, which can record and play back input, is used (but it only works
with Linux, requires root and doesn't support so many things, e.g. wheel).
*/
#include "smallinput.h"
#define SFG_GAME_STEP_COMMAND input_update();
#if DEMO == 1
#define DEMO_MAX_LENGTH 1000000
uint8_t demoRecord[DEMO_MAX_LENGTH];
#else // DEMO == 2
uint8_t demoRecord[] =
{ // paste demo record here
// #include "demo.dem"
67,0,0,0,1,83,72,0,0,0,2,83,79,0,0,0,1,83,81,0,0,0,2,83,87,0,0,0,1,83,92,0,
0,0,2,83,116,0,0,0,1,87,120,0,0,0,2,87,126,0,0,0,1,87,130,0,0,0,2,87,148,0,0,0,
1,83,151,0,0,0,2,83,157,0,0,0,1,83,161,0,0,0,2,83,166,0,0,0,1,83,171,0,0,0,2,83,
201,0,0,0,1,71,255,255,255,255,255,
};
#endif
#endif
// #define SFG_START_LEVEL 1
#define SFG_IMMORTAL 1
#define SFG_UNLOCK_DOOR 1
@ -42,7 +75,7 @@
#define SFG_BACKGROUND_BLUR 1
#else
// lower quality
#define SFG_FPS 35
#define SFG_FPS 30
#define SFG_SCREEN_RESOLUTION_X 640
#define SFG_SCREEN_RESOLUTION_Y 480
#define SFG_RAYCASTING_SUBSAMPLE 2
@ -168,6 +201,11 @@ int8_t mouseMoved = 0; /* Whether the mouse has moved since program started,
void SFG_getMouseOffset(int16_t *x, int16_t *y)
{
#if DEMO
// For some reason mouse doesn't desyncs, so don't allow it for demos.
return;
#endif
#ifndef __EMSCRIPTEN__
if (mouseMoved)
{
@ -191,6 +229,32 @@ void SFG_processEvent(uint8_t event, uint8_t data)
int8_t SFG_keyPressed(uint8_t key)
{
#if DEMO
switch (key)
{
case SFG_KEY_UP: return input_getKey('w') ||
input_getKey(SMALLINPUT_ARROW_UP); break;
case SFG_KEY_RIGHT: return input_getKey('e') ||
input_getKey(SMALLINPUT_ARROW_RIGHT); break;
case SFG_KEY_DOWN: return input_getKey('s') ||
input_getKey(SMALLINPUT_ARROW_DOWN); break;
case SFG_KEY_LEFT: return input_getKey('q') ||
input_getKey(SMALLINPUT_ARROW_LEFT); break;
case SFG_KEY_A: return input_getKey('g') || input_getKey(SMALLINPUT_RETURN); break;
case SFG_KEY_B: return input_getKey('h') || input_getKey(SMALLINPUT_MOUSE_L); break;
case SFG_KEY_C: return input_getKey('j'); break;
case SFG_KEY_JUMP: return input_getKey(' '); break;
case SFG_KEY_STRAFE_LEFT: return input_getKey('a'); break;
case SFG_KEY_STRAFE_RIGHT: return input_getKey('d'); break;
case SFG_KEY_MAP: return input_getKey(SMALLINPUT_TAB); break;
case SFG_KEY_TOGGLE_FREELOOK: return input_getKey(SMALLINPUT_MOUSE_R); break;
case SFG_KEY_MENU: return input_getKey(SMALLINPUT_ESCAPE); break;
case SFG_KEY_PREVIOUS_WEAPON: return input_getKey('o'); break;
case SFG_KEY_NEXT_WEAPON: return input_getKey('p'); break;
default: return 0;
}
#endif
if (webKeyboardState[key])
return 1;
@ -308,6 +372,7 @@ void mainLoopIteration()
SDL_PauseAudio(0);
#endif
#if !DEMO
while (SDL_PollEvent(&event)) // also automatically updates sdlKeyboardState
{
if (event.type == SDL_MOUSEWHEEL)
@ -322,6 +387,7 @@ void mainLoopIteration()
else if (event.type == SDL_MOUSEMOTION)
mouseMoved = 1;
}
#endif
sdlMouseButtonState = SDL_GetMouseState(NULL,NULL);
@ -497,10 +563,18 @@ int main(int argc, char *argv[])
SFG_init();
#if DEMO == 0
SDL_PumpEvents();
SDL_WarpMouseInWindow(window,
SFG_SCREEN_RESOLUTION_X / 2, SFG_SCREEN_RESOLUTION_Y / 2);
#elif DEMO == 1
if (!input_init(SMALLINPUT_MODE_RECORD,demoRecord,DEMO_MAX_LENGTH))
return 1;
#else // DEMO == 2
if (!input_init(SMALLINPUT_MODE_PLAY,demoRecord,sizeof(demoRecord)))
return 1;
#endif
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(mainLoopIteration,0,1);
@ -519,5 +593,14 @@ int main(int argc, char *argv[])
puts("SDL: ending");
#if DEMO
input_end();
#if DEMO == 1
puts("recorded demo:");
input_printRecord();
#endif
#endif
return 0;
}

View File

@ -99,12 +99,16 @@ int8_t SFG_keyPressed(uint8_t key)
case SFG_KEY_RIGHT: return input_getKey('d') || input_getKey(SMALLINPUT_ARROW_RIGHT); break;
case SFG_KEY_DOWN: return input_getKey('s') || input_getKey(SMALLINPUT_ARROW_DOWN); break;
case SFG_KEY_LEFT: return input_getKey('a') || input_getKey(SMALLINPUT_ARROW_LEFT); break;
case SFG_KEY_A: return input_getKey('g'); break;
case SFG_KEY_B: return input_getKey('h') || input_getKey(SMALLINPUT_MOUSE_L); break;
case SFG_KEY_A: return input_getKey('g') || input_getKey(SMALLINPUT_RETURN); break;
case SFG_KEY_B: return input_getKey('h') || input_getKey(SMALLINPUT_MOUSE_L) || input_getKey(SMALLINPUT_CTRL); break;
case SFG_KEY_C: return input_getKey('j'); break;
case SFG_KEY_MAP: return input_getKey(SMALLINPUT_TAB); break;
case SFG_KEY_JUMP: return input_getKey(' '); break;
default: return 0; break;
case SFG_KEY_MENU: return input_getKey(SMALLINPUT_ESCAPE); break;
case SFG_KEY_NEXT_WEAPON: return input_getKey('2'); break;
case SFG_KEY_PREVIOUS_WEAPON: return input_getKey('1'); break;
case SFG_KEY_TOGGLE_FREELOOK: return input_getKey(SMALLINPUT_MOUSE_R); break;
default: return 0; breakdwwwwwwawwwwwwwwwwwwawwwwwwwwwwwsswswswsdwswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwsswwwwwwwwwd
}
}
@ -132,7 +136,7 @@ int main()
timeStart = getTime();
input_init();
input_init(SMALLINPUT_MODE_NORMAL,0,0);
SFG_init();
screen[SCREENSIZE - 1] = 0; // string terminator

View File

@ -13,12 +13,13 @@
#define _SMALLINPUT_H
#include <stdint.h>
#include <stdio.h>
#define SMALLINPUT_MODE_NORMAL 0 ///< Only handle input.
#define SMALLINPUT_MODE_RECORD 1 ///< Handle input and record it.
#define SMALLINPUT_MODE_PLAY 2 ///< Play back recorded input.
#define SMALLINPUT_KEY_NONE 0
#define SMALLINPUT_ARROW_UP 128
#define SMALLINPUT_ARROW_RIGHT 129
#define SMALLINPUT_ARROW_DOWN 130
#define SMALLINPUT_ARROW_LEFT 131
#define SMALLINPUT_SPACE ' '
#define SMALLINPUT_BACKSPACE 8
#define SMALLINPUT_TAB 9
@ -26,13 +27,45 @@
#define SMALLINPUT_SHIFT 14
#define SMALLINPUT_ESCAPE 27
#define SMALLINPUT_DELETE 127
#define SMALLINPUT_ARROW_UP 128
#define SMALLINPUT_ARROW_RIGHT 129
#define SMALLINPUT_ARROW_DOWN 130
#define SMALLINPUT_ARROW_LEFT 131
#define SMALLINPUT_F1 132
#define SMALLINPUT_F2 133
#define SMALLINPUT_F3 134
#define SMALLINPUT_F4 135
#define SMALLINPUT_F5 136
#define SMALLINPUT_F6 137
#define SMALLINPUT_F7 138
#define SMALLINPUT_F8 139
#define SMALLINPUT_F9 140
#define SMALLINPUT_F10 141
#define SMALLINPUT_F11 142
#define SMALLINPUT_F12 143
#define SMALLINPUT_CTRL 144
#define SMALLINPUT_MOUSE_L 253
#define SMALLINPUT_MOUSE_M 254
#define SMALLINPUT_MOUSE_R 255
#define SMALLINPUT_RECORD_KEY_DOWN 1 ///< Mouse down event, followed by code.
#define SMALLINPUT_RECORD_KEY_UP 2 ///< Moue up event, followed by code.
#define SMALLINPUT_RECORD_MOUSE_X 3 ///< Mouse x move, followed by s32 value.
#define SMALLINPUT_RECORD_MOUSE_Y 4 ///< Mouse y move, followed by s32 value.
#define SMALLINPUT_RECORD_END 255 ///< Record end, followed by 4 more same values.
uint8_t input_keyStates[256];
int32_t input_mousePosition[2];
uint8_t input_keyStatesPrevious[256];
int32_t input_mousePositionPrevious[2];
uint8_t input_mode;
uint32_t input_frame = 0;
uint8_t *input_recordData;
uint32_t input_recordPosition;
uint32_t input_recordSize;
#if 1 // TODO: add other options for input handling (SDL, xinput, ...)
/*
@ -67,14 +100,14 @@ uint32_t input_frame = 0;
#define no KEY_RESERVED
no,no,no,no,no,no,no,no,KEY_BACKSPACE,KEY_TAB,no,no,no,KEY_ENTER,KEY_LEFTSHIFT,no,
no,no,no,no,no,no,no,no,no,no,no,KEY_ESC,no,no,no,no,
KEY_SPACE,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
KEY_SPACE,no,no,no,no,no,no,no,no,no,no,no,KEY_COMMA,no,KEY_DOT,no,
KEY_0,KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,no,KEY_SEMICOLON,no,KEY_EQUAL,no,KEY_QUESTION,
no,KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K,KEY_L,KEY_M,KEY_N,KEY_O,
KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V,KEY_W,KEY_X,KEY_Y,KEY_Z,no,no,no,no,no,
no,KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K,KEY_L,KEY_M,KEY_N,KEY_O,
KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V,KEY_W,KEY_X,KEY_Y,KEY_Z,no,no,no,no,KEY_DELETE,
KEY_UP,KEY_RIGHT,KEY_DOWN,KEY_LEFT,no,no,no,no,no,no,no,no,no,no,no,no,
no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
KEY_UP,KEY_RIGHT,KEY_DOWN,KEY_LEFT,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,KEY_F11,KEY_F12,
KEY_LEFTCTRL,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,
@ -85,27 +118,91 @@ uint32_t input_frame = 0;
};
#endif
/**
Initializes the library.
*/
void input_init(void)
void input_recordU8(uint8_t value)
{
if (input_recordPosition < (input_recordSize - 1))
{
input_recordData[input_recordPosition] = value;
input_recordPosition++;
}
}
void input_recordU32(uint32_t value)
{
for (uint8_t i = 0; i < 4; ++i)
{
input_recordU8(value % 256);
value /= 256;
}
}
uint32_t input_readU32(uint8_t *data)
{
uint32_t result = 0;
for (uint8_t i = 0; i < 4; ++i)
result = result * 256 + data[3 - i];
return result;
}
/**
Initializes the library with given mode (SMALLINPUT_MODE_*).
*/
uint8_t input_init(uint8_t mode, uint8_t *recordData, uint32_t recordDataLength)
{
input_mode = mode;
input_mousePosition[0] = 0;
input_mousePosition[1] = 0;
input_mousePositionPrevious[0] = 0;
input_mousePositionPrevious[1] = 0;
input_frame = 0;
input_recordData = recordData;
input_recordPosition = 0;
input_recordSize = recordDataLength;
for (int16_t i = 0; i < 256; ++i)
{
input_keyStates[i] = 0;
input_keyStatesPrevious[i] = 0;
}
input_keyboardFile = open(INPUT_KEYBOARD_FILE, O_RDONLY);
fcntl(input_keyboardFile, F_SETFL, O_NONBLOCK);
uint8_t result = 1;
input_mouseFile = open(INPUT_MOUSE_FILE, O_RDONLY);
fcntl(input_mouseFile, F_SETFL, O_NONBLOCK);
if (input_mode != SMALLINPUT_MODE_PLAY)
{
input_keyboardFile = open(INPUT_KEYBOARD_FILE, O_RDONLY);
result = input_keyboardFile >= 0;
if (result)
{
fcntl(input_keyboardFile, F_SETFL, O_NONBLOCK);
input_mouseFile = open(INPUT_MOUSE_FILE, O_RDONLY);
result = input_mouseFile >= 0;
if (result)
fcntl(input_mouseFile, F_SETFL, O_NONBLOCK);
}
if (!result)
puts("could not open device file (are you root?)");
}
return result;
}
void input_end(void)
{
if (input_mode == SMALLINPUT_MODE_RECORD)
for (uint8_t i = 0; i < 5; ++i)
input_recordU8(SMALLINPUT_RECORD_END);
close(input_keyboardFile);
close(input_mouseFile);
}
@ -118,33 +215,76 @@ void input_update(void)
{
LinuxInputEvent event;
while (1) // keyboard
if (input_mode == SMALLINPUT_MODE_PLAY)
{
if (read(input_keyboardFile, &event, sizeof(event)) <= 0)
break;
if (event.type == EV_KEY && (event.value == 1 || event.value == 0))
for (uint16_t i = 0; i < 256; ++i)
if (event.code == input_linuxCodes[i])
{
input_keyStates[i] = event.value;
break;
}
}
while (1) // mouse
{
if (read(input_mouseFile, &event, sizeof(event)) <= 0)
break;
if (event.type == EV_REL)
input_mousePosition[event.code % 2] += event.value;
else if (event.type == EV_KEY)
while (input_recordPosition < input_recordSize)
{
input_keyStates[
event.code == BTN_LEFT ? SMALLINPUT_MOUSE_L :
(event.code == BTN_RIGHT ? SMALLINPUT_MOUSE_R : SMALLINPUT_MOUSE_M)]
= event.value;
uint32_t nextFrame = input_readU32(input_recordData + input_recordPosition);
if (input_frame >= nextFrame)
{
input_recordPosition += 4;
uint8_t rec = input_recordData[input_recordPosition];
switch (rec)
{
case SMALLINPUT_RECORD_KEY_DOWN:
case SMALLINPUT_RECORD_KEY_UP:
input_recordPosition++;
input_keyStates[input_recordData[input_recordPosition]] = rec == SMALLINPUT_RECORD_KEY_DOWN;
input_recordPosition++;
break;
case SMALLINPUT_RECORD_MOUSE_X:
case SMALLINPUT_RECORD_MOUSE_Y:
input_recordPosition++;
input_mousePosition[rec == SMALLINPUT_RECORD_MOUSE_Y] =
input_readU32(input_recordData + input_recordPosition);
input_recordPosition += 4;
break;
case SMALLINPUT_RECORD_END:
input_recordPosition = input_recordSize;
break;
default: /*printf("corrupt record\n");*/ break;
}
}
else
break;
}
}
else
{
while (1) // keyboard
{
if (read(input_keyboardFile, &event, sizeof(event)) <= 0)
break;
if (event.type == EV_KEY && (event.value == 1 || event.value == 0))
for (uint16_t i = 0; i < 256; ++i)
if (event.code == input_linuxCodes[i])
{
input_keyStates[i] = event.value;
break;
}
}
while (1) // mouse
{
if (read(input_mouseFile, &event, sizeof(event)) <= 0)
break;
if (event.type == EV_REL)
input_mousePosition[event.code % 2] += event.value;
else if (event.type == EV_KEY)
{
input_keyStates[
event.code == BTN_LEFT ? SMALLINPUT_MOUSE_L :
(event.code == BTN_RIGHT ? SMALLINPUT_MOUSE_R : SMALLINPUT_MOUSE_M)]
= event.value;
}
}
}
@ -152,6 +292,34 @@ void input_update(void)
if (input_keyStates[i] && input_keyStates[i] < 255)
input_keyStates[i]++;
if (input_mode == SMALLINPUT_MODE_RECORD)
{
for (uint8_t i = 0; i < 2; ++i) // record mouse events
if (input_mousePositionPrevious[i] != input_mousePosition[i])
{
input_recordU32(input_frame + 1);
input_recordU8((i == 0) ? SMALLINPUT_RECORD_MOUSE_X : SMALLINPUT_RECORD_MOUSE_Y);
input_recordU32(input_mousePosition[i]);
input_mousePositionPrevious[i] = input_mousePosition[i];
}
for (uint16_t i = 0; i < 256; ++i) // record key events
{
uint8_t a = input_keyStates[i] > 0;
uint8_t b = input_keyStatesPrevious[i] > 0;
if (a != b)
{
input_recordU32(input_frame + 1);
input_recordU8(a ? SMALLINPUT_RECORD_KEY_DOWN : SMALLINPUT_RECORD_KEY_UP);
input_recordU8(i);
input_keyStatesPrevious[i] = input_keyStates[i];
}
}
}
input_frame++;
}
@ -170,7 +338,7 @@ static inline uint8_t input_getKey(uint8_t key)
/**
Gets the mouse position.
*/
static inline void input_getMousePos(int16_t *x, int16_t *y)
static inline void input_getMousePos(int32_t *x, int32_t *y)
{
*x = input_mousePosition[0];
*y = input_mousePosition[1];
@ -182,34 +350,55 @@ static inline void input_setMousePos(int32_t x, int32_t y)
input_mousePosition[1] = y;
}
#if 0 // this can be used to test this
mates
#include <stdio.h>
int main(void)
/**
Prints the current input state.
*/
void input_print()
{
input_init();
printf("frame: %d\nmouse pos: %d %d",input_frame,input_mousePosition[0],input_mousePosition[1]);
while (1)
for (uint16_t i = 0; i < 256; ++i)
{
input_update();
if (i % 8 == 0)
putchar('\n');
puts("\n\n\n\n");
char c = (i > ' ' && i <= 126) ? i : '?';
for (uint16_t i = 0; i < 256; ++i)
printf("%d (%c): %d, ",i,i,input_keyStates[i]);
uint8_t n = input_getKey(i);
printf("mouse: %d %d (%d %d %d)\n",input_mousePosition[0],input_mousePosition[1],input_getKey(SMALLINPUT_MOUSE_L),input_getKey(SMALLINPUT_MOUSE_M),input_getKey(SMALLINPUT_MOUSE_R));
puts("");
usleep(10000);
printf("%s",n ? " [" : " ");
printf("%03d (\'%c\'): %03d",i,c,input_getKey(i));
printf("%s",n ? "] " : " ");
}
input_end();
putchar('\n');
}
#endif
void input_printRecord()
{
for (uint32_t i = 0; i < input_recordPosition; ++i)
{
if (i % 32 == 0)
putchar('\n');
#endif
printf("%d,",input_recordData[i]);
}
}
uint32_t input_hash()
{
uint32_t result = 0;
for (uint16_t i = 0; i < 256; ++i)
result += input_getKey(i) * (i + 1);
int32_t x, y;
input_getMousePos(&x,&y);
result += x + y << 16;
return result;
}
#endif // guard