#include "world/stage.h" #include "render/shapes.h" #include #include #define STAGE_W 6.0f /* m wide (along its front) */ #define STAGE_D 4.0f /* m deep */ #define LOOK_STEPS 8 /* the look changes in this many steps through each part of the work */ /* Person-hours each step takes (someone of middling skill). */ static const float NEED[BUILD_DONE] = { [BUILD_MARKED] = 1.5f, /* the ground cleared, levelled */ [BUILD_FRAME] = 4.0f, /* posts in, beams on (and the logs for it) */ [BUILD_FLOOR] = 4.0f, /* the deck */ [BUILD_ROOF] = 3.0f, /* the back wall, the roof */ [BUILD_DECOR] = 2.5f, /* paint, banners */ }; /* Each floor's colour (the paint on the back wall, the banners). */ static Vec3 accent(int project) { switch (project) { case PROJECT_HOFBUEHNE: return vec3(0.78f, 0.18f, 0.16f); /* bands: red */ case PROJECT_OBSTWIESE: return vec3(0.52f, 0.20f, 0.70f); /* psytrance: purple */ case PROJECT_KARTOFFELACKER: return vec3(0.90f, 0.55f, 0.15f); /* world music: orange */ case PROJECT_HIDDEN: return vec3(0.16f, 0.22f, 0.50f); /* techno: deep blue */ default: return vec3(0.5f, 0.5f, 0.5f); } } Object *Stage_create(Project project, Vec3 at, float yaw) { Object *s = Object_create(OBJ_STAGE, PALETTE_WARM, at, vec3(STAGE_W, 3.6f, STAGE_D), (float)project); if (!s) return NULL; s->rotation = vec3(0.0f, yaw, 0.0f); Object_syncColliders(s); Stage_show(s); return s; } Object *Stage_of(Object *const *objects, int count, Project project) { for (int i = 0; i < count; i++) if (objects[i]->type == OBJ_STAGE && objects[i]->variant == (int)project) return objects[i]; return NULL; } const char *Stage_stepName(int stage) { static const char *const NAME[] = { "clearing the ground", "the frame", "the deck", "the back wall and the roof", "paint and banners", "done" }; return NAME[stage < 0 ? 0 : stage > BUILD_DONE ? BUILD_DONE : stage]; } float Stage_part(const Object *s) { if (s->build_stage >= BUILD_DONE) return 1.0f; return fminf(1.0f, s->build_work / NEED[s->build_stage]); } /* What each step takes, lying by it: logs, then the bought material (MaterialKind). */ static const int NEED_LOGS[BUILD_DONE] = { [BUILD_FRAME] = STAGE_LOGS }; static const int NEED_MAT[BUILD_DONE][MATERIAL_KINDS] = { [BUILD_FLOOR] = { [MATERIAL_BOARDS] = 2, [MATERIAL_NAILS] = 1 }, [BUILD_ROOF] = { [MATERIAL_BOARDS] = 2, [MATERIAL_SCREWS] = 1 }, [BUILD_DECOR] = { [MATERIAL_PAINT] = 1 }, }; /* Things of a kind lying by it (not held, not used up): logs (kind -1) or material. */ static int lyingBy(const World *world, const Object *s, int kind, Object **found, int max) { int n = 0; for (int i = 0; i < world->count && n < max; i++) { Object *o = world->objects[i]; if (o->is_held || o->broken) continue; if (kind < 0 ? o->type != OBJ_LOG : o->type != OBJ_MATERIAL || o->variant != kind) continue; float dx = o->position.x - s->position.x, dz = o->position.z - s->position.z; if (dx * dx + dz * dz < STAGE_LOG_REACH * STAGE_LOG_REACH) { if (found) found[n] = o; n++; } } return n; } static int needOf(int stage, int kind) { if (stage < 0 || stage >= BUILD_DONE) return 0; return kind < 0 ? NEED_LOGS[stage] : NEED_MAT[stage][kind]; } int Stage_missing(const World *world, const Object *s, char *what, size_t size) { static const char *const ONE[] = { "log", "bundle of boards", "box of nails", "box of screws", "bucket of paint" }; static const char *const MORE[] = { "logs", "bundles of boards", "boxes of nails", "boxes of screws", "buckets of paint" }; int total = 0; size_t len = 0; if (what && size) what[0] = 0; for (int kind = -1; kind < MATERIAL_KINDS; kind++) { int need = needOf(s->build_stage, kind); if (!need) continue; int have = lyingBy(world, s, kind, NULL, need), gap = need - have; if (gap <= 0) continue; total += gap; if (what && len < size) { if (gap == 1) snprintf(what + len, size - len, "%sa %s", len ? " and " : "", ONE[kind + 1]); else snprintf(what + len, size - len, "%s%d %s", len ? " and " : "", gap, MORE[kind + 1]); while (len < size && what[len]) len++; } } return total; } int Stage_missingMaterial(const World *world, const Object *s) { for (int kind = 0; kind < MATERIAL_KINDS; kind++) { int need = needOf(s->build_stage, kind); if (need && lyingBy(world, s, kind, NULL, need) < need) return kind; } return -1; } int Stage_work(World *world, Object *s, float person_hours) { if (s->build_stage >= BUILD_DONE) return -1; if (Stage_missing(world, s, NULL, 0) > 0) return -2; s->build_work += person_hours; int done = -1; if (s->build_work >= NEED[s->build_stage]) { for (int kind = -1; kind < MATERIAL_KINDS; kind++) { /* (what lay by it went into it) */ Object *used[STAGE_LOGS > 4 ? STAGE_LOGS : 4]; int n = lyingBy(world, s, kind, used, needOf(s->build_stage, kind)); for (int k = 0; k < n; k++) Object_useUp(used[k]); } s->build_stage++; s->build_work = 0.0f; done = s->build_stage; } Stage_show(s); return done; } Vec3 Stage_workSpot(const Object *s, float side) { Vec3 f = yaw_front(s->rotation.y), r = vec3(-f.z, 0.0f, f.x); /* (its front: +Z of the mesh) */ float out = s->build_stage >= BUILD_ROOF ? STAGE_D * 0.5f - 0.6f : STAGE_D * 0.5f + 0.5f; /* (on the deck once there's a roof to it) */ Vec3 p = vec3_add(s->position, vec3_add(vec3_scale(f, out), vec3_scale(r, side * STAGE_W * 0.4f))); p.y = s->position.y; return p; } Vec3 Stage_handsAt(const Object *s, Vec3 spot, float t) { Vec3 to = vec3_sub(s->position, spot); to.y = 0.0f; float len = vec3_length(to); to = len > 1e-3f ? vec3_scale(to, 1.0f / len) : yaw_front(s->rotation.y); float h = s->build_stage == BUILD_MARKED ? 0.3f : s->build_stage == BUILD_FRAME ? 1.2f + 0.4f * sinf(t * 0.7f) : s->build_stage == BUILD_FLOOR ? STAGE_DECK : 1.4f; float knock = 0.06f * fabsf(sinf(t * 6.0f)); /* (hammering) */ return vec3_add(spot, vec3_add(vec3_scale(to, 0.55f), vec3(0.0f, h + knock, 0.0f))); } void Stage_show(Object *s) { int look = s->build_stage * LOOK_STEPS + (int)(Stage_part(s) * (float)LOOK_STEPS); if (s->shown == look + 1) return; Mesh_destroy(&s->mesh); s->mesh = Shapes_stage(s->build_stage, Stage_part(s), STAGE_W, STAGE_D, accent(s->variant)); s->shown = look + 1; int solid = s->build_stage > BUILD_FLOOR ? 1 : 0; /* (the deck can be stood on once it's in) */ if (s->collider_count != solid) { s->collider_count = solid; Object_syncColliders(s); } }