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 1.61.0: step ladder
- A step ladder lies in the tool room, in front of the other two - A click opens it and stands it in front of you; with a wall just ahead it stands with its back to it, and from its top you climb onto the wall - Climb it like the others (feet on the treads, hands on the top); E / Q folds it up again Includes 1.60.0 (if not committed separately): - A folding ladder (two 2.5 m halves on a hinge, 5 m open) beside the telescopic one; one click at a wall unfolds it and leans it there Includes 1.59.0 (if not committed separately): - Stepping up onto blocks above knee high; set moves and jumps only for things standing on the ground in front of you Includes 1.58.0 (if not committed separately): - Walking over knee-high things is one smooth set move that always finishes; at a run: jump up onto blocks, catch climbable edges
entropie/Makefile | 2 +- entropie/README.md | 4 + entropie/STATE.md | 7 +- entropie/commit_message.txt | 15 ++- entropie/src/game/game_actions.c | 7 +- entropie/src/render/shapes.h | 10 ++ entropie/src/render/shapes_props.c | 51 ++++++++++ entropie/src/world/ladder.c | 192 ++++++++++++++++++++++++++----------- entropie/src/world/ladder.h | 1 + entropie/src/world/object.c | 5 +- entropie/src/world/site.c | 1 + 11 files changed, 226 insertions(+), 69 deletions(-) diff --git a/entropie/Makefile b/entropie/Makefile index 2ed03c0..81a5ea7 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 1.60.0 +VERSION := 1.61.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/README.md b/entropie/README.md index 23241d4..298892f 100644 --- a/entropie/README.md +++ b/entropie/README.md @@ -320,6 +320,10 @@ A hand that can't reach its grip lets go and takes hold again. - Beside it lies a folding ladder (two 2.5 m halves on a hinge, 5 m open). A click at a wall unfolds it and leans it there; elsewhere a click unfolds or folds it +- In front of them lies a step ladder. A click stands it up in front of you + and opens it; with a wall just ahead it stands with its back to it, and + from its top you climb onto the wall if that's not much higher. E / Q + folds it up again - Ladder in hand: click at a wall and it slides out just as far as it takes (about 0.9 m over the edge) and leans there in one go: you stand it up, slide it out and tip it onto the wall (about a second); slid out by hand diff --git a/entropie/STATE.md b/entropie/STATE.md index 8945bf8..0737850 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -95,6 +95,8 @@ Ladder: one click at a wall slides it out to fit (top on the edge, 0.9 m over it Folding ladder (`variant` LADDER_FOLDING, 9.8 kg so it carries one-handed): two 2.5 m sections on a hinge (`shapes_props.c`, Shapes_foldLadder: the upper section swings round the hinge by `extend`). It is either folded (2.5 m) or open (5 m, 18 rungs); leaning at a wall it always opens fully, the putUp animation swings the upper half up. `ladder.c` lengthFor / rung / rungCount pick per type. +Step ladder (LADDER_STEP, 7 kg): a 1.95 m front leg with 5 treads plus a platform, the back leg hinged at the top (`Shapes_stepLadder`, `extend` = how far open, 30 degrees between the legs, each 15 off upright, so the treads are set level). Two colliders, one per leg (`ladder.c` fitColliders). standStep: stood up 0.45 m in front of you; a wall within 0.6 m past its back feet: back to the wall, 8 cm off; `lean_top` = its length when the wall top is between 0.6 under and 0.9 over its top, so the usual "over the edge" works. Feet only on the treads (top tread 1.39 m along), hands on the platform. Taking it folds it up again. NOTE: `mat4_rotate_x(a)` turns +Y towards -Z. + Raised ladder: slid out (extend goal > 0.5) and in hand, it's held 0.45 m up from its foot, the hand in front, the other hand higher up the rail, tipped up 60 deg in front (43-83 deg with the view pitch, eased 1.6 rad/s; `ladder.c` Ladder_placeHeld). A click leans it where you look (view ray, ladder length + 1.5 m; else a ray along the ladder, length + 0.8 m; a top face: the side towards you under that edge). Aimed within 0.45 m of the top it rests on the edge; lower, against the face at that height, flatter (no flatter than 55 deg: "aim at least X m up"); its foot at most 1.8 m from you. Looking down (view y < -0.6) a click slides it together; nothing aimed at: says so. Carrying long things (ladder, logs: 0.62 m and longer): they stop at people and the dog like at walls (unless you carry it together), push them out of the way (up to 25 cm a frame), knock light loose things aside; NPCs treat a long thing someone carries as in the way and walk round it (`world.c` pushOffCarried, `ai.c` ai_blockedBy). @@ -109,7 +111,7 @@ Piling styles: how neatly someone stacks (`ai_stackCare`: firewood skill and lik Woodpile: firewood is stacked into the lowest free place (gaps left by pieces taken off are filled first); it counts what really lies on it; a piece that fell off is loose again (gets restacked). -Bottles (beer, vodka, water), apples, bread, sausages (grill: done, dark, burnt), crates and boxes, logs, branches, firewood, stumps, woodpiles, fireplace, trash and garbage bags, the waste container, tables and garage furniture, stones, garden hose (a rope: falls, lies, drapes round things), watering can, telescopic and folding ladder, axe, shears, chainsaw, trees (fruit trees too), bushes, cars, buildings. +Bottles (beer, vodka, water), apples, bread, sausages (grill: done, dark, burnt), crates and boxes, logs, branches, firewood, stumps, woodpiles, fireplace, trash and garbage bags, the waste container, tables and garage furniture, stones, garden hose (a rope: falls, lies, drapes round things), watering can, telescopic, folding and step ladder, axe, shears, chainsaw, trees (fruit trees too), bushes, cars, buildings. ## World @@ -148,7 +150,6 @@ Most important first. - Clothing (trousers, skirt, dress, fly) – needed for the above **Things** -- More ladder types: a step ladder (stands on its own) - Hose near fire: melts, may burn; duct tape for small damage; burned through: shorter, or cut the bad part (knife, scissors) and join the ends; put the fire out with your drink (C) **Tools for us** @@ -159,4 +160,4 @@ Most important first. - The walking map rebuild takes ~9 ms when fixed things change (a felled tree) --- -State of `entropie 1.60.0 (build 6e3e5e9)` +State of `entropie 1.61.0 (build e870d0c)` diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt index bdbb1ab..5ca9a15 100644 --- a/entropie/commit_message.txt +++ b/entropie/commit_message.txt @@ -1,9 +1,14 @@ -entropie 1.60.0: folding ladder +entropie 1.61.0: step ladder -- A folding ladder (two 2.5 m halves on a hinge, 5 m open) lies beside - the telescopic one in the tool room -- One click at a wall unfolds it in the put-up animation and leans it - there; elsewhere a click unfolds or folds it +- A step ladder lies in the tool room, in front of the other two +- A click opens it and stands it in front of you; with a wall just ahead + it stands with its back to it, and from its top you climb onto the wall +- Climb it like the others (feet on the treads, hands on the top); + E / Q folds it up again + +Includes 1.60.0 (if not committed separately): +- A folding ladder (two 2.5 m halves on a hinge, 5 m open) beside the + telescopic one; one click at a wall unfolds it and leans it there Includes 1.59.0 (if not committed separately): - Stepping up onto blocks above knee high; set moves and jumps only for diff --git a/entropie/src/game/game_actions.c b/entropie/src/game/game_actions.c index e650ffa..96d75a6 100644 --- a/entropie/src/game/game_actions.c +++ b/entropie/src/game/game_actions.c @@ -358,7 +358,10 @@ static bool useLadder(Game *game) { if (was_in && lean != LEAN_DONE) { Ladder_setExtend(ladder, extend_was); ladder->extend_goal = 0.0f; } if (lean == LEAN_DONE) game->putting_up = ladder; const bool fold = ladder->variant == LADDER_FOLDING; - if (lean == LEAN_DONE) + if (lean == LEAN_DONE && ladder->variant == LADDER_STEP) + say(game, ladder->lean_top > 0.0f ? "You stand the step ladder there - climb it to get up there" + : "You stand the step ladder up - click it to climb"); + else if (lean == LEAN_DONE) say(game, ladder->lean_top > 0.0f ? (was_in ? (fold ? "You unfold the ladder and lean it there - click it to climb" : "You slide the ladder out and lean it there - click it to climb") : "The ladder leans there - click it to climb") @@ -390,7 +393,7 @@ static bool takeLadder(Game *game) { bool at_top = false; Object *ladder = Ladder_near(&game->world, p, &at_top); if (!ladder || at_top || Hands_freeHand(p) < 0) return false; - if (Ladder_take(p, ladder)) say(game, "You take the ladder down"); + if (Ladder_take(p, ladder)) say(game, ladder->variant == LADDER_STEP ? "You fold the step ladder up" : "You take the ladder down"); return true; } diff --git a/entropie/src/render/shapes.h b/entropie/src/render/shapes.h index d086276..b191c5a 100644 --- a/entropie/src/render/shapes.h +++ b/entropie/src/render/shapes.h @@ -141,6 +141,16 @@ Mesh Shapes_ladder(float extend); float Shapes_foldLadderLength(float unfold); float Shapes_foldLadderRung(int k); Mesh Shapes_foldLadder(float unfold); +/* The step ladder: a front leg with flat treads (+Y up it, +Z its front) and a + back leg hinged at the top. open 0 = folded flat .. 1 = standing (legs spread). */ +#define STEP_TREADS 5 /* the top platform comes on top of these */ +#define STEP_HINGE_Z -0.06f /* the back leg's hinge, behind the front leg */ +float Shapes_stepLadderLength(void); +/* Tread k along the front leg; k = STEP_TREADS: the top platform. */ +float Shapes_stepLadderRung(int k); +/* Angle between the legs (rad). */ +float Shapes_stepLadderSpread(float open); +Mesh Shapes_stepLadder(float open); /* A round bush, base at y = 0. */ Mesh Shapes_bush(float diameter, float height, unsigned seed); diff --git a/entropie/src/render/shapes_props.c b/entropie/src/render/shapes_props.c index 68edd06..9292cbb 100644 --- a/entropie/src/render/shapes_props.c +++ b/entropie/src/render/shapes_props.c @@ -918,6 +918,57 @@ Mesh Shapes_foldLadder(float unfold) { return Mesh_fromBuilder(&b); } +/* The step ladder: open, the legs stand 30 degrees apart, each 15 off upright; + the treads are set at that angle so they're level when it stands. */ +#define STEP_LEN 1.95f +#define STEP_FIRST 0.27f +#define STEP_PITCH 0.29f +#define STEP_SPREAD 0.5236f +#define STEP_SHUT 0.03f /* (folded: the back leg lies just behind the front one) */ + +float Shapes_stepLadderLength(void) { return STEP_LEN; } +float Shapes_stepLadderRung(int k) { + return k >= STEP_TREADS ? STEP_LEN - 0.03f : STEP_FIRST + STEP_PITCH * (float)k; +} +float Shapes_stepLadderSpread(float open) { return STEP_SHUT + (STEP_SPREAD - STEP_SHUT) * clampf(open, 0.0f, 1.0f); } + +Mesh Shapes_stepLadder(float open) { + const Vec3 alu = { 0.74f, 0.76f, 0.78f }, tread = { 0.55f, 0.57f, 0.60f }, plastic = { 0.85f, 0.32f, 0.12f }, + rubber = { 0.15f, 0.15f, 0.16f }; + const float half = LADDER_WIDTH * 0.5f, level = STEP_SPREAD * 0.5f; + MeshBuilder b; + MeshBuilder_init(&b); + /* the front leg: rails, rubber feet, treads (level when it stands) */ + for (int s = -1; s <= 1; s += 2) { + float x = (float)s * half; + addBlock(&b, vec3(x, STEP_LEN * 0.5f, 0.0f), vec3(0.03f, STEP_LEN, 0.07f), alu); + addBlock(&b, vec3(x, 0.02f, 0.0f), vec3(0.045f, 0.04f, 0.085f), rubber); + } + for (int k = 0; k < STEP_TREADS; k++) { + size_t first = b.vertex_count; + addBlock(&b, vec3_zero(), vec3(2.0f * half - 0.02f, 0.025f, 0.11f), tread); + transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.0f, Shapes_stepLadderRung(k), -0.015f)), mat4_rotate_x(-level))); + } + { /* the top: a plastic platform over the hinge */ + size_t first = b.vertex_count; + addBlock(&b, vec3(0.0f, 0.0f, -0.05f), vec3(2.0f * half + 0.06f, 0.05f, 0.2f), plastic); + transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.0f, STEP_LEN - 0.02f, 0.0f)), mat4_rotate_x(-level))); + } + /* the back leg: rails and two braces, swung out from the hinge */ + size_t first = b.vertex_count; + const float back = STEP_LEN - 0.05f; + for (int s = -1; s <= 1; s += 2) { + float x = (float)s * (half - 0.02f); + addBlock(&b, vec3(x, back * 0.5f, 0.0f), vec3(0.03f, back, 0.05f), alu); + addBlock(&b, vec3(x, back - 0.02f, 0.0f), vec3(0.045f, 0.04f, 0.07f), rubber); + } + for (int k = 1; k <= 2; k++) + addBlock(&b, vec3(0.0f, back * (float)k / 3.0f, 0.0f), vec3(2.0f * half - 0.06f, 0.025f, 0.025f), alu); + transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.0f, STEP_LEN - 0.04f, STEP_HINGE_Z)), + mat4_rotate_x(PI_F - Shapes_stepLadderSpread(open)))); + return Mesh_fromBuilder(&b); +} + Mesh Shapes_ladder(float extend) { const Vec3 alu = { 0.76f, 0.78f, 0.80f }, joint = { 0.20f, 0.20f, 0.22f }; const float half = LADDER_WIDTH * 0.5f, top = Shapes_ladderLength(extend); diff --git a/entropie/src/world/ladder.c b/entropie/src/world/ladder.c index 68148c6..6377464 100644 --- a/entropie/src/world/ladder.c +++ b/entropie/src/world/ladder.c @@ -26,34 +26,56 @@ #define AIM_FOOT 1.8f /* its foot no further than this from you (else: step closer) */ #define AIM_PAST 0.8f /* held up, it leans on what its top points at up to this much past it */ #define AIM_EDGE 0.45f /* ... aimed this close under the top of it: the top rests on the edge */ +#define STEP_FOLD_SPEED 2.5f /* step ladder: folds up this fast (of the way per second) */ +#define STEP_IN_FRONT 0.45f /* ... stood up: its front feet this far in front of you */ +#define STEP_AT_WALL 0.6f /* ... a wall this close past its back feet: it stands with its back to it */ +#define STEP_WALL_GAP 0.08f /* ... its back feet this far from that wall */ +#define STEP_OFF_UP 0.9f /* ... from its top you get onto that if it's no higher than this over the top */ static const Vec3 UP = { 0.0f, 1.0f, 0.0f }; static Vec3 flatv(Vec3 v) { return vec3(v.x, 0.0f, v.z); } static float flatDist(Vec3 a, Vec3 b) { return vec3_length(flatv(vec3_sub(a, b))); } -/* Two kinds: telescopic (variant 0: slides out) and folding (variant 1: two sections on a hinge, unfolds). */ +/* Three kinds: telescopic (variant 0: slides out), folding (two sections on a hinge, unfolds) and the + step ladder (stands on its own: extend is how far its legs are open). */ static bool folding(const Object *l) { return l->variant == LADDER_FOLDING; } -static float lengthFor(const Object *l, float extend) { return folding(l) ? Shapes_foldLadderLength(extend) : Shapes_ladderLength(extend); } +static bool stepLadder(const Object *l) { return l->variant == LADDER_STEP; } +static float lengthFor(const Object *l, float extend) { + return stepLadder(l) ? Shapes_stepLadderLength() : folding(l) ? Shapes_foldLadderLength(extend) : Shapes_ladderLength(extend); +} static float lengthOf(const Object *l) { return lengthFor(l, l->extend); } static Vec3 upAlong(const Object *l) { return l->colliders[0].axes[1]; } static Vec3 pointAt(const Object *l, float along) { return vec3_add(l->position, vec3_scale(upAlong(l), along)); } +/* Colliders for that length (the step ladder: one per leg). */ +static void fitColliders(Object *l, float extend) { + float len = lengthFor(l, extend); + l->colliders[0].center = vec3(0.0f, len * 0.5f, stepLadder(l) ? -0.015f : 0.0f); + l->colliders[0].half_extents = vec3(LADDER_WIDTH * 0.5f + 0.03f, len * 0.5f, stepLadder(l) ? 0.06f : 0.04f); + if (stepLadder(l)) { + float a = Shapes_stepLadderSpread(extend); + Vec3 hinge = vec3(0.0f, len - 0.04f, STEP_HINGE_Z), down = vec3(0.0f, -cosf(a), -sinf(a)); + l->colliders[1] = (Collider){ .center = vec3_add(hinge, vec3_scale(down, len * 0.5f)), + .half_extents = vec3(LADDER_WIDTH * 0.5f + 0.01f, len * 0.5f, 0.03f), + .tilt = vec3(-a, 0.0f, 0.0f) }; + l->collider_count = 2; + } + Object_syncColliders(l); +} + /* New mesh and collider for the current length. */ static void reshape(Object *l) { - Mesh mesh = folding(l) ? Shapes_foldLadder(l->extend) : Shapes_ladder(l->extend); + Mesh mesh = stepLadder(l) ? Shapes_stepLadder(l->extend) : folding(l) ? Shapes_foldLadder(l->extend) : Shapes_ladder(l->extend); if (mesh.vao) { Mesh_destroy(&l->mesh); l->mesh = mesh; } - float len = lengthOf(l); - l->colliders[0].center = vec3(0.0f, len * 0.5f, 0.0f); - l->colliders[0].half_extents = vec3(LADDER_WIDTH * 0.5f + 0.03f, len * 0.5f, 0.04f); - Object_syncColliders(l); + fitColliders(l, l->extend); } void Ladder_slide(Object *l) { - if (l->leaning || l->placing > 0.0f) return; + if (l->leaning || l->placing > 0.0f || stepLadder(l)) return; l->extend_goal = l->extend_goal > 0.5f ? 0.0f : 1.0f; } @@ -92,7 +114,7 @@ void Ladder_update(Object *l, float dt) { if (l->placing > 0.0f) { putUp(l, dt); return; } if (l->raised > 0.0f) l->raised += clampf(l->raise_goal - l->raised, -RAISE_SPEED * dt, RAISE_SPEED * dt); if (l->extend == l->extend_goal) return; - float step = SLIDE_SPEED * dt; + float step = (stepLadder(l) ? STEP_FOLD_SPEED : SLIDE_SPEED) * dt; l->extend = l->extend < l->extend_goal ? fminf(l->extend + step, l->extend_goal) : fmaxf(l->extend - step, l->extend_goal); reshape(l); } @@ -211,9 +233,99 @@ static Object *aimedAt(const World *world, const Object *who, const Object *l, V return thing; } +/* Lets go of it and puts it up there (stood up, slid out or opened, tipped: see putUp). + n: out towards where you stand at it. */ +static void startPutUp(Object *who, Object *l, Vec3 foot, Vec3 up, Vec3 z, Vec3 n, float extend_to, float top) { + Vec3 from_foot = l->position, from_axis = upAlong(l), from_face = l->colliders[0].axes[2]; + for (int h2 = 0; h2 < 2; h2++) + if (who->hands.item[h2] == l) Hands_drop(who, h2); + l->velocity = l->angular_velocity = vec3_zero(); + l->is_static = true; + l->leaning = false; + l->lean_normal = n; + l->placing = 1e-4f; + l->place_foot[0] = from_foot; + l->place_foot[1] = foot; + l->place_axis[0] = from_axis; + l->place_axis[1] = up; + l->place_face[0] = from_face; + l->place_face[1] = z; + l->place_extend[0] = l->extend; + l->place_extend[1] = extend_to; + l->place_top = top; + l->lean_top = l->place_top; /* (known now: the game says whether it reaches) */ + /* stepping back out of its way, to stand at its foot */ + float behind = vec3_dot(flatv(vec3_sub(who->position, foot)), n); + if (behind < 0.5f) { + who->position = vec3_add(who->position, vec3_scale(n, 0.5f - behind)); + who->body.replant = true; + Object_syncColliders(who); + } +} + +/* Would it stand there (opened, at extend_to) without going into anything? */ +static bool roomFor(const World *world, const Object *who, const Object *l, const Object *thing, Vec3 foot, Vec3 up, Vec3 z, + float extend_to) { + Object stood = *l; + stood.extend = extend_to; + stood.rotation = euler_yxz_from_axes(up, z); + stood.position = foot; + stood.is_held = false; + fitColliders(&stood, extend_to); + for (int i = 0; i < world->count; i++) { + const Object *o = world->objects[i]; + if (o == l || o == thing || o == who || o->is_held || o->collider_count == 0 || o->type == OBJ_DECAL || + o->type == OBJ_PUDDLE || o->type == OBJ_STINK || flatDist(o->position, foot) > 30.0f) continue; + if (Physics_penetration(&stood, o) > LEAN_ROOM) return false; + } + return true; +} + +/* The step ladder: opened and stood in front of you - or, a wall just past + it, with its back to the wall (from its top you get up there if it's not too high). */ +static int standStep(World *world, Object *who, Object *l, const char **why) { + const float len = Shapes_stepLadderLength(), lean = 0.5f * Shapes_stepLadderSpread(1.0f); + const float out = len * sinf(lean), height = len * cosf(lean); /* (each leg's feet this far out from under the top) */ + Vec3 f = vec3_normalize(flatv(Object_facing(who))); + Vec3 foot = vec3_add(who->position, vec3_scale(f, STEP_IN_FRONT)); + float top = 0.0f; + Vec3 face = vec3_zero(), n = vec3_zero(); + Object *thing = faceAhead(world, who, l, &face, &n); + if (thing && fabsf(n.y) < 0.35f) { + n = vec3_normalize(flatv(n)); + float d = vec3_dot(flatv(vec3_sub(face, who->position)), f); + if (d < STEP_IN_FRONT + 2.0f * out + STEP_AT_WALL) { /* at it: its back to it */ + f = vec3_scale(n, -1.0f); + foot = vec3_add(face, vec3_scale(n, STEP_WALL_GAP + 2.0f * out)); + Vec3 inside = vec3_sub(face, vec3_scale(n, 0.06f)); + float ground = World_groundHeight(world, foot.x, foot.z, who->position.y + 0.8f); + float h = topOf(thing, inside.x, inside.z, face.y + 40.0f) - ground; + if (h > height - 0.6f && h < height + STEP_OFF_UP) top = len; + } else thing = NULL; + } else thing = NULL; + foot.y = World_groundHeight(world, foot.x, foot.z, who->position.y + 0.8f); + if (fabsf(foot.y - who->position.y) > 0.5f) { *why = "No level ground there for the step ladder"; return LEAN_NO; } + Vec3 across = vec3_cross(f, UP); + for (int leg = 0; leg < 2; leg++) { + Vec3 c = vec3_add(foot, vec3_scale(f, 2.0f * out * (float)leg)); + for (int s = -1; s <= 1; s += 2) { + Vec3 p = vec3_add(c, vec3_scale(across, (float)s * LADDER_WIDTH * 0.5f)); + float g = World_groundHeight(world, p.x, p.z, foot.y + 1.0f); + if (g > foot.y + 0.12f || g < foot.y - 0.15f) { *why = "Not level enough there for the step ladder"; return LEAN_NO; } + } + } + Vec3 back = vec3_scale(f, -1.0f); /* (you climb it from this side) */ + Vec3 up = vec3_normalize(vec3_add(vec3_scale(UP, cosf(lean)), vec3_scale(f, sinf(lean)))); + Vec3 z = vec3_normalize(vec3_sub(back, vec3_scale(up, vec3_dot(back, up)))); + if (!roomFor(world, who, l, NULL, foot, up, z, 1.0f)) { *why = "No room for the step ladder there"; return LEAN_NO; } + startPutUp(who, l, foot, up, z, back, 1.0f, top); + return LEAN_DONE; +} + int Ladder_lean(World *world, Object *who, Object *l, bool fit, const char **why) { Vec3 face = vec3_zero(), n = vec3_zero(); float aim_y = -1e9f; /* (held up: where its top should go) */ + if (stepLadder(l)) return standStep(world, who, l, why); const bool raised = Ladder_isRaised(l); /* (held up and looking down at the ground: slide it together) */ if (raised && who->type == OBJ_PLAYER && who->body.look_dir.y < LOOK_DOWN) return LEAN_NOTHING; @@ -278,49 +390,9 @@ int Ladder_lean(World *world, Object *who, Object *l, bool fit, const char **why Vec3 up = vec3_normalize(vec3_sub(rest, foot)); Vec3 z = vec3_normalize(vec3_sub(n, vec3_scale(up, vec3_dot(n, up)))); /* the ladder's face, out from the wall */ /* Room for all of it there (not through a wall beside it, or anything else) */ - { - Object stood = *l; - stood.extend = extend_to; - stood.colliders[0].center = vec3(0.0f, len * 0.5f, 0.0f); - stood.colliders[0].half_extents = vec3(LADDER_WIDTH * 0.5f + 0.03f, len * 0.5f, 0.04f); - stood.rotation = euler_yxz_from_axes(up, z); - stood.position = foot; - stood.is_held = false; - Object_syncColliders(&stood); - for (int i = 0; i < world->count; i++) { - const Object *o = world->objects[i]; - if (o == l || o == thing || o == who || o->is_held || o->collider_count == 0 || o->type == OBJ_DECAL || - o->type == OBJ_PUDDLE || o->type == OBJ_STINK || flatDist(o->position, foot) > 30.0f) continue; - if (Physics_penetration(&stood, o) > LEAN_ROOM) { *why = "No room for it there - something's in the way"; return LEAN_NO; } - } - } + if (!roomFor(world, who, l, thing, foot, up, z, extend_to)) { *why = "No room for it there - something's in the way"; return LEAN_NO; } - /* Let go of it and put it up there (stood up, slid out, tipped onto it: see place) */ - Vec3 from_foot = l->position, from_axis = upAlong(l), from_face = l->colliders[0].axes[2]; - for (int h2 = 0; h2 < 2; h2++) - if (who->hands.item[h2] == l) Hands_drop(who, h2); - l->velocity = l->angular_velocity = vec3_zero(); - l->is_static = true; - l->leaning = false; - l->lean_normal = n; - l->placing = 1e-4f; - l->place_foot[0] = from_foot; - l->place_foot[1] = foot; - l->place_axis[0] = from_axis; - l->place_axis[1] = up; - l->place_face[0] = from_face; - l->place_face[1] = z; - l->place_extend[0] = l->extend; - l->place_extend[1] = extend_to; - l->place_top = reaches ? vec3_length(vec3_sub(rest, foot)) : 0.0f; - l->lean_top = l->place_top; /* (known now: the game says whether it reaches) */ - /* stepping back out of its way, to stand at its foot */ - float behind = vec3_dot(flatv(vec3_sub(who->position, foot)), n); - if (behind < 0.5f) { - who->position = vec3_add(who->position, vec3_scale(n, 0.5f - behind)); - who->body.replant = true; - Object_syncColliders(who); - } + startPutUp(who, l, foot, up, z, n, extend_to, reaches ? vec3_length(vec3_sub(rest, foot)) : 0.0f); return LEAN_DONE; } @@ -343,7 +415,7 @@ Object *Ladder_near(const World *world, const Object *who, bool *at_top) { /* up there, at its top */ if (l->lean_top > 0.0f) { Vec3 edge = Ladder_edge(l); - if (flatDist(edge, who->position) < MOUNT_REACH && fabsf(edge.y - who->position.y) < 0.8f) { + if (flatDist(edge, who->position) < MOUNT_REACH && fabsf(edge.y - who->position.y) < (stepLadder(l) ? STEP_OFF_UP + 0.1f : 0.8f)) { *at_top = true; return l; } @@ -359,11 +431,13 @@ bool Ladder_take(Object *who, Object *l) { l->is_static = false; l->lean_top = 0.0f; l->rest_time = 0.0f; + if (stepLadder(l)) l->extend_goal = 0.0f; /* (folds up as you take it) */ return Hands_pickUpObject(who, hand, l); } /* Highest the feet go: the hands still find rungs above them. */ static float highest(const Object *l) { + if (stepLadder(l)) return Shapes_stepLadderRung(STEP_TREADS - 1) - 0.02f; /* (on the top tread, not the platform) */ float top = l->lean_top > 0.0f ? l->lean_top : lengthOf(l); return fmaxf(0.0f, top - HANDS_ABOVE - 0.15f); } @@ -414,8 +488,12 @@ int Ladder_climb(Object *who, float input, float dt) { } /* Rung k's height along the ladder. */ -static float rung(const Object *l, int k) { return folding(l) ? Shapes_foldLadderRung(k) : Shapes_ladderRung(l->extend, k); } -static int rungCount(const Object *l) { return folding(l) ? (l->extend > 0.99f ? 2 * FOLD_RUNGS : FOLD_RUNGS) : LADDER_RUNGS; } +static float rung(const Object *l, int k) { + return stepLadder(l) ? Shapes_stepLadderRung(k) : folding(l) ? Shapes_foldLadderRung(k) : Shapes_ladderRung(l->extend, k); +} +static int rungCount(const Object *l) { + return stepLadder(l) ? STEP_TREADS + 1 : folding(l) ? (l->extend > 0.99f ? 2 * FOLD_RUNGS : FOLD_RUNGS) : LADDER_RUNGS; +} /* The rung at or just above `along`. */ static int rungAbove(const Object *l, float along) { @@ -476,8 +554,8 @@ void Ladder_poseFeet(Object *who, float dt) { int k = rungAbove(l, who->ladder_at - 0.02f); for (int i = 0; i < 2; i++) { /* one foot a rung higher than the other, swapping as you climb */ - int kf = k + ((k + i) % 2 == 0 ? 0 : 1); - if (kf >= rungCount(l)) kf = rungCount(l) - 1; + int kf = k + ((k + i) % 2 == 0 ? 0 : 1), last = rungCount(l) - (stepLadder(l) ? 2 : 1); /* (no feet on the platform) */ + if (kf > last) kf = last; float side = i == 0 ? 1.0f : -1.0f; Foot *foot = &who->body.feet[i]; Vec3 goal = vec3_add(pointAt(l, rung(l, kf)), vec3_add(vec3_scale(across, side * 0.11f), @@ -494,7 +572,7 @@ void Ladder_poseFeet(Object *who, float dt) { } bool Ladder_isRaised(const Object *l) { - return l->type == OBJ_LADDER && l->is_held && l->extend_goal > 0.5f && l->placing <= 0.0f; + return l->type == OBJ_LADDER && !stepLadder(l) && l->is_held && l->extend_goal > 0.5f && l->placing <= 0.0f; } /* How steep a raised ladder is held: up with the view (more upright looking up). */ diff --git a/entropie/src/world/ladder.h b/entropie/src/world/ladder.h index 3670244..d0d429e 100644 --- a/entropie/src/world/ladder.h +++ b/entropie/src/world/ladder.h @@ -22,6 +22,7 @@ void Ladder_setExtend(Object *ladder, float extend); void Ladder_update(Object *ladder, float dt); #define LADDER_FOLDING 1 /* variant: two sections on a hinge (unfolds); 0: telescopic (slides out) */ +#define LADDER_STEP 2 /* variant: a step ladder (opens and stands on its own) */ enum { LEAN_NOTHING, LEAN_DONE, LEAN_NO }; /* Leans the ladder who carries against what's right in front. LEAN_NO sets why. diff --git a/entropie/src/world/object.c b/entropie/src/world/object.c index eb85ccd..3bddc40 100644 --- a/entropie/src/world/object.c +++ b/entropie/src/world/object.c @@ -954,7 +954,10 @@ void Object_describe(const Object *obj, char *out, size_t size) { } case OBJ_HOSE: snprintf(out, size, "Garden hose - left click (hold) to spray: washes away puddles and dirt"); break; case OBJ_LADDER: - if (obj->variant == 1) /* (folding) */ + if (obj->variant == 2) /* (step ladder) */ + snprintf(out, size, obj->leaning ? "Step ladder - left click: climb (W / S), E / Q: fold it up" + : "Step ladder - left click: stand it up in front of you (at a wall: with its back to it)"); + else if (obj->variant == 1) /* (folding) */ snprintf(out, size, obj->leaning ? "Folding ladder - left click: climb (W / S), E / Q: take it down" : obj->extend_goal > 0.5f ? "Folding ladder (unfolded) - left click where it should lean, look down: fold it" : "Folding ladder (folded) - left click at a wall: unfold it and lean it there"); diff --git a/entropie/src/world/site.c b/entropie/src/world/site.c index 8ee92df..31d658e 100644 --- a/entropie/src/world/site.c +++ b/entropie/src/world/site.c @@ -730,6 +730,7 @@ static bool furnishGarages(World *world, Vec3 origin, float yaw, float zsign) { { OBJ_CHAINSAW, { 25.6f, 0.3f, 12.0f }, { 0.0f, 0.0f, 0.0f }, { 0, 60, 0 }, 6.0f, 0 }, { OBJ_LADDER, { 20.9f, 0.1f, 13.55f }, { 0.0f, 0.0f, 0.0f }, { 90, 90, 0 }, 9.5f, 0 }, /* slid together, along the end wall */ { OBJ_LADDER, { 23.2f, 0.1f, 13.55f }, { 0.0f, 0.0f, 0.0f }, { 90, 90, 0 }, 9.8f, LADDER_FOLDING }, /* folded, beside it */ + { OBJ_LADDER, { 21.0f, 0.1f, 12.6f }, { 0.0f, 0.0f, 0.0f }, { 90, 90, 0 }, 7.0f, LADDER_STEP }, /* folded, in front of them */ }; for (unsigned i = 0; i < sizeof(TOOLS) / sizeof(TOOLS[0]); i++) { Vec3 pos = placed(TOOLS[i].at, WALLED_CENTER, origin, yaw, zsign);