Nimbin[12]?Games / Entropie / entropie/src/world/field.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/field.c 7.1 KB · 184 lines raw
#include "world/field.h"
#include "world/site.h"
#include "world/cast.h"

#include <math.h>
#include <string.h>

_Static_assert(FIELD_CELLS == WORLD_FIELD_CELLS, "world.h WORLD_FIELD_CELLS");

#define REFUGE_SPREAD 2.5f /* m: let-go snails round the refuge */
#define STAGE_CLEAR   3.6f /* m round the stage: no grass to mow there */

static unsigned mix(unsigned h) {
    h ^= h >> 16; h *= 0x7feb352du; h ^= h >> 15; h *= 0x846ca68bu; h ^= h >> 16;
    return h;
}
static float dice(unsigned k) { return (float)(mix(k) & 0xffffu) / 65535.0f; }

static int cellOf(float x, float z) {
    int ix = (int)floorf((x - FIELD_X0) / FIELD_CELL), iz = (int)floorf((z - FIELD_Z0) / FIELD_CELL);
    return ix < 0 || iz < 0 || ix >= FIELD_NX || iz >= FIELD_NZ ? -1 : iz * FIELD_NX + ix;
}

static Vec3 middleOf(const World *world, int c) {
    float x = FIELD_X0 + ((float)(c % FIELD_NX) + 0.5f) * FIELD_CELL, z = FIELD_Z0 + ((float)(c / FIELD_NX) + 0.5f) * FIELD_CELL;
    return vec3(x, world->has_terrain ? Terrain_height(&world->terrain, x, z) : 0.0f, z);
}

bool Field_inside(float x, float z) { return cellOf(x, z) >= 0; }

float Field_mownAt(const World *world, float x, float z) {
    int c = cellOf(x, z);
    if (c < 0) return 0.0f;
    return world->mown[c] == FIELD_OFF ? 1.0f : (float)world->mown[c] / (float)FIELD_MOWN;
}

static bool snailWild(const Object *o) { return o->type == OBJ_SNAIL && o->variant == 0 && !o->broken && !o->is_held; }

int Field_mow(World *world, Vec3 at, float radius, int *killed) {
    int done = 0, dead = 0;
    for (int dz = -2; dz <= 2; dz++)
        for (int dx = -2; dx <= 2; dx++) {
            int c = cellOf(at.x + (float)dx * FIELD_CELL, at.z + (float)dz * FIELD_CELL);
            if (c < 0 || world->mown[c] >= FIELD_MOWN) continue;
            Vec3 m = middleOf(world, c);
            if (hypotf(m.x - at.x, m.z - at.z) > radius + FIELD_CELL * 0.5f) continue;
            if (++world->mown[c] >= FIELD_MOWN) done++;
            world->mow_rev++;
            for (int i = 0; i < world->count; i++) { /* (a snail in the grass there: too late) */
                Object *o = world->objects[i];
                if (!snailWild(o) || cellOf(o->position.x, o->position.z) != c) continue;
                Object_useUp(o);
                world->snails_killed++;
                dead++;
            }
        }
    if (killed) *killed = dead;
    return done;
}

float Field_done(const World *world) {
    int all = 0, mown = 0;
    for (int c = 0; c < FIELD_CELLS; c++) {
        if (world->mown[c] == FIELD_OFF) continue;
        all++;
        if (world->mown[c] >= FIELD_MOWN) mown++;
    }
    return all ? (float)mown / (float)all : 1.0f;
}

bool Field_nextUnmown(const World *world, Vec3 from, Vec3 *out) {
    int best = -1;
    float best_d = 1e9f;
    for (int c = 0; c < FIELD_CELLS; c++) {
        if (world->mown[c] >= FIELD_MOWN) continue;
        Vec3 m = middleOf(world, c);
        float d = hypotf(m.x - from.x, m.z - from.z) + 0.3f * (float)(c / FIELD_NX); /* (row by row, roughly) */
        if (d < best_d) { best_d = d; best = c; }
    }
    if (best < 0) return false;
    *out = middleOf(world, best);
    return true;
}

static bool addSnail(World *world, Vec3 at, int variant) {
    at.y = World_groundHeight(world, at.x, at.z, at.y + 2.0f) + 0.02f;
    Object *s = Object_create(OBJ_SNAIL, PALETTE_WARM, at, vec3_zero(), (float)variant);
    if (!s) return false;
    s->rotation.y = 6.2832f * dice((unsigned)(at.x * 977.0f) ^ (unsigned)(at.z * 131.0f));
    Object_syncColliders(s);
    return World_add(world, s);
}

