From 42035de7447dbc9c7f0e79f4d9c659c12d6fe47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20=C4=8C=C3=AD=C5=BE?= Date: Wed, 28 Oct 2020 16:41:23 +0100 Subject: [PATCH] Imrpove frontends --- README.md | 7 +++- game.h | 10 +++--- main_csfml.c | 94 ++++++++++++++++++++++++++++++++++++++++--------- main_sdl.c | 74 ++++++++++++++++++-------------------- main_terminal.c | 17 ++++----- media/texts.txt | 1 + 6 files changed, 132 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index b32af8d..09e9f73 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,11 @@ Why go back to the 90s and not further? Early 90s is roughly right before PCs an What we perceive as good graphics is heavily dependant on what we've learned to perceive as good graphics, and it's more about aesthetics than things like resolution or polygon count. Doom looked amazing when it came out and it still does today to people who didn't let the industry teach them that good graphics equals super HD with realistic shaders requiring the latest and most expensive GPU. +### Why doesn't this have feature X? Even Doom had it. + +Though inspired by Doom, this is **NOT** Doom. Keep in mind that this isn't primarily a PC game, this is a small platform independent game that happens to also natively run on PC. Also this game's goals are different than those of commercial games, it is still to a big degree just an experimental game. It has been written by a single person who had to do the design, engine programming, game programming, tool programming, graphics, sound recording, media creation, all in spare time. +Please don't hate me. + ### Shouldn't games simply be fun? You're complicating everything with ideological bullshit when it's really just about entertainment. Games should definitely be fun to play, but they are still technology and engineering art. We have more than enough games that are trying to be just fun before everything else, but practically none putting a little more emphasis also on other aspects -- projects that don't spend all the effort on the shallow shell, but care equally about the insides and the wider context of the world they exist in. I think we need many more games like this one. @@ -178,7 +183,7 @@ Python scripts are only simple helpers for converting resources, which aren't re ### Why aren't you writing in assembly then? -Because assembly isn't portable and even a "portable assembly" (bytecode) would make it too difficult to write a game of this complexity. C is about the minimum required abstraction. +Because assembly isn't portable and even a "portable assembly" (bytecode) would make it too difficult to write a game of this complexity, and still probably wouldn't be as portable as C. C is about the minimum required abstraction. ### So I can do anything with this for free? Even like sell it and stuff? diff --git a/game.h b/game.h index 9d0adc7..4d89d4a 100755 --- a/game.h +++ b/game.h @@ -1,8 +1,8 @@ /** @file game.h - Main source file of the game that puts together all the pieces. main game - logic is implemented here. + Main source file of Anarch the game that puts together all the pieces. main + game logic is implemented here. Physics notes (you can break this when messing with constants): @@ -23,13 +23,11 @@ #ifndef _SFG_GAME_H #define _SFG_GAME_H -#include +#include // Needed for fixed width types, can easily be replaced. /* The following keys are mandatory to be implemented on any platform in order - for the game to be playable. - - Enums are bloat :) + for the game to be playable. Enums are bloat. */ #define SFG_KEY_UP 0 #define SFG_KEY_RIGHT 1 diff --git a/main_csfml.c b/main_csfml.c index db6d486..a6e6ec5 100644 --- a/main_csfml.c +++ b/main_csfml.c @@ -2,8 +2,8 @@ @file main_csfml.c This is a csfml (C binding for SFML) implementation of the game front end. - This is another alternative to the SDL for the PC. This front end is more - minimal and simple than the SDL, so it's better as a learning resource. + This is another alternative to the SDL for the PC. This front end is maybe a + little simpler than the SDL, so it's better as a learning resource. by Miloslav Ciz (drummyfish), 2020 @@ -27,6 +27,9 @@ #define SFG_DITHERED_SHADOW 1 #define SFG_DIMINISH_SPRITES 1 #define SFG_RESOLUTION_SCALEDOWN 1 +#define SFG_BACKGROUND_BLUR 1 + +#define SFG_LOG(s) printf("game: %s\n",s); #define MUSIC_VOLUME 16 @@ -58,9 +61,9 @@ int8_t SFG_keyPressed(uint8_t key) return k(S) || k(Down) || k(Num5) || k (Num2); break; case SFG_KEY_LEFT: return k(Q) || k(Left) || k(Num4); break; case SFG_KEY_A: - return k(J) || k(Return) || k(LShift) || + return k(J) || k(Return) || k(LControl) || k(RControl) || sfMouse_isButtonPressed(sfMouseLeft); break; - case SFG_KEY_B: return k(K) || k(LControl) || k(RControl); break; + case SFG_KEY_B: return k(K) || k(LShift); break; case SFG_KEY_C: return k(L); break; case SFG_KEY_JUMP: return k(Space); break; case SFG_KEY_STRAFE_LEFT: return k(A) || k(Num7); break; @@ -75,11 +78,10 @@ int8_t SFG_keyPressed(uint8_t key) if (k(P) || k(X)) return 1; - if (mouseWheelState > 0) - { - mouseWheelState--; - return 1; - } +#define checkMouse(cmp)\ + if (mouseWheelState cmp 0) { mouseWheelState = 0; return 1; } + + checkMouse(>) return 0; break; @@ -88,11 +90,9 @@ int8_t SFG_keyPressed(uint8_t key) if (k(O) || k(Y) || k(Z)) return 1; - if (mouseWheelState < 0) - { - mouseWheelState++; - return 1; - } + checkMouse(<) + +#undef checkMouse return 0; break; @@ -251,14 +251,70 @@ void soundSeek(sfTime t, void *userData) { } -int main() +uint32_t screenshotNumber = 0; + +/** + Saves a screenshot using the simple uncompressed PPM file format. +*/ +void screenshot() { + char fileName[64]; + + sprintf(fileName,"screenshot_%05d.ppm",screenshotNumber); + + FILE *f = fopen(fileName,"w"); + + if (!f) + { + puts("error: could not take screenshot"); + return; + } + + fprintf(f,"P6 %d %d 255\n",SFG_SCREEN_RESOLUTION_X,SFG_SCREEN_RESOLUTION_Y); + + for (uint32_t i = 0; i < WINDOW_SIZE; ++i) + fwrite(&windowPixels[i],1,3,f); + + puts("screenshot taken"); + + screenshotNumber++; + + fclose(f); +} + +int main(int argc, char *argv[]) +{ + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h' && argv[1][2] == 0) + { + puts("Anarch (CSFML), version " SFG_VERSION_STRING "\n"); + puts("Anarch is a unique suckless FPS game. Collect weapons and items and destroy"); + puts("robot enemies in your way in order to get to the level finish. Some door are"); + puts("locked and require access cards. Good luck!\n"); + puts("created by Miloslav \"drummyfish\" Ciz, 2020, released under CC0 1.0 (public domain)\n"); + puts("controls:\n"); + puts("- arrows, numpad, [W] [S] [A] [D] [Q] [R]: movement"); + puts("- mouse: rotation, [LMB] shoot, [RMB] toggle free look"); + puts("- [SPACE]: jump"); + puts("- [J] [RETURN] [CTRL] [LMB]: game A button (shoot, confirm)"); + puts("- [K] [SHIFT]: game B button (cancel, strafe)"); + puts("- [L]: game C button (+ down = menu, + up = jump, ...)"); + puts("- [F]: cycle next/previous weapon"); + puts("- [O] [P] [X] [Y] [Z] [mouse wheel]: change weapons"); + puts("- [TAB]: map"); + puts("- [F12]: screenshot"); + puts("- [ESCAPE]: menu"); + + return 0; + } + + SFG_init(); + sfVideoMode mode = {SFG_SCREEN_RESOLUTION_X, SFG_SCREEN_RESOLUTION_Y, 32}; sfEvent event; clock = sfClock_create(); sfClock_restart(clock); - SFG_init(); + puts("initializing"); for (int i = 0; i < AUDIO_BUFFER_SIZE; ++i) audioBuffer[i] = 0; @@ -288,6 +344,8 @@ int main() sfSoundStream_play(sound); + puts("starting"); + while (sfRenderWindow_isOpen(window)) { while (sfRenderWindow_pollEvent(window,&event)) @@ -295,6 +353,8 @@ int main() sfRenderWindow_close(window); else if (event.type == sfEvtMouseWheelMoved) mouseWheelState = event.mouseWheel.delta; + else if (event.type == sfEvtKeyPressed && event.key.code == sfKeyF12) + screenshot(); if (!SFG_mainLoopBody()) break; @@ -306,6 +366,8 @@ int main() sfRenderWindow_display(window); } + puts("ending"); + sfSoundStream_stop(sound); sfSoundStream_destroy(sound); sfSprite_destroy(windowSprite); diff --git a/main_sdl.c b/main_sdl.c index 1d67dd2..692f749 100644 --- a/main_sdl.c +++ b/main_sdl.c @@ -30,10 +30,7 @@ #define SFG_UNLOCK_DOOR 1 // #define SFG_REVEAL_MAP 1 #define SFG_INFINITE_AMMO 1 - // #define SFG_TIME_MULTIPLIER 512 - -// uncomment for perfomance debug // #define SFG_CPU_LOAD(percent) printf("CPU load: %d%\n",percent); #ifndef __EMSCRIPTEN__ @@ -70,20 +67,13 @@ #include #endif - -// #define SFG_SCREEN_RESOLUTION_X 88 -// #define SFG_SCREEN_RESOLUTION_Y 110 - -// #define SFG_SCREEN_RESOLUTION_X 80 -// #define SFG_SCREEN_RESOLUTION_Y 64 - /* - SDL is easier to play thanks to nice controls, so make the player take full + SDL is easier to play thanks to nice controls so make the player take full damage to make it a bit harder. */ #define SFG_PLAYER_DAMAGE_MULTIPLIER 1024 -#define MUSIC_VOLUME 16 +#define SDL_MUSIC_VOLUME 16 #if !SFG_OS_IS_MALWARE #include @@ -101,16 +91,18 @@ uint8_t webKeyboardState[SFG_KEY_COUNT]; uint8_t sdlMouseButtonState = 0; int8_t sdlMouseWheelState = 0; -uint16_t screen[SFG_SCREEN_RESOLUTION_X * SFG_SCREEN_RESOLUTION_Y]; // RGB565 format +uint16_t sdlScreen[SFG_SCREEN_RESOLUTION_X * SFG_SCREEN_RESOLUTION_Y]; // RGB565 SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *texture; SDL_Surface *screenSurface; +// now implement the Anarch API functions (SFG_*) + void SFG_setPixel(uint16_t x, uint16_t y, uint8_t colorIndex) { - screen[y * SFG_SCREEN_RESOLUTION_X + x] = paletteRGB565[colorIndex]; + sdlScreen[y * SFG_SCREEN_RESOLUTION_X + x] = paletteRGB565[colorIndex]; } uint32_t SFG_getTimeMs() @@ -154,6 +146,7 @@ uint8_t SFG_load(uint8_t data[SFG_SAVE_SIZE]) return 1; #else + // no saving for web version return 0; #endif } @@ -166,7 +159,7 @@ void SFG_sleepMs(uint16_t timeMs) } #ifdef __EMSCRIPTEN__ -void webButton(uint8_t key, uint8_t down) +void webButton(uint8_t key, uint8_t down) // HTML button pressed { webKeyboardState[key] = down; } @@ -200,7 +193,7 @@ void SFG_processEvent(uint8_t event, uint8_t data) int8_t SFG_keyPressed(uint8_t key) { - if (webKeyboardState[key]) + if (webKeyboardState[key]) // this only takes effect in the web version return 1; #define k(x) sdlKeyboardState[SDL_SCANCODE_ ## x] @@ -211,9 +204,9 @@ int8_t SFG_keyPressed(uint8_t key) case SFG_KEY_RIGHT: return k(RIGHT) || k(E) || k(KP_6); break; case SFG_KEY_DOWN: return k(DOWN) || k(S) || k(KP_5) || k(KP_2); break; case SFG_KEY_LEFT: return k(LEFT) || k(Q) || k(KP_4); break; - case SFG_KEY_A: return k(J) || k(RETURN) || k(LSHIFT) || + case SFG_KEY_A: return k(J) || k(RETURN) || k(LCTRL) || k(RCTRL) || (sdlMouseButtonState & SDL_BUTTON_LMASK); break; - case SFG_KEY_B: return k(K) || k(LCTRL) || k(RCTRL); break; + case SFG_KEY_B: return k(K) || k(LSHIFT); break; case SFG_KEY_C: return k(L); break; case SFG_KEY_JUMP: return k(SPACE); break; case SFG_KEY_STRAFE_LEFT: return k(A) || k(KP_7); break; @@ -227,11 +220,10 @@ int8_t SFG_keyPressed(uint8_t key) if (k(P) || k(X)) return 1; - if (sdlMouseWheelState > 0) - { - sdlMouseWheelState = 0; - return 1; - } +#define checkMouse(cmp)\ + if (sdlMouseWheelState cmp 0) { sdlMouseWheelState = 0; return 1; } + + checkMouse(>) return 0; break; @@ -240,11 +232,9 @@ int8_t SFG_keyPressed(uint8_t key) if (k(O) || k(Y) || k(Z)) return 1; - if (sdlMouseWheelState < 0) - { - sdlMouseWheelState = 0; - return 1; - } + checkMouse(<) + +#undef checkMouse return 0; break; @@ -262,7 +252,7 @@ void mainLoopIteration() SDL_Event event; #ifdef __EMSCRIPTEN__ - // Hack, without it sound won't work because of shitty browser audio policies. + // hack, without it sound won't work because of shitty browser audio policies if (SFG_game.frame % 512 == 0) SDL_PauseAudio(0); @@ -272,7 +262,7 @@ void mainLoopIteration() { if (event.type == SDL_MOUSEWHEEL) { - if (event.wheel.y > 0) // scroll up + if (event.wheel.y > 0) // scroll up sdlMouseWheelState = 1; else if (event.wheel.y < 0) // scroll down sdlMouseWheelState = -1; @@ -288,7 +278,8 @@ void mainLoopIteration() if (!SFG_mainLoopBody()) running = 0; - SDL_UpdateTexture(texture,NULL,screen,SFG_SCREEN_RESOLUTION_X * sizeof(uint16_t)); + SDL_UpdateTexture(texture,NULL,sdlScreen, + SFG_SCREEN_RESOLUTION_X * sizeof(uint16_t)); SDL_RenderClear(renderer); SDL_RenderCopy(renderer,texture,NULL,NULL); @@ -297,7 +288,8 @@ void mainLoopIteration() #ifdef __EMSCRIPTEN__ typedef void (*em_callback_func)(void); -void emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop); +void emscripten_set_main_loop( + em_callback_func func, int fps, int simulate_infinite_loop); #endif uint16_t audioBuff[SFG_SFX_SAMPLE_COUNT]; @@ -319,7 +311,7 @@ void audioFillCallback(void *userdata, uint8_t *s, int l) for (int i = 0; i < l / 2; ++i) { s16[i] = musicOn ? - mixSamples(audioBuff[audioPos], MUSIC_VOLUME * + mixSamples(audioBuff[audioPos], SDL_MUSIC_VOLUME * (SFG_getNextMusicSample() - SFG_musicTrackAverages[SFG_MusicState.track])) : audioBuff[audioPos]; @@ -386,22 +378,24 @@ int main(int argc, char *argv[]) if (argHelp) { - puts("Anarch, version " SFG_VERSION_STRING "\n"); + puts("Anarch (SDL), version " SFG_VERSION_STRING "\n"); puts("Anarch is a unique suckless FPS game. Collect weapons and items and destroy"); puts("robot enemies in your way in order to get to the level finish. Some door are"); puts("locked and require access cards. Good luck!\n"); - puts("created by Miloslav \"drummyfish\" Ciz, released under CC0 1.0 (public domain)\n"); + puts("created by Miloslav \"drummyfish\" Ciz, 2020, released under CC0 1.0 (public domain)\n"); puts("CLI flags:\n"); puts("-h print this help and exit"); puts("-w force window"); puts("-f force fullscreen\n"); puts("controls:\n"); puts("- arrows, numpad, [W] [S] [A] [D] [Q] [R]: movement"); - puts("- mouse: rotation, [LMB] shoot, [RMB] toggle free look, wheel weapon change"); + puts("- mouse: rotation, [LMB] shoot, [RMB] toggle free look"); puts("- [SPACE]: jump"); - puts("- [G] [RETURN] [SHIFT]: game A button (confirm, strafe)"); - puts("- [H] [CTRL]: game B button (cancel, shoot)"); - puts("- [O] [P] [X] [Y] [Z]: change weapons"); + puts("- [J] [RETURN] [CTRL] [LMB]: game A button (shoot, confirm)"); + puts("- [K] [SHIFT]: game B button (cancel, strafe)"); + puts("- [L]: game C button (+ down = menu, + up = jump, ...)"); + puts("- [F]: cycle next/previous weapon"); + puts("- [O] [P] [X] [Y] [Z] [mouse wheel]: change weapons"); puts("- [TAB]: map"); puts("- [ESCAPE]: menu"); @@ -485,10 +479,10 @@ int main(int argc, char *argv[]) puts("SDL: freeing SDL"); SDL_PauseAudio(1); + SDL_CloseAudio(); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); - SDL_CloseAudio(); puts("SDL: ending"); diff --git a/main_terminal.c b/main_terminal.c index 6be3e77..699be20 100644 --- a/main_terminal.c +++ b/main_terminal.c @@ -1,11 +1,11 @@ /** @file main_pokitto.cpp - This is Linux terminal implementation of the game front end. This needs root - priviledges (sudo) to work! This frontend is more of an experiment, don't - expect it to work perfectly and everywhere. - - Needs to be run with sudo. + This is Linux terminal implementation of the game front end. If you replace + the input methods, it will most likely run in other terminals as well. This + needs root priviledges (sudo) to work (because we need to read keyboard and + mouse inputs)! This frontend is more of an experiment, don't expect it to work + perfectly and everywhere. by Miloslav Ciz (drummyfish), 2019 @@ -99,14 +99,15 @@ 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') || 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_A: return input_getKey('i') || input_getKey(SMALLINPUT_RETURN) || input_getKey(SMALLINPUT_MOUSE_L); break; + case SFG_KEY_B: return input_getKey('j') || input_getKey(SMALLINPUT_CTRL); break; + case SFG_KEY_C: return input_getKey('k'); break; case SFG_KEY_MAP: return input_getKey(SMALLINPUT_TAB); break; case SFG_KEY_JUMP: return input_getKey(' '); 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_CYCLE_WEAPON: return input_getKey('f'); break; case SFG_KEY_TOGGLE_FREELOOK: return input_getKey(SMALLINPUT_MOUSE_R); break; default: return 0; break; } diff --git a/media/texts.txt b/media/texts.txt index cef723c..eb14e4c 100644 --- a/media/texts.txt +++ b/media/texts.txt @@ -18,6 +18,7 @@ everything else in this repository. \ / '-.__ __.-' ''''' + share on: - own website - itch.io