From b79b83e56a3dbbbe7f6d9b6e5ae488f73b17f315 Mon Sep 17 00:00:00 2001 From: Miloslav Ciz Date: Wed, 18 Dec 2024 20:45:35 +0100 Subject: [PATCH] Clean a bit and stuff --- README.md | 41 ++++++++++++++++++++++-------- assets.h | 3 +++ map.h | 8 ++---- racing.h | 56 ++++++++++++++++------------------------- renderer.h | 73 +++++++++++++++++++++++++++--------------------------- settings.h | 6 +++-- 6 files changed, 99 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index ed9f685..adfd7af 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,37 @@ The following is a draft of readme that will eventually be published, it may con WIP racing game -**Cheating in this game is allowed.** Just drive and have fun, try to find the best solution to each map by any means you judge fun enough. +**Cheating in this game is allowed.** Just drive and have fun, try to find the best solution to each map in any way you deem entertaining. + +## What Is This + +This is a libre 3D racing game very much inspired by the proprietary game Trackmania. It is written in a selfless way (it won't try to exploit you in any way, not even indirectly) following a philosophy of high minimalism, excluding any poison and bullshit of so called "modern technology". It was completely made by a single man who is extremely different from all other humans. + +The goal in this game is to finish a track as fast as possible. Unlike in typical racing games there is no live multiplayer: one doesn't race against other players directly, players instead compete by individually trying to finish given track faster, utilizing skill, tricks, shortcuts and potentially -- if they agree -- even computer assistance. Players don't even have to compete and may rather collaborate on finding faster solutions, analyzing tracks, looking for faster ways etc. This is therefore a kind of speedrunning game. + +Some of the **gameplay features** include: + +- **replays**: small files that record individual runs merely as a sequence of inputs -- this is good for sharing runs, proving records, creating TAS runs etc. +- **custom maps**: players can create their own maps using a simple plaintext format. +- **ghosts**: players may race against ghost (collisionless) opponents. +- **great many settings**: basically everything is modifyable. +- Zero price, no ads, no subscriptions, great performance, small size, no DRM and any other "modern" bullshit you will typically see in "modern" games. ## Why This Game Is Special -- **Everything is written in KISS/suckless C99 from scratch**. The whole project, including the physics engine, rendering engine, game logic and assets embedded in source code have TODO lines of code in totas. -- It has **custom 3D physics engine** (tinyphysicsengine) written from scratch, implemented in a single header file, using no GPU or floating point. -- It has **custom 3D software rendering engine** (small3dlib) written from scratch, implemented in a single header file, using no GPU or floating point. -- It **doesn't use floating point at all**, everything is done using fixed point. -- It **doesn't have any dependency libraries, not even the C standard library**, everything is written from scratch for maximum portability. Libraries such as SDL are used for frontends, but they are never an inherent part of the project, they can be basically drop in replaced with a similar library. -- **No build system** is used, everything is in a **single compilation unit**, to build the game only a single compiler invocation is needed. -- It is **extremely portable and future proof** thanks to having no dependencies besides C99 compiler. -- It is **extremely moddable and hackable**, the code is relatively simple and well commented, written to encourage hacking. There are no obstacles such as DRM, anti-cheating, obfuscation etc. -- It is **absolute and completely public domain free software with ZERO conditions on use** under CC0, it is legally more free that most "FOSS" software, there is no copyleft or credit requirement, you can do ABSOLUTELY anything you want with the project. This is a **selfless project aiming for no benefit of the creator** (include any non-finacial benefit as well), this is made purely to bring more good to the world without gaining any advantage for self. +- **Everything is written in KISS/suckless C99 from scratch**. The whole project, including the physics engine, rendering engine, game logic and assets embedded in source code have TODO lines of code in total. +- **Everything is created 100% from scratch** by the author, including code (of the game, physics engine, 3D rendering engine, frontends), textures (created from own photos), 3D models, sound effects, music (created using custom sound font made from own recorded samples), font, accompanying materials etcetc. This ensures absolute freedom, there is no shadow of a doubt there is indeed is no 3rd party copyright. Everything was also **created exclusively with free software**. +- It has **custom KISS 3D physics engine** (tinyphysicsengine) written from scratch, implemented in a single header file, using **no GPU or floating point**. +- It has **custom KISS 3D software rendering engine** (small3dlib) written from scratch, implemented in a single header file, using **no GPU or floating point**. +- The whole game **doesn't use floating point at all**, everything is done with fixed point. +- It **doesn't have any dependency libraries, not even the C standard library**, everything is written from scratch for **maximum portability**. Libraries such as SDL are used for frontends, but they are never an inherent part of the project, they can be basically drop in replaced with a similar library. +- **No build system** is used, everything is in a **single compilation unit**, to build the game only a single compiler invocation is needed. This removes another bloat and dependency. +- It is **extremely portable and future proof** thanks to having no dependencies besides C99 compiler. So far it was ported to: + - TODO +- It is **extremely moddable and hackable**, the code is relatively simple and well commented, written to encourage hacking. There are no obstacles such as DRM, anti-cheating, obfuscation etc. Cheating and hacking is allowed, do literally what you want. +- **No codes of censorship, flags or similar political bullshit**. This is just a game. +- It is **absolutely and completely public domain free software with ZERO conditions on use** under CC0, it is legally more free that most "FOSS" software, there is no copyleft or credit requirement, you can do ABSOLUTELY anything you want with the project. This is a **selfless project aiming for no benefit of the creator** (include any non-finacial benefit as well), this is made purely to bring more good to the world without gaining any advantage for self. + +## Manifesto + +By now I can't possibly summarize my views and life philosophy satisfyingly in a few paragraphs. If you're interested, kindly follow the rabbithole leading to my [website](http://www.tastyfish.cz) etc. diff --git a/assets.h b/assets.h index 4d1c2ee..6300072 100644 --- a/assets.h +++ b/assets.h @@ -22,6 +22,9 @@ static const char *LCR_maps[] = "#=s0s0 #fd190" // big concrete + "#+v0n0" + "#!x0n0" + "#=s0B0 #fd910" // concrete wall "#^s1A0 #fk110" // ramps before wall diff --git a/map.h b/map.h index 5f8ddad..ada9cd2 100644 --- a/map.h +++ b/map.h @@ -235,8 +235,7 @@ uint32_t LCR_mapBlockCoordsToCoordNumber(uint8_t x, uint8_t y, uint8_t z) void LCR_rampGetDimensions(uint8_t rampType, uint8_t *height4ths, uint8_t *length6ths) { - *height4ths = - (rampType == LCR_BLOCK_RAMP_14) + + *height4ths = (rampType == LCR_BLOCK_RAMP_14) + (rampType == LCR_BLOCK_RAMP || rampType == LCR_BLOCK_RAMP_ACCEL || rampType == LCR_BLOCK_RAMP_FAN || rampType == LCR_BLOCK_RAMP_STEEP) * 4 + (rampType == LCR_BLOCK_RAMP_12 || rampType == LCR_BLOCK_RAMP_34) * 2 + @@ -254,11 +253,8 @@ uint8_t *LCR_getMapBlockAtCoordNumber(uint32_t coord) while (b >= a) { uint16_t mid = (a + b) / 2; - uint8_t *block = LCR_currentMap.blocks + mid * LCR_BLOCK_SIZE; - - uint32_t coord2 = - LCR_mapBlockGetCoordNumber(block); + uint32_t coord2 = LCR_mapBlockGetCoordNumber(block); if (coord2 == coord) return block; diff --git a/racing.h b/racing.h index cd094cf..25350c3 100644 --- a/racing.h +++ b/racing.h @@ -9,17 +9,17 @@ typedef int32_t LCR_GameUnit; ///< abstract game unit #define LCR_GAME_UNIT 1024 ///< length of map square in LCR_GameUnits -#define LCR_RACING_INPUT_FORW 0x01 -#define LCR_RACING_INPUT_RIGHT 0x02 -#define LCR_RACING_INPUT_BACK 0x04 -#define LCR_RACING_INPUT_LEFT 0x08 +#define LCR_RACING_INPUT_FORW 0x01 +#define LCR_RACING_INPUT_RIGHT 0x02 +#define LCR_RACING_INPUT_BACK 0x04 +#define LCR_RACING_INPUT_LEFT 0x08 #define LCR_RACING_EVENT_CP_TAKEN 0x0001 #define LCR_RACING_EVENT_FINISHED 0x0002 #define LCR_RACING_EVENT_CRASH_SMALL 0x0004 #define LCR_RACING_EVENT_CRASH_BIG 0x0008 -#define LCR_PHYSICS_UNIT 4096 ///< length of map square for physics engine +#define LCR_PHYSICS_UNIT 4096 ///< len. of square for phys. engine #define TPE_RESHAPE_TENSION_LIMIT 3 #define TPE_RESHAPE_ITERATIONS 8 @@ -66,14 +66,14 @@ struct Lower bits record current collisions, higher bits the previous state (for averaging). */ - TPE_Vec3 carPositions[2]; ///* Current and previous position in game units. - TPE_Vec3 carRotations[2]; ///* Current and previous rotation in game units. - uint8_t carDrifting; ///* Whether or not the car is currently in drift. + TPE_Vec3 carPositions[2]; ///< Current and previous position in game units. + TPE_Vec3 carRotations[2]; ///< Current and previous rotation in game units. + uint8_t carDrifting; ///< Whether or not the car is currently in drift. TPE_Vec3 carOKPositions[LCR_CAR_JOINTS]; uint8_t carNotOKCount; - TPE_Unit fanForce; ///* Upwards acceleration caused by a fan. + TPE_Unit fanForce; ///< Upwards acceleration caused by a fan. LCR_GameUnit wheelRotation; LCR_GameUnit wheelSteer; @@ -489,8 +489,6 @@ LCR_GameUnit LCR_racingGetCarSpeedSigned(void) return LCR_racing.carSpeed; } - - uint8_t _LCR_racingCollisionHandler(uint16_t b1, uint16_t j1, uint16_t b2, uint16_t j2, TPE_Vec3 p) { @@ -516,12 +514,9 @@ LCR_GameUnit _LCR_racingSmoothRot(LCR_GameUnit angleNew, LCR_GameUnit angleOld) return angleOld + diff / 2; } - -void _LCR_racingUpdateCarPosRot(void) +TPE_Vec3 _LCR_racingGetWheelCenterPoint(void) { - TPE_Vec3 tmpVec = LCR_racing.carPositions[0]; - - TPE_Vec3 wheelAverage = _LCR_TPE_vec3DividePlain( + return _LCR_TPE_vec3DividePlain( TPE_vec3Plus( TPE_vec3Plus( LCR_racing.carBody.joints[0].position, @@ -529,9 +524,15 @@ void _LCR_racingUpdateCarPosRot(void) TPE_vec3Plus( LCR_racing.carBody.joints[2].position, LCR_racing.carBody.joints[3].position)),4); +} + +void _LCR_racingUpdateCarPosRot(void) +{ + TPE_Vec3 tmpVec = LCR_racing.carPositions[0]; LCR_racing.carPositions[0] = _LCR_TPE_vec3DividePlain( - TPE_vec3TimesPlain(wheelAverage,LCR_GAME_UNIT),LCR_PHYSICS_UNIT); + TPE_vec3TimesPlain(_LCR_racingGetWheelCenterPoint(),LCR_GAME_UNIT), + LCR_PHYSICS_UNIT); LCR_racing.carPositions[0] = // smooth the position TPE_vec3KeepWithinBox(LCR_racing.carPositions[1],LCR_racing.carPositions[0], @@ -637,7 +638,6 @@ void LCR_racingInit(void) &(LCR_racing.carBody),1,_LCR_racingEnvironmentFunction); LCR_racing.physicsWorld.collisionCallback = _LCR_racingCollisionHandler; - } /** @@ -742,12 +742,9 @@ void _LCR_racingWheelAccelerate(unsigned int wheel, TPE_Vec3 dir, if (accelerator) acc *= 2; // TODO: constant? - LCR_racing.carBody.joints[wheel].velocity[0] += - (dir.x * acc) / TPE_F; - LCR_racing.carBody.joints[wheel].velocity[1] += - (dir.y * acc) / TPE_F; - LCR_racing.carBody.joints[wheel].velocity[2] += - (dir.z * acc) / TPE_F; + LCR_racing.carBody.joints[wheel].velocity[0] += (dir.x * acc) / TPE_F; + LCR_racing.carBody.joints[wheel].velocity[1] += (dir.y * acc) / TPE_F; + LCR_racing.carBody.joints[wheel].velocity[2] += (dir.z * acc) / TPE_F; } int _LCR_racingCarShapeOK(void) @@ -1035,16 +1032,7 @@ uint32_t LCR_racingStep(unsigned int input) if (angle <= 0) { LCR_LOG1("roof flipped over, fixing") - LCR_racing.carBody.joints[4].position = - _LCR_TPE_vec3DividePlain( - TPE_vec3Plus( - TPE_vec3Plus( - LCR_racing.carBody.joints[0].position, - LCR_racing.carBody.joints[1].position), - TPE_vec3Plus( - LCR_racing.carBody.joints[2].position, - LCR_racing.carBody.joints[3].position)),4); - + LCR_racing.carBody.joints[4].position = _LCR_racingGetWheelCenterPoint(); angle = 0; } diff --git a/renderer.h b/renderer.h index af0c76b..9ad2dea 100644 --- a/renderer.h +++ b/renderer.h @@ -206,10 +206,9 @@ void _LCR_pixelFunc3D(S3L_PixelInfo *pixel) if (pixel->modelIndex < 8) { - uint8_t tData = - (LCR_renderer.mapTriangleData + - LCR_renderer.chunkStarts[LCR_renderer.loadedChunks[ - pixel->modelIndex]])[pixel->triangleIndex]; + uint8_t tData = (LCR_renderer.mapTriangleData + + LCR_renderer.chunkStarts[LCR_renderer.loadedChunks[ + pixel->modelIndex]])[pixel->triangleIndex]; switch (tData & 0x0f) { @@ -218,19 +217,31 @@ void _LCR_pixelFunc3D(S3L_PixelInfo *pixel) 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; + break; + + case LCR_RENDERER_MAT_CP1: + LCR_renderer.flatAndTransparent = LCR_SETTING_CHECKPOINT_1_COLOR; + break; + + case LCR_RENDERER_MAT_FIN: + LCR_renderer.flatAndTransparent = LCR_SETTING_FINISH_COLOR; + break; + default: break; } - tData &= 0x30; + tData &= 0x30; // isolate type - LCR_renderer.flatAndTransparent |= - (((uint16_t) (tData)) >> 4) | - (((uint16_t) (tData)) << 2) | - (((uint16_t) (tData)) << 7); + LCR_renderer.flatAndTransparent |= (((uint16_t) (tData)) >> 4) | + (((uint16_t) (tData)) << 2) | (((uint16_t) (tData)) << 7); } else LCR_renderer.flatAndTransparent >>= 1; // car, darken + // alter each triangle's color slightly: LCR_renderer.flatAndTransparent += pixel->triangleIndex % 4; } @@ -258,28 +269,21 @@ void _LCR_pixelFunc3D(S3L_PixelInfo *pixel) LCR_loadImage(LCR_IMAGE_CAR); for (int i = 0; i < 6; ++i) - { - LCR_renderer.triUVs[i] = - (LCR_carUvs[2 * LCR_carTriangleUvs[ - 3 * pixel->triangleIndex + i / 2] + i % 2] * (LCR_IMAGE_SIZE + 1)) - / 512; - } + LCR_renderer.triUVs[i] = (LCR_carUvs[2 * LCR_carTriangleUvs[3 * + pixel->triangleIndex + i / 2] + i % 2] * (LCR_IMAGE_SIZE + 1)) / 512; } else { // map model - const S3L_Index *t = - LCR_renderer.models[pixel->modelIndex].triangles + - 3 * pixel->triangleIndex; S3L_Unit *v[3]; + const S3L_Index *t = LCR_renderer.models[pixel->modelIndex].triangles + + 3 * pixel->triangleIndex; for (int i = 0; i < 3; ++i) v[i] = LCR_renderer.mapVerts + 3 * t[i]; - const uint8_t *triData = - LCR_renderer.mapTriangleData + - LCR_renderer.chunkStarts[LCR_renderer.loadedChunks[ - pixel->modelIndex]]; + const uint8_t *triData = LCR_renderer.mapTriangleData + + LCR_renderer.chunkStarts[LCR_renderer.loadedChunks[pixel->modelIndex]]; uint8_t type = triData[pixel->triangleIndex] >> 4; uint8_t mat = triData[pixel->triangleIndex] & 0x0f; @@ -426,8 +430,7 @@ S3L_Index _LCR_rendererAddMapVert(S3L_Unit x, S3L_Unit y, S3L_Unit z) return LCR_renderer.mapModel.vertexCount - 1; } - LCR_LOG0("couldn't add map vertex!"); - + LCR_LOG0("couldn't add map vertex"); return 0; } @@ -442,8 +445,7 @@ void _LCR_rendererAddMapTri(S3L_Index a, S3L_Index b, S3L_Index c, uint8_t mat) *t = b; t++; *t = c; - LCR_renderer.mapTriangleData[LCR_renderer.mapModel.triangleCount] = - mat; + LCR_renderer.mapTriangleData[LCR_renderer.mapModel.triangleCount] = mat; LCR_renderer.mapModel.triangleCount++; } @@ -543,8 +545,7 @@ uint8_t _LCR_rendererCheckMapTriCover(const S3L_Index *t1, { if (S3L_abs(vertices[0][0] - vertices[3][0]) + S3L_abs(vertices[0][1] - vertices[3][1]) + - S3L_abs(vertices[0][2] - vertices[3][2]) > - 2 * LCR_RENDERER_UNIT) + S3L_abs(vertices[0][2] - vertices[3][2]) > 2 * LCR_RENDERER_UNIT) return 0; // quick manhattan distance bailout condition for (int j = 0; j < 2; ++j) @@ -1008,7 +1009,6 @@ uint8_t LCR_rendererInit(void) LCR_LOG0("initializing renderer"); LCR_renderer.frame = 0; - LCR_renderer.carModel = LCR_renderer.models + 8; LCR_renderer.ghostModel = LCR_renderer.models + 9; @@ -1123,17 +1123,18 @@ void LCR_rendererMoveCamera(LCR_GameUnit forwRightUpOffset[3], LCR_renderer.scene.camera.transform.rotation.x += (yawPitchOffset[1] * S3L_F) / LCR_GAME_UNIT; -#define chk(o,c,l) \ +#define CHK(o,c,l) \ if (LCR_renderer.scene.camera.transform.translation.c o l) \ LCR_renderer.scene.camera.transform.translation.c = l; -chk(<,x,-1 * LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) -chk(>,x,LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) -chk(<,y,-1 * LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 4) -chk(>,y,LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 4) -chk(<,z,-1 * LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) -chk(>,z,LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) + CHK(<,x,-1 * LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) + CHK(>,x,LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) + CHK(<,y,-1 * LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 4) + CHK(>,y,LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 4) + CHK(<,z,-1 * LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) + CHK(>,z,LCR_MAP_SIZE_BLOCKS * LCR_RENDERER_UNIT / 2) +#undef CHK } /** diff --git a/settings.h b/settings.h index 3a3b9c1..d4accc6 100644 --- a/settings.h +++ b/settings.h @@ -10,8 +10,10 @@ #endif #ifndef LCR_SETTING_FPS - /** Rendering frames per second. Note this only applies to graphics, NOT - physics. */ + /** + Rendering frames per second. Note this only applies to graphics, NOT + physics. + */ #define LCR_SETTING_FPS 30 #endif