Entropie git · main
Entropie festival game; first steps
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.gzentropie/src/world/field.c 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;
}