bool Field_prepare(World *world) {
    memset(world->mown, 0, sizeof world->mown);
    for (int i = 0; i < world->count; i++) { /* (under and round the stage: nothing to mow) */
        const Object *s = world->objects[i];
        if (s->type != OBJ_STAGE) continue;
        for (int c = 0; c < FIELD_CELLS; c++) {
            Vec3 m = middleOf(world, c);
            if (hypotf(m.x - s->position.x, m.z - s->position.z) < STAGE_CLEAR) world->mown[c] = FIELD_OFF;
        }
    }
    for (int k = 0; k < FIELD_SNAILS; k++) { /* the snails: about the field ... */
        float x = FIELD_X0 + 0.5f + dice(1000u + (unsigned)k) * ((float)FIELD_NX * FIELD_CELL - 1.0f);
        float z = FIELD_Z0 + 0.5f + dice(2000u + (unsigned)k) * ((float)FIELD_NZ * FIELD_CELL - 1.0f);
        int c = cellOf(x, z);
        if (c >= 0 && world->mown[c] == FIELD_OFF) continue;
        if (!addSnail(world, vec3(x, 0.0f, z), 0)) return false;
    }
    Vec3 orchard = Site_floorSpot(PROJECT_OBSTWIESE); /* ... and in the orchard, round where its stage goes */
    for (int k = 0; k < ORCHARD_SNAILS; k++) {
        float a = 6.2832f * dice(3000u + (unsigned)k), r = 4.5f + 6.0f * dice(4000u + (unsigned)k);
        if (!addSnail(world, vec3(orchard.x + cosf(a) * r, orchard.y, orchard.z + sinf(a) * r), 0)) return false;
    }
    return true;
}

int Field_snailsInField(const World *world) {
    int n = 0;
    for (int i = 0; i < world->count; i++)
        if (snailWild(world->objects[i]) && Field_inside(world->objects[i]->position.x, world->objects[i]->position.z)) n++;
    return n;
}

int Field_snailsLeft(const World *world) {
    int n = 0;
    for (int i = 0; i < world->count; i++) if (snailWild(world->objects[i])) n++;
    return n;
}

bool Field_pickSnail(World *world, Object *who, Object *snail) {
    if (!snailWild(snail) || who->snails >= SNAIL_BUCKET) return false;
    (void)world;
    Object_useUp(snail);
    who->snails++;
    return true;
}

int Field_release(World *world, Object *who) {
    int n = who->snails;
    if (n <= 0) return 0;
    Vec3 refuge = Site_snailRefuge();
    for (int k = 0; k < n; k++) {
        unsigned seed = (unsigned)(world->snails_rescued + k) * 7919u;
        float a = 6.2832f * dice(seed), r = REFUGE_SPREAD * sqrtf(dice(seed + 1u));
        addSnail(world, vec3(refuge.x + cosf(a) * r, refuge.y, refuge.z + sinf(a) * r), 1);
    }
    world->snails_rescued += n;
    who->snails = 0;
    return n;
}

Object *Field_artLog(const World *world) {
    Vec3 spot = Site_artSpot();
    Object *best = NULL;
    float best_d = ART_REACH;
    for (int i = 0; i < world->count; i++) {
        Object *o = world->objects[i];
        if (o->type != OBJ_LOG || o->is_held || o->lifted || o->broken || o->size.y < 0.8f || o->size.x < 0.2f) continue;
        float d = hypotf(o->position.x - spot.x, o->position.z - spot.z);
        if (d < best_d) { best_d = d; best = o; }
    }
    return best;
}

Object *Field_carve(World *world, Object *log) {
    Vec3 at = Object_colliderCenter(log, 0);
    float d = log->size.x, h = fminf(fmaxf(log->size.y, 0.9f), 1.7f);
    at.y = World_groundHeight(world, at.x, at.z, at.y + 1.0f);
    Object *art = Object_create(OBJ_SCULPTURE, PALETTE_WARM, at, vec3(d, h, d), (float)(Field_artPieces(world) % 3));
    if (!art) return NULL;
    art->rotation.y = log->rotation.y;
    Object_syncColliders(art);
    if (!World_add(world, art)) { Object_destroy(art); return NULL; }
    Object_useUp(log);
    return art;
}

int Field_artPieces(const World *world) {
    int n = 0;
    for (int i = 0; i < world->count; i++) if (world->objects[i]->type == OBJ_SCULPTURE && !world->objects[i]->broken) n++;
    return n;
}