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/scene.c raw
#include "world/scene.h"
#include "world/site.h"
#include "world/cast.h"
#include <SDL3/SDL.h>
typedef struct {
ObjectType type;
Palette palette;
Vec3 position;
Vec3 size;
float weight;
Vec3 rotation_deg;
} Spawn;
#define HUMAN_SIZE { 0.5f, 1.75f, 0.25f }
#define NO_ROTATION { 0, 0, 0 }
/* The yard where it all starts, around Site_yard(). The player must be the
first entry. Order also defines physics order. */
static const Spawn SCENE[] = {
/* Characters */
{ OBJ_PLAYER, PALETTE_MODERN, { 1, 1, 0 }, HUMAN_SIZE, 75, NO_ROTATION }, /* (dropped onto the ground: low, no hard landing) */
{ OBJ_HUMAN, PALETTE_MODERN, { -1.5f, 1, -3 }, HUMAN_SIZE, 88, { 0, 180, 0 } }, /* Volkram, facing the player */
{ OBJ_HUMAN, PALETTE_MODERN, { 2.5f, 1, -4 }, HUMAN_SIZE, 60, { 0, 160, 0 } }, /* Jana */
{ OBJ_HUMAN, PALETTE_MODERN, { -5.0f, 1, 4 }, HUMAN_SIZE, 70, { 0, 120, 0 } }, /* Toni */
{ OBJ_HUMAN, PALETTE_MODERN, { 6.0f, 1, 3 }, HUMAN_SIZE, 72, { 0, 250, 0 } }, /* Kurti */
{ OBJ_HUMAN, PALETTE_MODERN, { 4.0f, 1, -6 }, HUMAN_SIZE, 92, { 0, 200, 0 } }, /* Martin */
{ OBJ_HUMAN, PALETTE_MODERN, { -3.0f, 1, -6 }, HUMAN_SIZE, 76, { 0, 150, 0 } }, /* Jonas */
/* Props (the ground itself is the terrain, see terrain.c) */
{ OBJ_TREE, PALETTE_WARM, { 3, 0, 2 }, { 3.0f, 3.0f, 0.0f }, 0, NO_ROTATION }, /* canopy, trunk */
/* (the tools are in the tool room of your garages, see site.c) */
/* A few apples (the drinks come in the crates from your car) */
{ OBJ_APPLE, PALETTE_WARM, { 2.2f, 0.2f, 1.2f }, { 0.08f, 0.08f, 0.08f }, 0.2f, NO_ROTATION },
{ OBJ_APPLE, PALETTE_WARM, { 3.9f, 0.2f, 1.4f }, { 0.08f, 0.08f, 0.08f }, 0.2f, NO_ROTATION },
{ OBJ_APPLE, PALETTE_WARM, { 2.6f, 0.2f, 3.1f }, { 0.08f, 0.08f, 0.08f }, 0.2f, NO_ROTATION },
/* A log to cut and split */
{ OBJ_LOG, PALETTE_WARM, { 5.8f, -0.25f, 0.2f }, { 0.36f, 1.4f, 0.0f }, 0, { 90, 20, 0 } },
};
/* Things to try things out with (carrying, pushing, lifting together), out
of the way beside the obstacle course: positions from its test corner. */
static const Spawn TESTING[] = {
/* Things to carry: one hand (small) or both hands (medium crate) */
{ OBJ_BOX, PALETTE_WARM, { 0.0f, 0.5f, -2.0f }, { 0.40f, 0.40f, 0.40f }, 6, NO_ROTATION },
{ OBJ_BOX, PALETTE_MODERN, { 1.2f, 0.5f, -2.2f }, { 0.35f, 0.35f, 0.35f }, 5, { 0, 30, 0 } },
{ OBJ_CYLINDER, PALETTE_MODERN, { 2.0f, 0.5f, -1.8f }, { 0.30f, 0.45f, 0.30f }, 4, NO_ROTATION },
{ OBJ_BOX, PALETTE_STANDARD, { 0.5f, 0.5f, -5.5f }, { 0.80f, 0.60f, 0.60f }, 20, NO_ROTATION },
/* Pushable objects */
{ OBJ_CYLINDER, PALETTE_WARM, { -3, 0, 1 }, { 1, 3, 1 }, 1200, { 90, 0, 0 } }, /* takes two or more */
{ OBJ_CYLINDER, PALETTE_MODERN, { -3, 0, 4 }, { 1, 3, 1 }, 100, NO_ROTATION },
{ OBJ_BOX, PALETTE_MODERN, { 1, 0, 2 }, { 2, 1, 2 }, 20, NO_ROTATION },
};
/* Who they are, how they look and their part in it: world/cast.c (the people
above, in that order: you, Volkram, Jana, Toni, Kurti, Martin, Jonas). */
/* Where the things to try out stand: beside the obstacle course, left of its lanes. */
#define TEST_ACROSS -9.5f /* m across from the middle lane (the lanes are 3.5 apart) */
#define TEST_ALONG 6.0f /* m down from its start */
static Vec3 testCorner(void) {
Vec3 start, along;
Site_courseFrame(&start, &along);
Vec3 across = vec3(-along.z, 0.0f, along.x);
return vec3_add(start, vec3_add(vec3_scale(across, TEST_ACROSS), vec3_scale(along, TEST_ALONG)));
}
/* A ramp to walk up: a plank 20 degrees up, its foot on the ground, its top
resting on a block (a loose box left tilted would hang in the air: loose
things don't turn in the physics). */
#define RAMP_AT { 4.0f, 0.0f, 8.0f }
#define RAMP_SIZE { 1.2f, 0.15f, 3.2f }
#define RAMP_SLOPE 20.0f
static bool addRamp(World *world, Vec3 corner) {
const Vec3 at = vec3_add(corner, (Vec3)RAMP_AT);
Object *ramp = Object_create(OBJ_BOX_STATIC, PALETTE_MODERN, at, (Vec3)RAMP_SIZE, 100.0f);
if (!ramp) return false;
Object_setRotation(ramp, vec3(deg2rad(RAMP_SLOPE), 0.0f, 0.0f));
/* its lowest and highest corners: the low end goes onto the ground */
const Collider *c = &ramp->colliders[0];
Vec3 center = Object_colliderCenter(ramp, 0), low = center, high = center;
for (int k = 0; k < 8; k++) {
Vec3 p = center;
p = vec3_add(p, vec3_scale(c->axes[0], (k & 1 ? 1.0f : -1.0f) * c->half_extents.x));
p = vec3_add(p, vec3_scale(c->axes[1], (k & 2 ? 1.0f : -1.0f) * c->half_extents.y));
p = vec3_add(p, vec3_scale(c->axes[2], (k & 4 ? 1.0f : -1.0f) * c->half_extents.z));
if (p.y < low.y) low = p;
if (p.y > high.y) high = p;
}
ramp->position.y += Terrain_height(&world->terrain, low.x, low.z) - 0.03f - low.y;
Object_syncColliders(ramp);
if (!World_add(world, ramp)) return false;
/* the block under its top end (up to the ramp's underside there) */
Vec3 up_end = vec3_add(center, vec3_scale(c->axes[2], vec3_dot(vec3_sub(high, center), c->axes[2]) > 0.0f ? c->half_extents.z : -c->half_extents.z));
up_end.y += Terrain_height(&world->terrain, low.x, low.z) - 0.03f - low.y;
Vec3 out = vec3_normalize(vec3(c->axes[2].x, 0.0f, c->axes[2].z));
if (vec3_dot(out, vec3_sub(high, low)) < 0.0f) out = vec3_scale(out, -1.0f);
Vec3 foot = vec3_sub(up_end, vec3_scale(out, 0.25f)); /* (a bit in from the end: the plank sticks out over it) */
float ground = Terrain_height(&world->terrain, foot.x, foot.z);
float under = up_end.y - fabsf(c->axes[1].y) * c->half_extents.y - 0.12f - ground; /* (the underside, a bit lower there) */
if (under < 0.1f) return true;
Object *block = Object_create(OBJ_BOX_STATIC, PALETTE_GROUND, vec3(foot.x, ground, foot.z), vec3(1.3f, under, 0.4f), 100.0f);
return block && World_add(world, block);
}
bool Scene_finish(World *world) {
/* Everybody knows where the firewood goes: the woodpile by the barn */
Object *pile = NULL;
for (int i = 0; i < world->count && !pile; i++)
if (world->objects[i]->type == OBJ_WOODPILE) pile = world->objects[i];
for (int i = 0; i < world->count && pile; i++)
if (world->objects[i]->type == OBJ_HUMAN) Ai_setWoodpile(world->objects[i], pile);
/* Kurti and his sons sit at the fire when you come (they welcome you when you go over) */
Object *fire = NULL, *benches[3] = { NULL, NULL, NULL };
int nb = 0;
for (int i = 0; i < world->count; i++) {
Object *o = world->objects[i];
if (o->type == OBJ_FIREPLACE && !fire) fire = o;
}
for (int i = 0; i < world->count && fire && nb < 3; i++) {
Object *o = world->objects[i];
if (o->type == OBJ_FURNITURE && o->variant == FURN_BENCH && vec3_length(vec3_sub(o->position, fire->position)) < 5.0f) benches[nb++] = o;
}
for (int i = 0, k = 0; i < world->count && k < nb; i++) {
Object *o = world->objects[i];
if (o->type != OBJ_HUMAN || !o->lives_here) continue;
Object *bench = benches[k++];
Vec3 out = vec3_normalize(vec3_sub(fire->position, bench->position));
o->position = vec3_add(bench->position, vec3(out.x * 0.55f, 0.0f, out.z * 0.55f));
o->position.y = Terrain_height(&world->terrain, o->position.x, o->position.z);
Object_syncColliders(o);
Object_sit(o, bench, fire->position);
o->ai.mode = AI_IDLE;
o->ai.timer = 150.0f + 30.0f * (float)k; /* (sitting there a while) */
}
return addRamp(world, testCorner());
}
Object *Scene_load(World *world) {
Object *player = NULL;
int characters = 0;
const Vec3 yard = Site_yard();
for (size_t i = 0; i < SDL_arraysize(SCENE); i++) {
const Spawn *s = &SCENE[i];
bool person = s->type == OBJ_PLAYER || s->type == OBJ_HUMAN;
Object *obj = person ? Cast_create(characters++, vec3_add(s->position, yard))
: Object_create(s->type, s->palette, vec3_add(s->position, yard), s->size, s->weight);
if (!obj) return NULL;
Vec3 r = s->rotation_deg;
if (r.x != 0.0f || r.y != 0.0f || r.z != 0.0f)
Object_setRotation(obj, vec3(deg2rad(r.x), deg2rad(r.y), deg2rad(r.z)));
if (!World_add(world, obj)) return NULL;
if (s->type == OBJ_PLAYER && !player) player = obj;
}
/* The things to try out, by the obstacle course */
const Vec3 corner = testCorner();
for (size_t i = 0; i < SDL_arraysize(TESTING); i++) {
const Spawn *s = &TESTING[i];
Object *obj = Object_create(s->type, s->palette, vec3_add(s->position, corner), s->size, s->weight);
if (!obj) return NULL;
Vec3 r = s->rotation_deg;
if (r.x != 0.0f || r.y != 0.0f || r.z != 0.0f) Object_setRotation(obj, vec3(deg2rad(r.x), deg2rad(r.y), deg2rad(r.z)));
if (!World_add(world, obj)) return NULL;
}
/* Everything else: the farm, your buildings, the forest */
return Site_build(world) ? player : NULL;
}