diff --git a/TODO.txt b/TODO.txt index 1c5af9c..64b97c5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,8 +2,8 @@ fuck issue trackers :D =========== GENERAL ============== -- controller supports? analog input could be "tapping" the keys with varying - frequency +- maybe address the jerky rotations? or not? +- add (digital) controller support to SDL and CSFML? - frontends: - auto test frontend, with no I/O, that will just internally run a series of inputs and check if the output is as expected @@ -38,14 +38,51 @@ fuck issue trackers :D =========== BUGS ================= -- it seems like on arduinos tiny maps can't be loaded (say "FAILED") even if in - theory they should (enough block space to load): try to set the exact same - settings on PC and see if the maps load or what +- on arduinos for some reason straight triangles (floor, walls, ...) on map + don't fucking display, they seem to not be added at all, but maybe it's + something with the "instability" due to low memory... - replay loading BUG! somehow map2 replay was saves with hash 05ef0ab1 instead of correct 3c5ba5dd once, WTF, check where hash gets modified +============ FUTURE ============== + +- mods: + - bouncy car body with inertia, not affecting physics ofc, just eye candy + - TAS mod? ideas: + - step by single frames, the mod will literally keep all inputs in RAM, will + be able to rewind back (quickly replay the inputs) + - kinda bruteforce solver? + - export maps as 3D models in OBJ format (just print to console?) + - mod allowing to view two replays at once (one normal replay, one ghost), + would be cool for run comparison + - some extra graphic consoomer goodies? like: + - leave light trail behind the car (particles?) + - postproc effects? render to a buffer in renderer, then draw it again + - analog controller support, would just tap arrows in frequency according to + analog the analog value; create a new frontend function to implement, + something like LCR_getAnalogInput(type) or smt + - apply this to mouse lol -- L/R buttons can be up/down, moving the mouse + can steer, middle button can restart + - car horn? + - drunk mode: apply some kinda wave/sin/cos transform to all models to make + them swing, maybe even add delay to inputs + - weather mods? like lightning (invert colors quickly), rain, snow, sand, ... +- "AI" driver? +- 2D renderer? like isometric or top-down or something, could enable the game + literally on very weak devices? top down might be nice, the car could be + drawn out of circles, getting bigger closer to camera, camera could rotate + along with car etc. +- kinda fancy OpenGL renderer? +- literal network multiplayer? could even be easy bcz no collisions +- "user friendly" map editor? +- procedural map generator? + =========== HANDLED ============== +- it seems like on arduinos tiny maps can't be loaded (say "FAILED") even if in + theory they should (enough block space to load): try to set the exact same + settings on PC and see if the maps load or what. IT'S BCS BUILDING THE MAP + TEMPORARILY REQUIRES MORE BLOCKS - should drifting make a sound? NO NEED - menu: key repeat? - replay validation? maybe yes? diff --git a/frontend_espboy/frontend_espboy.ino b/frontend_espboy/frontend_espboy.ino index 07d5377..c207dda 100644 --- a/frontend_espboy/frontend_espboy.ino +++ b/frontend_espboy/frontend_espboy.ino @@ -33,11 +33,8 @@ TFT_eSPI tft; #define LCR_SETTING_LOG_LEVEL 0 #define LCR_SETTING_LOD_DISTANCE 100 #define LCR_SETTING_CAR_ANIMATION_SUBDIVIDE 0 - #define LCR_SETTING_FPS 25 - #define LCR_SETTING_POTATO_GRAPHICS 1 - #define LCR_SETTING_ENABLE_DATA_FILE 0 #define LCR_SETTING_ONLY_SMALL_MAPS 1 @@ -65,7 +62,7 @@ uint8_t LCR_keyPressed(uint8_t key) case LCR_KEY_LEFT: return keys & 0x01; break; case LCR_KEY_A: return keys & 0x10; break; case LCR_KEY_B: return keys & 0x20; break; - default: return 0; break; + default: return 0; break; } } diff --git a/frontend_helper.h b/frontend_helper.h index c92a6fb..1e80b13 100644 --- a/frontend_helper.h +++ b/frontend_helper.h @@ -12,7 +12,7 @@ #define LCR_SETTING_RESOLUTION_Y 200 #define LCR_SETTING_POTATO_GRAPHICS 1 #define LCR_SETTING_332_COLOR 1 - #define LCR_SETTING_FPS 25 + #define LCR_SETTING_FPS 20 #define LCR_SETTING_CAR_SHADOW 0 #define LCR_SETTING_CAR_ANIMATION_SUBDIVIDE 0 #define LCR_SETTING_PARTICLES 0 @@ -28,7 +28,7 @@ #define LCR_SETTING_PARTICLES 0 #define LCR_SETTING_TEXTURE_SUBSAMPLE 8 #define LCR_SETTING_FOG 0 - #define LCR_SETTING_FPS 30 + #define LCR_SETTING_FPS 25 #elif PRESET_QUALITY == 3 // normal #define LCR_SETTING_RESOLUTION_X 800 #define LCR_SETTING_RESOLUTION_Y 600 diff --git a/game.h b/game.h index d096877..5aff626 100644 --- a/game.h +++ b/game.h @@ -365,6 +365,7 @@ struct unsigned int itemsTotal; } dataFile; +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 #define LCR_GHOST_SAMPLE_SIZE 5 struct { @@ -377,6 +378,7 @@ struct allow ghosts for even long replays. */ int16_t offset[3]; ///< Small correcting position offset. } ghost; +#endif #ifdef LCR_FPS_GET_MS uint16_t renderFrameMS; @@ -498,7 +500,10 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost) LCR_rendererLoadMapChunks(); } +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 LCR_game.ghost.active = ghost; +#endif + LCR_gameSetState(LCR_GAME_STATE_RUN_STARTING); LCR_game.runTime = 0; } @@ -506,6 +511,7 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost) void LCR_gameGetNthGhostSample(unsigned int n, LCR_GameUnit position[3], LCR_GameUnit rotation[3]) { +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 n *= LCR_GHOST_SAMPLE_SIZE; position[0] = LCR_game.ghost.samples[n]; @@ -538,11 +544,20 @@ void LCR_gameGetNthGhostSample(unsigned int n, rotation[i] = (rotation[i] * LCR_GAME_UNIT) / 16; } +#else + for (int i = 0; i < 3; ++i) + { + position[i] = 0; + rotation[i] = 0; + } + +#endif } void LCR_gameGhostGetTransform(uint32_t frame, LCR_GameUnit position[3], LCR_GameUnit rotation[3]) { +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 int n = ((frame >> LCR_game.ghost.stretch) / LCR_SETTING_GHOST_STEP); LCR_gameGetNthGhostSample(n,position,rotation); @@ -575,6 +590,13 @@ void LCR_gameGhostGetTransform(uint32_t frame, / LCR_SETTING_GHOST_STEP)) % LCR_GAME_UNIT; } } +#else + for (int i = 0; i < 3; ++i) + { + position[i] = 0; + rotation[i] = 0; + } +#endif } /** @@ -583,6 +605,7 @@ void LCR_gameGhostGetTransform(uint32_t frame, */ void _LCR_gamePrepareGhost(void) { +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 && LCR_SETTING_REPLAY_MAX_SIZE != 0 LCR_GameUnit carTransform[6]; LCR_LOG1("preparing ghost"); @@ -658,6 +681,7 @@ void _LCR_gamePrepareGhost(void) LCR_game.ghost.offset[i] = (((((int) LCR_currentMap.startPos[i]) - LCR_MAP_SIZE_BLOCKS / 2) * LCR_GAME_UNIT + LCR_GAME_UNIT / 2) / (i == 1 ? 2 : 1)) - carTransform[i]; +#endif } LCR_GameUnit LCR_carSpeedKMH(void) @@ -853,8 +877,9 @@ uint8_t LCR_gameLoadMap(unsigned int mapIndex) if the replay couldn't be loaded or -3 if the replay is invalid. This function potentially reloads current map! */ -unsigned int LCR_gameLoadReplay(unsigned int replayIndex) +int LCR_gameLoadReplay(unsigned int replayIndex) { +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 uint32_t mapHash; uint16_t nameHash; char c; @@ -921,6 +946,9 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex) } return 0; +#else + return -2; +#endif } void LCR_gameEraseMenuItemNames(void) @@ -1240,8 +1268,12 @@ void LCR_gameDraw3DView(void) LCR_GameUnit carTransform[6]; LCR_GameUnit physicsInterpolationParam = - !(LCR_racing.replay.on && LCR_replayHasFinished()) ? - LCR_GAME_UNIT - +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 + !(LCR_racing.replay.on && LCR_replayHasFinished()) +#else + 1 +#endif + ? LCR_GAME_UNIT - ((LCR_game.nextRacingTickTime - LCR_game.time) * LCR_GAME_UNIT) / LCR_RACING_TICK_MS_RT : LCR_GAME_UNIT / 2; @@ -1251,6 +1283,7 @@ void LCR_gameDraw3DView(void) LCR_rendererSetCarTransform(carTransform,carTransform + 3); +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 if (LCR_game.ghost.active && LCR_game.state != LCR_GAME_STATE_RUN_STARTING) { LCR_GameUnit carTransform2[3]; @@ -1269,6 +1302,7 @@ void LCR_gameDraw3DView(void) } else LCR_rendererSetGhostVisibility(0); +#endif if (LCR_game.cameraMode != LCR_CAMERA_MODE_FREE && LCR_game.state != LCR_GAME_STATE_RUN_FINISHED) @@ -1347,6 +1381,8 @@ void LCR_gameSaveReplay(void) char str[10]; LCR_LOG0("saving replay"); + +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 _LCR_gameDataCharWrite(LCR_DATA_FILE_SEPARATOR); _LCR_gameDataCharWrite('R'); @@ -1371,6 +1407,7 @@ void LCR_gameSaveReplay(void) LCR_replayOutputStr(_LCR_gameDataCharWrite); LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_SAVED]); +#endif } /** @@ -1400,13 +1437,28 @@ void LCR_gameHandleInput(void) if (LCR_game.keyStates[LCR_KEY_A] == 1) { if (LCR_game.runTime <= LCR_currentMap.targetTime - && !LCR_game.ghost.active) +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 + && !LCR_game.ghost.active +#endif + ) { LCR_LOG1("setting new target time"); LCR_currentMap.targetTime = LCR_game.runTime; } - LCR_gameResetRun(LCR_racing.replay.on,LCR_game.ghost.active); + LCR_gameResetRun( +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 + LCR_racing.replay.on, +#else + 0, +#endif + +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 + LCR_game.ghost.active +#else + 0 +#endif + ); } } else if (LCR_game.state == LCR_GAME_STATE_RUN_STARTING) @@ -1466,7 +1518,19 @@ void LCR_gameHandleInput(void) LCR_rendererMoveCamera(offsets,offsets + 3); } else if (LCR_game.keyStates[LCR_KEY_A] == 1) - LCR_gameResetRun(LCR_racing.replay.on,LCR_game.ghost.active); + LCR_gameResetRun( +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 + LCR_racing.replay.on, +#else + 0, +#endif + +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 + LCR_game.ghost.active +#else + 0 +#endif + ); } else // LCR_GAME_STATE_MENU { @@ -1566,10 +1630,12 @@ void LCR_gameHandleInput(void) break; case 3: +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 if (LCR_game.statePrev == LCR_GAME_STATE_RUN_FINISHED && !LCR_racing.replay.on) LCR_gameSaveReplay(); else +#endif LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_FAIL]); break; @@ -1661,7 +1727,9 @@ uint8_t LCR_gameStep(uint32_t time) if (LCR_game.menu.selectedTab == 3) { _LCR_gamePrepareGhost(); +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 LCR_currentMap.targetTime = LCR_racing.replay.achievedTime; +#endif } LCR_gameSetState(LCR_GAME_STATE_LOADED); @@ -1736,13 +1804,25 @@ uint8_t LCR_gameStep(uint32_t time) LCR_LOG1("finished, time:"); LCR_LOG1_NUM(LCR_game.runTime); - if (LCR_game.runTime <= LCR_currentMap.targetTime && - !LCR_racing.replay.on) + if (LCR_game.runTime <= LCR_currentMap.targetTime +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 + && !LCR_racing.replay.on +#endif + ) { LCR_gameSaveReplay(); - if (!LCR_game.mapBeaten && !LCR_game.ghost.active && - !LCR_racing.replay.on) + if (!LCR_game.mapBeaten && +#if LCR_SETTING_GHOST_MAX_SAMPLES != 0 + !LCR_game.ghost.active && +#endif + +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 + !LCR_racing.replay.on +#else + 1 +#endif + ) { LCR_LOG1("map beaten"); LCR_game.mapBeaten = 1; diff --git a/racing.h b/racing.h index c9e6202..c20d281 100644 --- a/racing.h +++ b/racing.h @@ -171,6 +171,7 @@ struct uint16_t crashState; +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 struct { uint8_t on; ///< Currently playing replay? @@ -182,6 +183,7 @@ struct uint16_t currentFrame; uint32_t achievedTime; ///< Time achieved in physics frames. } replay; +#endif } LCR_racing; uint32_t LCR_timeTicksToMS(uint32_t ticks) @@ -211,15 +213,19 @@ static inline uint8_t LCR_racingCurrentGroundMaterial(void) void LCR_replayInitRecording(void) { LCR_LOG1("initializing replay recording"); +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 LCR_racing.replay.eventCount = 0; LCR_racing.replay.achievedTime = 0; +#endif } void LCR_replayInitPlaying(void) { LCR_LOG1("initializing replay playing"); +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 LCR_racing.replay.currentEvent = 0; LCR_racing.replay.currentFrame = 0; +#endif } /** @@ -230,6 +236,7 @@ void LCR_replayOutputStr(void (*printChar)(char)) { LCR_LOG1("outputting replay"); +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 const char *s = LCR_currentMap.name; printChar(LCR_RACING_VERSION1); @@ -274,6 +281,7 @@ void LCR_replayOutputStr(void (*printChar)(char)) } printChar('\n'); +#endif } /** @@ -285,6 +293,7 @@ void LCR_replayOutputStr(void (*printChar)(char)) int LCR_replayLoadFromStr(char (*nextChar)(void), uint32_t *mapHash, uint16_t *nameHash) { +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 char c; LCR_racing.replay.eventCount = 0; @@ -367,10 +376,14 @@ int LCR_replayLoadFromStr(char (*nextChar)(void), uint32_t *mapHash, LCR_racing.replay.events[i] = 0; return 1; +#else + return 0; +#endif } int LCR_replayHasFinished(void) { +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 if (LCR_racing.replay.currentEvent == LCR_racing.replay.eventCount) { uint32_t totalTime = LCR_racing.replay.currentFrame; @@ -382,6 +395,9 @@ int LCR_replayHasFinished(void) } return LCR_racing.replay.currentEvent > LCR_racing.replay.eventCount; +#else + return 1; +#endif } /** @@ -390,6 +406,7 @@ int LCR_replayHasFinished(void) */ uint8_t LCR_replayGetNextInput(void) { +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 if (LCR_replayHasFinished()) { LCR_racing.replay.currentFrame++; // has to be here @@ -407,6 +424,9 @@ uint8_t LCR_replayGetNextInput(void) return LCR_racing.replay.currentEvent ? (LCR_racing.replay.events[LCR_racing.replay.currentEvent - 1] & 0x0f) : 0; +#else + return 0; +#endif } /** @@ -419,6 +439,7 @@ int LCR_replayRecordEvent(uint32_t frame, uint8_t input) { LCR_LOG2("recording replay event"); +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 if (LCR_racing.replay.achievedTime) return 1; // already finished @@ -429,7 +450,6 @@ int LCR_replayRecordEvent(uint32_t frame, uint8_t input) return 1; } -#if LCR_SETTING_REPLAY_MAX_SIZE > 0 uint8_t previousInput = 0; uint32_t previousFrame = 0; @@ -985,7 +1005,9 @@ void LCR_racingRestart(uint8_t replay) LCR_racing.tick = 0; LCR_racing.fanForce = 0; +#if LCR_SETTING_REPLAY_MAX_SIZE > 0 LCR_racing.replay.on = replay; +#endif TPE_bodyActivate(&(LCR_racing.carBody)); LCR_racing.wheelCollisions = 0; @@ -1069,7 +1091,9 @@ void LCR_racingInit(void) TPE_worldInit(&(LCR_racing.physicsWorld), &(LCR_racing.carBody),1,_LCR_racingEnvironmentFunction); +#if LCR_SETTING_REPLAY_MAX_SIZE > 0 LCR_racing.replay.on = 0; +#endif LCR_racing.physicsWorld.collisionCallback = _LCR_racingCollisionHandler; } @@ -1221,6 +1245,7 @@ uint32_t LCR_racingStep(unsigned int input) LCR_racing.groundMaterial = LCR_BLOCK_MATERIAL_CONCRETE; +#if LCR_SETTING_REPLAY_MAX_SIZE > 0 if (LCR_racing.replay.on) { if (LCR_racing.tick == 0) @@ -1232,6 +1257,7 @@ uint32_t LCR_racingStep(unsigned int input) input = LCR_replayGetNextInput(); } else +#endif { if (LCR_racing.tick == 0) LCR_replayInitRecording(); @@ -1641,8 +1667,10 @@ uint32_t LCR_racingStep(unsigned int input) { result |= LCR_RACING_EVENT_FINISHED; +#if LCR_SETTING_REPLAY_MAX_SIZE > 0 if (!LCR_racing.replay.on) LCR_replayRecordEvent(LCR_racing.tick,LCR_REPLAY_EVENT_END); +#endif } } } @@ -1692,6 +1720,7 @@ int LCR_replayValidate(void) { LCR_LOG1("validating replay"); +#if LCR_SETTING_REPLAY_MAX_SIZE != 0 int result = 0; LCR_racingRestart(1); @@ -1738,6 +1767,9 @@ int LCR_replayValidate(void) LCR_racingRestart(1); return result; +#else + return 0; +#endif } #endif // guard diff --git a/renderer.h b/renderer.h index 02d2153..c94855a 100644 --- a/renderer.h +++ b/renderer.h @@ -41,13 +41,9 @@ #define S3L_PERSPECTIVE_CORRECTION 0 #define S3L_NEAR_CROSS_STRATEGY 1 #define S3L_FLAT 1 - #define S3L_Z_BUFFER 0 - - #ifndef S3L_SORT - #define S3L_SORT 1 - #endif - - #define S3L_MAX_TRIANGES_DRAWN 48 + #undef S3L_Z_BUFFER + #define S3L_Z_BUFFER 2 // simplified + #define S3L_MAX_TRIANGES_DRAWN 64 // lower, in case sorting was turned on #endif #define S3L_NEAR (10 * (S3L_F / 16)) // too low value will cause overflows @@ -330,11 +326,11 @@ void _LCR_pixelFunc3D(S3L_PixelInfo *pixel) switch (tData & 0x0f) { - case 3: LCR_renderer.flatAndTransparent &= ~(0x3008); break; // grass - case 4: LCR_renderer.flatAndTransparent &= ~(0x0408); break; // mud - case 5: LCR_renderer.flatAndTransparent |= 0x0010; break; // ice - case 6: LCR_renderer.flatAndTransparent |= 0x8080; break; // acc - case 7: LCR_renderer.flatAndTransparent &= ~(0x4208); break; // fan + case 3: LCR_renderer.flatAndTransparent &= ~(0x3008); break; // grass + case 4: LCR_renderer.flatAndTransparent &= ~(0x0408); break; // mud + case 5: LCR_renderer.flatAndTransparent |= 0x0010; break; // ice + case 6: LCR_renderer.flatAndTransparent |= 0x8080; break; // acc + case 7: LCR_renderer.flatAndTransparent &= ~(0x4208); break; // fan case LCR_RENDERER_MAT_CP0: LCR_renderer.flatAndTransparent = LCR_SETTING_CHECKPOINT_0_COLOR; @@ -718,7 +714,7 @@ int _LCR_rendererQuadCoversTri(const S3L_Unit quad[8], const S3L_Unit tri[6]) } /** - Checks whether two triangles (and potenrially their neighbors) cover each + Checks whether two triangles (and potentially their neighbors) cover each other, in return values lowest bit means whether t1 is covered and the second lowest bit means whether t2 is covered. */ @@ -1094,7 +1090,7 @@ uint8_t _LCR_buildMapModel(void) (((VERT(triIndices[0],2) == VERT(triIndices[1],2)) && // same Z? (VERT(triIndices[1],2) == VERT(triIndices[2],2))) << 5); - if (!(triData & 0xf0)) + if (!triData) { // diagonal walls