Nimbin[12]?Games / Entropie / entropie/src/world/course.c

Entropie git · main

Entropie festival game; first steps

sdl c game entropie cmake · first commit 2026-10-10 · last commit 2026-11-02 (18 hours ago) · synced 10 hours ago

C 88.5% Markdown 9.1% GLSL 2.3%
git clone https://git.christianimmanuel.de/games/Entropie.gitwget https://git.christianimmanuel.de/games/Entropie/archive/Entropie.tar.gz
entropie/src/world/course.c 4.9 KB · 110 lines raw
#include "world/course.h"
#include "world/site.h"

#define LANE 3.5f /* meters between the lanes */

typedef enum { LOG, BRANCH, FIREWOOD, CRATE, BENCH, COOLBOX, WALL, STUMP, BLOCK } Piece;

typedef struct {
    Piece piece;
    float side, along; /* lane (-1 left, 0 middle, 1 right) and meters down the course */
    Vec3  size;        /* LOG: diameter; boxes: across, high, deep */
    float kg;
} Obstacle;

static const Obstacle COURSE[] = {
    /* left: spaced out, getting higher */
    { BRANCH,   -1,  4.0f, { 0.0f, 0.9f, 0.0f }, 1.5f },
    { FIREWOOD, -1,  8.0f, { 0.3f, 0.45f, 2.0f }, 0.0f },
    { LOG,      -1, 12.0f, { 0.20f, 0, 0 }, 0.0f },
    { LOG,      -1, 16.0f, { 0.30f, 0, 0 }, 0.0f },
    { LOG,      -1, 20.0f, { 0.42f, 0, 0 }, 0.0f },
    { CRATE,    -1, 24.0f, { 0.8f, 0.3f, 0.4f }, 12.0f },
    { CRATE,    -1, 28.0f, { 0.4f, 0.4f, 0.4f }, 6.0f },  /* the orange cube */
    { CRATE,    -1, 32.0f, { 0.6f, 0.45f, 0.45f }, 18.0f },
    { CRATE,    -1, 36.0f, { 0.8f, 0.6f, 0.6f }, 20.0f },  /* too high to step over */
    /* middle: close together */
    { LOG,       0,  4.0f, { 0.30f, 0, 0 }, 0.0f },
    { LOG,       0,  5.0f, { 0.30f, 0, 0 }, 0.0f },
    { LOG,       0,  6.2f, { 0.30f, 0, 0 }, 0.0f },
    { LOG,       0,  7.6f, { 0.30f, 0, 0 }, 0.0f },
    { LOG,       0,  9.2f, { 0.30f, 0, 0 }, 0.0f },
    { LOG,       0, 12.0f, { 0.18f, 0, 0 }, 0.0f },
    { LOG,       0, 12.7f, { 0.18f, 0, 0 }, 0.0f },
    { LOG,       0, 13.4f, { 0.18f, 0, 0 }, 0.0f },
    { CRATE,     0, 16.0f, { 0.8f, 0.3f, 0.4f }, 12.0f },
    { CRATE,     0, 17.2f, { 0.8f, 0.3f, 0.4f }, 12.0f },
    { LOG,       0, 18.4f, { 0.36f, 0, 0 }, 0.0f },
    /* right: things that stay put */
    { BENCH,     1,  4.0f, { 1.8f, 0.45f, 0.36f }, 0.0f },
    { COOLBOX,   1,  8.0f, { 0.6f, 0.45f, 0.4f }, 0.0f },
    { WALL,      1, 12.0f, { 2.0f, 0.35f, 0.25f }, 0.0f },
    { STUMP,     1, 16.0f, { 0.45f, 0.35f, 0.0f }, 0.0f },
    { BENCH,     1, 20.0f, { 1.8f, 0.45f, 0.36f }, 0.0f },
    { WALL,      1, 21.2f, { 2.0f, 0.25f, 0.25f }, 0.0f },
    { BLOCK,     1, 26.0f, { 1.2f, 0.6f, 0.8f }, 0.0f },  /* too high: step up / climb */
    { BLOCK,     1, 32.0f, { 1.6f, 1.1f, 1.0f }, 0.0f },  /* climb */
};
#define COURSE_COUNT ((int)(sizeof(COURSE) / sizeof(COURSE[0])))

