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/tent.c raw
#include "world/tent.h"
#include "world/car.h"
#include "people/hands.h"
#include "render/shapes.h"
#include "world/weather.h"
#include <stdio.h>
#include <string.h>
#define LAY_TIME 4.0f /* s (one person, average): spreading it out, the poles through their sleeves */
#define POLES_SMALL 16.0f /* ... the poles in and up */
#define POLES_BIG 24.0f
#define COVER_SMALL 10.0f /* ... the cover over it */
#define COVER_BIG 14.0f
#define PEG_TIME 2.5f /* ... each peg (with a stone to hit it: twice as quick) */
#define DOOR_OUT 0.45f /* in front of the door: this far out from the cover */
#define BED_REACH 0.8f /* a sleeping bag or mat put down this near the door goes in */
#define DOWN_QUICKER 0.6f /* taking it down: each bit this much of the time putting it up took ... */
#define FOLD_TIME 7.0f /* ... and folding it up into its bag at the end */
/* How far the stage shows: up, its work; coming down, the other way round. */
static float shownWork(const Object *tent) {
return tent->tent_down ? 1.0f - tent->tent_work : tent->tent_work;
}
static Vec3 local(const Object *tent, Vec3 p) {
return vec3_add(tent->position, mat4_mul_dir(mat4_rotate_y(tent->rotation.y), p));
}
static Vec3 toLocal(const Object *tent, Vec3 w) {
return mat4_mul_dir_transposed(mat4_rotate_y(tent->rotation.y), vec3_sub(w, tent->position));
}
/* Everybody's own cover colour (by name, like their other things). */
static Vec3 flyColor(const Object *tent) {
const Object *o = tent->owner;
if (o && o->guest) { /* the guests': all sorts */
static const Vec3 FLY[] = { { 0.78f, 0.22f, 0.20f }, { 0.20f, 0.42f, 0.70f }, { 0.85f, 0.62f, 0.15f }, { 0.25f, 0.55f, 0.35f },
{ 0.55f, 0.30f, 0.62f }, { 0.15f, 0.60f, 0.62f }, { 0.60f, 0.60f, 0.58f }, { 0.88f, 0.42f, 0.18f } };
return FLY[((o->guest - 1) / 2) % 8];
}
if (tent->variant) return vec3(0.40f, 0.44f, 0.24f); /* the big one: olive */
if (!o || o->type == OBJ_PLAYER) return vec3(0.22f, 0.36f, 0.56f); /* yours: blue */
if (!strcmp(o->name, "Toni")) return vec3(0.78f, 0.42f, 0.16f); /* Toni's: orange */
return vec3(0.30f, 0.50f, 0.30f);
}
/* Its look and its box for how far it's up (built again only when that shows). */
static void reshape(Object *tent) {
int big = tent->variant, steps = tent->tent_stage == TENT_POLES || tent->tent_stage == TENT_COVER ? (int)(shownWork(tent) * 12.0f) : 0;
int shown = tent->tent_stage * 1000 + steps * 10 + tent->stored;
if (shown == tent->shown) return;
tent->shown = shown;
Mesh_destroy(&tent->mesh);
tent->mesh = Shapes_tent(big, tent->tent_stage, (float)steps / 12.0f, tent->stored, vec3(0.90f, 0.86f, 0.62f), flyColor(tent));
float h = tent->tent_stage == TENT_LAID ? 0.04f : tent->tent_stage == TENT_POLES ? fmaxf(0.04f, TENT_H(big) * shownWork(tent)) : TENT_H(big);
tent->colliders[0].center = vec3(0.0f, h * 0.5f, 0.0f);
tent->colliders[0].half_extents = vec3(TENT_W(big) * 0.5f, h * 0.5f, TENT_L(big) * 0.5f);
Object_syncColliders(tent);
}
Object *Tent_unpack(World *world, Object *bag, Vec3 door_dir) {
if (!bag || bag->type != OBJ_GEAR || bag->variant < GEAR_TENT_SMALL || bag->is_held || bag->broken) return NULL;
int big = bag->variant == GEAR_TENT_BIG;
Vec3 at = bag->position;
at.y = Terrain_height(&world->terrain, at.x, at.z); /* (on the ground: not on the sleeping bags lying there - it hovered) */
Object *tent = Object_create(OBJ_TENT, PALETTE_STANDARD, at, vec3_zero(), big ? 1.0f : 0.0f);
if (!tent) return NULL;
tent->owner = bag->owner;
if (big && bag->owner && !strcmp(bag->owner->name, "Volkram")) /* (the big one is Volkram's and Jana's) */
for (int i = 0; i < world->count; i++)
if (world->objects[i]->type == OBJ_HUMAN && !strcmp(world->objects[i]->name, "Jana")) tent->owner2 = world->objects[i];
snprintf(tent->name, sizeof(tent->name), "Tent");
door_dir = vec3_normalize(vec3(door_dir.x, 0.0f, door_dir.z));
Object_setRotation(tent, vec3(0.0f, yaw_facing(door_dir), 0.0f));
reshape(tent);
if (!World_add(world, tent)) return NULL;
Object_useUp(bag);
return tent;
}
static bool hasStone(const Object *who) {
for (int h = 0; h < 2; h++)
if (who->hands.item[h] && who->hands.state[h] == HAND_HOLDING && who->hands.item[h]->type == OBJ_STONE) return true;
return false;
}
void Tent_takeDown(Object *const *objects, int count, Object *tent) {
if (tent->type != OBJ_TENT || tent->tent_down || tent->broken) return;
for (int i = 0; i < count; i++) { /* (anybody in it: out) */
Object *o = objects[i];
if (o->in_tent != tent) continue;
o->rain_nap = false;
if (o->body.asleep) Body_getUp(&o->body);
Tent_crawlOut(o);
}
tent->tent_down = true;
tent->tent_work = 0.0f;
if (tent->tent_stage >= TENT_UP) { tent->tent_stage = TENT_PEGS; tent->stored = TENT_PEGS(tent->variant); }
else if (tent->tent_stage == TENT_PEGS && tent->stored == 0) tent->tent_stage = TENT_COVER;
reshape(tent);
}
void Tent_refresh(Object *tent) {
tent->shown = -1;
reshape(tent);
}
bool Tent_movable(const Object *tent) {
return tent && tent->type == OBJ_TENT && !tent->broken &&
(tent->tent_stage < TENT_PEGS || (tent->tent_stage == TENT_PEGS && tent->stored == 0) || (tent->tent_down && tent->tent_stage <= TENT_COVER));
}
void Tent_startMove(Object *tent) {
if (tent->tent_down) { /* (the pegs out on the way down: now it's going up again where it's put) */
tent->tent_down = false;
if (tent->tent_stage == TENT_COVER) { tent->tent_stage = TENT_PEGS; tent->stored = 0; }
tent->tent_work = 0.0f;
reshape(tent);
}
tent->tent_dragged = true;
}
/* Coming down: the pegs out, the cover off, the poles down, folded up (packed in Tent_update). */
static int workDown(Object *tent, float rate, float dt) {
int big = tent->variant;
float time = tent->tent_stage == TENT_LAID ? FOLD_TIME
: DOWN_QUICKER * (tent->tent_stage == TENT_POLES ? (big ? POLES_BIG : POLES_SMALL)
: tent->tent_stage == TENT_COVER ? (big ? COVER_BIG : COVER_SMALL) : PEG_TIME);
tent->tent_work += dt * rate / time;
tent->tent_idle = 0.0f;
int done = -1;
if (tent->tent_work >= 1.0f) {
if (tent->tent_stage == TENT_LAID) { tent->tent_work = 1.0f; return TENT_DOWN_PACKED; } /* (Tent_update packs it) */
tent->tent_work = 0.0f;
if (tent->tent_stage == TENT_PEGS) {
if (--tent->stored <= 0) { tent->stored = 0; tent->tent_stage = TENT_COVER; done = TENT_DOWN + TENT_COVER; }
} else {
tent->tent_stage--;
done = TENT_DOWN + tent->tent_stage;
}
}
reshape(tent);
return done;
}
int Tent_work(Object *tent, Object *who, float dt) {
if (tent->type != OBJ_TENT || tent->broken || tent->tent_dragged || (tent->tent_stage >= TENT_UP && !tent->tent_down)) return -1;
int big = tent->variant;
float rate = (0.75f + 0.5f * who->wits) * (1.0f - 0.5f * fminf(who->drunk, 1.0f));
if (tent->tent_down) return workDown(tent, rate, dt);
float time = tent->tent_stage == TENT_LAID ? LAY_TIME
: tent->tent_stage == TENT_POLES ? (big ? POLES_BIG : POLES_SMALL)
: tent->tent_stage == TENT_COVER ? (big ? COVER_BIG : COVER_SMALL) : PEG_TIME / (hasStone(who) ? 2.0f : 1.0f);
tent->tent_work += dt * rate / time;
tent->tent_idle = 0.0f;
int done = -1;
if (tent->tent_work >= 1.0f) {
tent->tent_work = 0.0f;
if (tent->tent_stage == TENT_PEGS) {
if (++tent->stored >= TENT_PEGS(big)) { tent->tent_stage = TENT_UP; done = TENT_UP; }
} else {
tent->tent_stage++;
done = tent->tent_stage;
}
}
reshape(tent);
return done;
}
const char *Tent_job(const Object *tent) {
if (tent->tent_down) switch (tent->tent_stage) {
case TENT_LAID: return "folding it up into its bag";
case TENT_POLES: return "taking the poles down";
case TENT_COVER: return "pulling the cover off";
default: return "pulling the pegs out";
}
switch (tent->tent_stage) {
case TENT_LAID: return "spreading it out";
case TENT_POLES: return "putting the poles in";
case TENT_COVER: return "pulling the cover over";
case TENT_PEGS: return "hammering the pegs in";
default: return "done";
}
}
static Vec3 nextPeg(const Object *tent) {
float x, z;
Shapes_tentPeg(tent->variant, tent->tent_down ? (tent->stored > 0 ? tent->stored - 1 : 0) : tent->stored, &x, &z);
return vec3(x, 0.0f, z);
}
Vec3 Tent_workSpot(const Object *tent, float side) {
const float hw = TENT_W(tent->variant) * 0.5f, hl = TENT_L(tent->variant) * 0.5f;
float s = side < 0.0f ? -1.0f : 1.0f;
switch (tent->tent_stage) {
case TENT_COVER: return local(tent, vec3(s * 0.5f * fabsf(side), 0.0f, -hl * 1.14f - 0.55f)); /* at the back, pulling it over */
case TENT_PEGS: {
Vec3 p = nextPeg(tent), out = vec3_normalize(vec3(p.x, 0.0f, p.z));
return local(tent, vec3_add(p, vec3_scale(out, 0.45f)));
}
default: return local(tent, vec3(s * (hw + 0.55f), 0.0f, 0.0f)); /* beside it */
}
}
Vec3 Tent_handsAt(const Object *tent, const Object *who, float clock) {
const int big = tent->variant;
const float hw = TENT_W(big) * 0.5f, hl = TENT_L(big) * 0.5f, h = TENT_H(big);
Vec3 me = toLocal(tent, who->position);
float s = me.x < 0.0f ? -1.0f : 1.0f;
switch (tent->tent_stage) {
case TENT_LAID: return local(tent, vec3(s * hw, 0.08f, clampf(me.z, -hl, hl)));
case TENT_POLES: return local(tent, vec3(s * hw * 0.7f, fmaxf(0.1f, 0.55f * h * shownWork(tent)), clampf(me.z, -hl * 0.5f, hl * 0.5f)));
case TENT_COVER: return local(tent, vec3(s * 0.3f, h * 0.8f, -hl + 2.0f * hl * shownWork(tent)));
case TENT_PEGS: {
Vec3 p = nextPeg(tent);
p.y = 0.06f + 0.14f * fabsf(sinf(clock * 7.0f)); /* (hitting it in) */
return local(tent, p);
}
default: return Tent_door(tent);
}
}
Vec3 Tent_door(const Object *tent) {
return local(tent, vec3(0.0f, 0.0f, TENT_L(tent->variant) * 0.5f * 1.14f + DOOR_OUT));
}
bool Tent_isTheirs(const Object *tent, const Object *who) {
return tent && tent->type == OBJ_TENT && who && (tent->owner == who || tent->owner2 == who);
}
Object *Tent_of(Object *const *objects, int count, const Object *who) {
for (int i = 0; i < count; i++)
if (objects[i]->type == OBJ_TENT && !objects[i]->broken && Tent_isTheirs(objects[i], who)) return objects[i];
return NULL;
}
static int slotOf(const Object *tent, const Object *who) { return tent->owner2 == who ? 1 : 0; }
bool Tent_hasBed(const Object *tent, const Object *who) {
return tent && (tent->tent_beds & (1 << (2 * slotOf(tent, who)))) != 0;
}
void Tent_crawlIn(Object *tent, Object *who) {
const int big = tent->variant;
float x = big ? (slotOf(tent, who) ? 0.45f : -0.45f) : 0.0f;
Vec3 at = local(tent, vec3(x, 0.0f, TENT_L(big) * 0.5f - 0.95f)); /* (lying back into it, the feet towards the door) */
who->in_tent = tent;
who->sitting = false;
who->position = vec3(at.x, tent->position.y, at.z);
who->rotation.y = tent->rotation.y; /* (facing the door) */
who->velocity = vec3_zero();
who->movement = vec3_zero();
Object_syncColliders(who);
who->body.last_facing = Object_facing(who); /* (lies back from where they face now: the head to the back of the tent) */
Body_lieDown(&who->body);
}
void Tent_crawlOut(Object *who) {
Object *tent = who->in_tent;
if (!tent) return;
Vec3 door = Tent_door(tent);
who->in_tent = NULL;
who->position = vec3(door.x, tent->position.y, door.z);
who->rotation.y = tent->rotation.y;
who->body.replant = true;
Object_syncColliders(who);
}
/* Folded up: its bag where it lay (left packed), the sleeping bags and mats that were in it at the door. */
static void pack(World *world, Object *tent) {
const int big = tent->variant;
Object *bag = Gear_create(big ? GEAR_TENT_BIG : GEAR_TENT_SMALL, tent->owner, vec3_add(tent->position, vec3(0.0f, 0.02f, 0.0f)));
if (bag) {
bag->keep = true;
bag->rotation.y = tent->rotation.y;
Object_syncColliders(bag);
if (!World_add(world, bag)) Object_destroy(bag);
}
Object *sleeper[2] = { tent->owner, tent->owner2 };
for (int k = 0; k < 4; k++) {
if (!(tent->tent_beds & (1 << k)) || !sleeper[k / 2]) continue;
Vec3 at = local(tent, vec3((big ? (k / 2 ? 0.45f : -0.45f) : 0.0f) + (k % 2 ? 0.3f : -0.3f), 0.0f, TENT_L(big) * 0.5f * 1.14f + DOOR_OUT + 0.3f));
at.y = World_groundHeight(world, at.x, at.z, at.y + 0.5f) + 0.02f;
Object *thing = Gear_create(k % 2 ? GEAR_MAT : GEAR_SLEEPING_BAG, sleeper[k / 2], at);
if (!thing) continue;
thing->rotation.y = tent->rotation.y;
Object_syncColliders(thing);
if (!World_add(world, thing)) Object_destroy(thing);
}
tent->tent_beds = 0;
tent->owner = tent->owner2 = NULL;
Object_useUp(tent);
}
/* The wind and a tent not pegged down (pegs under half in): it slides and
turns along the ground, more standing on its poles than lying flat, till
it snags on something or somebody gets hold of it (working on it). */
#define BLOW_STANDING 4.0f /* m/s: on its poles, it goes from this wind ... */
#define BLOW_FLAT 6.5f /* ... lying flat from this */
#define BLOW_PUSH 0.13f /* m/s it slides for each m/s over that */
#define BLOW_HELD 1.5f /* s: worked on this recently, it's held */
static bool notIt(const Object *o, const void *tent) { return o == tent || o->is_character || !o->is_static; }
static void blowAbout(World *world, Object *tent, float dt) {
if (tent->tent_stage >= TENT_UP || (tent->tent_stage == TENT_PEGS && tent->stored * 2 >= TENT_PEGS(tent->variant)) ||
tent->tent_idle < BLOW_HELD) return;
float speed = Weather_windSpeed(world), from = tent->tent_stage == TENT_LAID ? BLOW_FLAT : BLOW_STANDING;
if (speed <= from) return;
Vec3 dir = vec3_scale(world->wind, 1.0f / speed);
float push = (speed - from) * BLOW_PUSH * (tent->tent_stage == TENT_LAID ? 0.5f : 1.0f);
float reach = 0.5f * fmaxf(TENT_W(tent->variant), TENT_L(tent->variant)) + 0.15f;
Vec3 from_p = vec3_add(tent->position, vec3(0.0f, 0.3f, 0.0f));
if (World_rayCast(world, from_p, dir, reach, notIt, tent, NULL, NULL) >= 0.0f) return; /* (snagged on something) */
tent->position = vec3_add(tent->position, vec3_scale(dir, push * dt));
tent->position.y = Terrain_height(&world->terrain, tent->position.x, tent->position.z);
Object_setRotation(tent, vec3(0.0f, tent->rotation.y + 0.06f * push * sinf(tent->position.x * 1.7f + tent->position.z) * dt, 0.0f));
Object_syncColliders(tent);
world->tent_blowing = tent;
tent->tent_blown = 6.0f; /* (its owner comes running) */
}
#define RAIN_NAP_END 0.15f /* rain: under this, out of the tent again */
void Tent_update(World *world, float dt) {
world->tent_blowing = NULL;
for (int i = 0; i < world->count; i++) {
Object *o = world->objects[i];
if (o->type == OBJ_TENT) {
o->tent_idle += dt;
o->tent_blown = fmaxf(0.0f, o->tent_blown - dt);
if (o->tent_down && !o->broken && o->tent_stage == TENT_LAID && o->tent_work >= 1.0f) pack(world, o);
else if (!o->broken) blowAbout(world, o, dt);
continue;
}
if (o->rain_nap && (!o->body.asleep || world->rain < RAIN_NAP_END)) { /* in out of the rain: up when it stops */
o->rain_nap = false;
if (o->body.asleep && o->needs.sleepiness < 0.6f) Body_getUp(&o->body);
}
if (o->is_character && o->in_tent && !o->body.asleep && o->body.pose == POSE_UP) { Tent_crawlOut(o); continue; }
/* a sleeping bag or a mat put down at the door of its owner's tent: in it goes */
if (o->type != OBJ_GEAR || o->variant > GEAR_MAT || o->is_held || o->broken || !o->owner) continue;
Object *tent = Tent_of(world->objects, world->count, o->owner);
if (!tent || tent->tent_stage < TENT_COVER) continue;
Vec3 door = Tent_door(tent);
if (hypotf(o->position.x - door.x, o->position.z - door.z) > BED_REACH) continue;
tent->tent_beds |= 1 << (2 * slotOf(tent, o->owner) + (o->variant == GEAR_MAT ? 1 : 0));
Object_useUp(o);
}
}