Clean a bit and stuff

This commit is contained in:
Miloslav Ciz 2024-12-18 20:45:35 +01:00
parent e42212d7c1
commit b79b83e56a
6 changed files with 99 additions and 88 deletions

View file

@ -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.

View file

@ -22,6 +22,9 @@ static const char *LCR_maps[] =
"#=s0s0 #fd190" // big concrete
"#+v0n0"
"#!x0n0"
"#=s0B0 #fd910" // concrete wall
"#^s1A0 #fk110" // ramps before wall

8
map.h
View file

@ -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;

View file

@ -19,7 +19,7 @@ typedef int32_t LCR_GameUnit; ///< abstract game unit
#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;
}

View file

@ -206,8 +206,7 @@ void _LCR_pixelFunc3D(S3L_PixelInfo *pixel)
if (pixel->modelIndex < 8)
{
uint8_t tData =
(LCR_renderer.mapTriangleData +
uint8_t tData = (LCR_renderer.mapTriangleData +
LCR_renderer.chunkStarts[LCR_renderer.loadedChunks[
pixel->modelIndex]])[pixel->triangleIndex];
@ -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
}
/**

View file

@ -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