static float ground(const World *world, Vec3 p) {
    return Terrain_height(&world->terrain, p.x, p.z);
}

static Object *place(World *world, const Obstacle *o, Vec3 start, Vec3 along, Vec3 across) {
    Vec3 p = vec3_add(start, vec3_add(vec3_scale(along, o->along), vec3_scale(across, o->side * LANE)));
    p.y = ground(world, p);
    float yaw = atan2f(across.z, across.x); /* (its width runs across the lane) */
    Object *obj = NULL;
    switch (o->piece) {
    case LOG: {
        obj = Object_create(OBJ_LOG, PALETTE_WARM, vec3_zero(), vec3(o->size.x, 1.6f, 0.0f), 0.0f);
        if (obj) Object_makeLog(obj, vec3_add(p, vec3(0.0f, o->size.x * 0.5f + 0.01f, 0.0f)), across, 1.6f, o->size.x);
        break;
    }
    case BRANCH:
        obj = Object_create(OBJ_BRANCH, PALETTE_WARM, vec3_add(p, vec3(0.0f, 0.15f, 0.0f)), o->size, o->kg);
        if (obj) Object_setRotation(obj, euler_yxz_from_axes(across, vec3(0.0f, 1.0f, 0.0f)));
        break;
    case FIREWOOD:
        obj = Object_create(OBJ_FIREWOOD, PALETTE_WARM, vec3_add(p, vec3(0.0f, 0.15f, 0.0f)), o->size, 0.0f);
        if (obj) Object_setRotation(obj, euler_yxz_from_axes(across, vec3(0.0f, 1.0f, 0.0f)));
        break;
    case CRATE:
        obj = Object_create(OBJ_BOX, o->kg <= 6.0f ? PALETTE_WARM : PALETTE_STANDARD, p, o->size, o->kg);
        break;
    case BENCH:
        obj = Object_createFurniture(FURN_BENCH, p, o->size, 0.0f);
        break;
    case COOLBOX:
        obj = Object_create(OBJ_SUPPLY, PALETTE_STANDARD, p, o->size, 0.0f);
        break;
    case WALL:
    case BLOCK:
        obj = Object_create(OBJ_BOX_STATIC, PALETTE_GROUND, p, o->size, 100.0f);
        break;
    case STUMP:
        obj = Object_create(OBJ_STUMP, PALETTE_WARM, vec3_add(p, vec3(0.0f, o->size.y * 0.5f, 0.0f)), o->size, 0.0f);
        break;
    }
    if (obj && o->piece != LOG && o->piece != BRANCH && o->piece != FIREWOOD && o->piece != STUMP)
        Object_setRotation(obj, vec3(0.0f, yaw, 0.0f)); /* (local x across: see mat4_rotate_y) */
    return obj;
}

bool Course_build(World *world, Object *who, float *yaw) {
    Vec3 start, along;
    Site_courseFrame(&start, &along);
    Vec3 across = vec3(-along.z, 0.0f, along.x);
    for (int i = 0; i < COURSE_COUNT; i++) {
        Object *o = place(world, &COURSE[i], start, along, across);
        if (!o || !World_add(world, o)) return false;
    }
    if (!who) return true;
    /* At the start of the middle lane, looking down the course */
    who->position = vec3(start.x, ground(world, start) + 0.05f, start.z);
    who->rotation.y = atan2f(along.z, along.x) + PI_F / 2.0f;
    Object_syncColliders(who);
    *yaw = atan2f(along.z, along.x);
    return true;
}