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.52.0: no stopping half over things, ladder held up and aimed
- Stopping half way over something: the body carries on over it (the foot over there stays, the other comes after); walking back takes you back - Holding still half over costs energy (a leg held up more); worn out, too long or legs too far apart you're made to go over, else you fall - A slid-out ladder is held near its foot, reaching up in front (steeper looking up); a click leans it where you look: on the edge when aimed at the top, against the face lower down; looking down slides it together
entropie/Makefile | 2 +- entropie/README.md | 7 +++ entropie/STATE.md | 5 +- entropie/commit_message.txt | 17 +++--- entropie/src/people/body.c | 42 ++++++++++++++ entropie/src/people/body.h | 8 +++ entropie/src/people/hands.c | 8 +++ entropie/src/world/ladder.c | 128 +++++++++++++++++++++++++++++++++++++++++-- entropie/src/world/ladder.h | 5 +- entropie/src/world/object.h | 1 + entropie/src/world/physics.c | 15 +++++ entropie/src/world/world.c | 28 ++++++++++ 12 files changed, 249 insertions(+), 17 deletions(-) diff --git a/entropie/Makefile b/entropie/Makefile index 0e64c91..d1ed684 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 1.51.0 +VERSION := 1.52.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/README.md b/entropie/README.md index 645ba95..a1f5f35 100644 --- a/entropie/README.md +++ b/entropie/README.md @@ -148,6 +148,9 @@ and satellite pictures (about 340 x 340 m). - Running into something sober: low, you trip or go headlong over it; high, you stagger back or go down to the side you came in from. How badly depends on speed, fitness, energy and clumsiness +- Stop half way over something and the body carries on over it; you never + hang in a half-over pose. Holding still half over costs energy; worn out + (or legs too far apart) you go over, and if that's not possible you fall - Stepping over is a risk: you may stumble or fall, more so sprinting, over high things, tired, drunk or clumsy. Rushing or clumsy, a leg that can't keep clear scrapes ("Ouch!"), and at a run it may trip you @@ -309,6 +312,10 @@ A hand that can't reach its grip lets go and takes hold again. slide it out and tip it onto the wall (about a second); slid out by hand first, it keeps its length. Elsewhere a click slides it out or together. Too close to something beside it, it says so instead of going through it +- Slid out, you hold it near its foot and it reaches up into the air in front + of you (steeper when you look up). A click leans it where you look: at the + top of a wall it rests on the edge, lower down it leans against the face + there. Look down and click to slide it together - It leans on anything that holds it: walls, the car, a tree (not a bush), with level ground and room for its feet. The top rests on the edge (the gutter of a roof); too short for that, it leans against the face diff --git a/entropie/STATE.md b/entropie/STATE.md index 49e1d72..304df12 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -75,6 +75,7 @@ Talk topics: offer, ask, suggest, where (new woodpile spot), ask for a hand, ask - **Approaching obstacles**: the sporty fit their steps to a good take-off spot; sprinting fit and fresh, they leap low narrow things; running into something: trip or headlong over it if low, stagger back or fall to the side if high (speed, fitness, energy, clumsiness) - **Over obstacles**: lean into it, the leg swings out a little and back in; lift the foot first, then over; the body waits for the back leg; knee turns out, or the foot comes up; sprinting, leaps; careless people just step on small things; trips and scrapes by speed, height, condition - **Bushes**: trying to get over one ends in it, twigs snap +- **Half way over something**: from when a foot is well into a step over something (lifted 12 cm+, 15 % into the step) until the body is over there (6 cm behind where that foot came down, at most 20 cm past it), you're crossing (`body.c` crossOver): stopping (or slower than 0.3 m/s) the body carries on over it at 0.6 m/s (`physics.c` carryOver); the foot over there stays down, the other comes after. Walking back takes you back. Standing still half over costs energy (2.5 %/s, a leg held up 6 %/s; less sporty, more); worn out, held longer than 1-6 s (fitness, energy) or legs over 0.95 m apart: you're made to go over; still stuck 1.5 s later: you fall (`world.c` halfOver) - **Sure-footedness**: one factor (`world.c`, unsure: ~0.2 sporty and careful .. ~1 average .. ~2.7 clumsy and unfit) scales every trip, slip, scrape and run-into, and how bad it gets; clumsy and unfit, you also stumble over your own feet jogging or running - **Climbing**: hand on it (up to 1.35 m), hang and pull up (higher, both hands free), jump for the edge; ladder up and down, over the edge at the top. Walking up to something 0.7–2 m high, the hands go onto its edge first; pulling up, the feet push against the face below the edge, then the right knee and foot come over onto it, then the left (scripted in `game_actions.c`, climbFeet); knees bend up. High pull-ups: pulled up till the arms press down (hips near the edge), then the knee comes up and you rise; the hands go on ahead onto the top and stay there, the back bends over the edge as far as it takes to keep them on it (body_pose.c, reach_bend). Jumping: holding Space (or W) grabs an edge the hands reach (about 2.4 m); up to 0.35 m higher, the fingertips touch it ("Just out of reach"); hands go up wide, then onto the edge - **Drink and the rest**: sips, burps, sways, veers, sick, passes out, sleeps it off; pees (stream lands on the real ground, soaks in or puddles) @@ -85,6 +86,8 @@ Talk topics: offer, ask, suggest, where (new woodpile spot), ask for a hand, ask Ladder: one click at a wall slides it out to fit (top on the edge, 0.9 m over it; up to its 6 m) and leans it; slid out by hand, it keeps that length. Putting it up is a 1.1 s animation (`ladder.c`, putUp): stood upright, slid out, tipped onto the wall; you stand still with both hands on it (`game.c`, puttingUp); it can only be climbed or taken once it leans. +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). Clicking with a tool in hand: if there's something it could work on within its reach + 0.6 m and not behind you, the click is for the tool (or "Step up to it"), never a pick-up into the free hand (Tools_jobNearby). @@ -149,4 +152,4 @@ Most important first. - The walking map rebuild takes ~9 ms when fixed things change (a felled tree) --- -State of `entropie 1.51.0 (build f2fae86)` +State of `entropie 1.52.0 (build 618b0b4)` diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt index aa92eaf..bcad6bd 100644 --- a/entropie/commit_message.txt +++ b/entropie/commit_message.txt @@ -1,10 +1,9 @@ -entropie 1.51.0: lift one end of a heavy log +entropie 1.52.0: no stopping half over things, ladder held up and aimed -- A log too heavy to carry alone (but not for half its weight): held near - one end, that end comes up to the hip and the other drags on the ground -- The hands slide to that end; dragging is slower; a helper joining - carries all of it; held in the middle it says to take it at one end - -Includes 1.50.0 (if not committed separately): -- Piling styles: careful people stack neat, stable piles; careless ones - put pieces askew, and a high loose pile comes down now and then +- Stopping half way over something: the body carries on over it (the foot + over there stays, the other comes after); walking back takes you back +- Holding still half over costs energy (a leg held up more); worn out, too + long or legs too far apart you're made to go over, else you fall +- A slid-out ladder is held near its foot, reaching up in front (steeper + looking up); a click leans it where you look: on the edge when aimed at + the top, against the face lower down; looking down slides it together diff --git a/entropie/src/people/body.c b/entropie/src/people/body.c index f8f7a43..a6509b2 100644 --- a/entropie/src/people/body.c +++ b/entropie/src/people/body.c @@ -9,6 +9,11 @@ static const float FIDGET_LENGTH[FIDGET_COUNT] = { 0.0f, 1.6f, 2.4f, 2.6f, 2.0f, static Vec3 clearSpot(const BodyFrame *f, Foot *foot, Vec3 spot, Vec3 other, float reach, bool sloppy, float s); static Vec3 leapPast(const BodyFrame *f, Foot *foot, Vec3 aim, Vec3 other, float reach, float s); +static void crossOver(Body *b, const BodyFrame *f, float s); +#define CROSS_OVER 0.12f /* a foot lifted this much over something ... */ +#define CROSS_FROM 0.15f /* ... and this far into its step: half way over it */ +#define CROSS_PAST 0.2f /* the body goes no further than this past where that foot came down */ +#define CROSS_DONE 0.06f /* over: the body this close behind where that foot came down (or past it) */ static float random01(Body *b) { if (b->rng == 0) b->rng = 0x9E3779B9u ^ (unsigned)(b->clock * 7919.0f); @@ -1012,6 +1017,8 @@ void Body_update(Body *b, const BodyFrame *f) { } } + crossOver(b, f, s); + /* Start a new step with the foot that drifted furthest from its spot (or that the body turned away from). Walking: one foot at a time. Running: the next foot may leave before the other lands. */ @@ -1028,6 +1035,9 @@ void Body_update(Body *b, const BodyFrame *f) { bool leap = speed > LEAP_SPEED && ((other->over > 0.0f && behind > LEAP_BEHIND * s) || foot->into > LEG_TOUCH * s); if (other->stepping && other->step < 1.0f - overlap && !leap) continue; if (any_stepping && !other->stepping) continue; + /* half over something: the foot already over there stays; the other one comes after it */ + if (b->crossing && !other->stepping && vec3_dot(flat(vec3_sub(foot->ankle, b->cross_to)), b->cross_dir) > -0.12f * s && + vec3_dot(flat(vec3_sub(other->ankle, b->cross_to)), b->cross_dir) < -0.25f * s) continue; Vec3 spot = ankleAt(uncrossed(footSpot(b, f, i, s), f, i, other->ankle, s), s); float drift = vec3_length(flat(vec3_sub(spot, foot->ankle))); @@ -1094,6 +1104,38 @@ void Body_update(Body *b, const BodyFrame *f) { } } +/* Half way over something: from when a foot is well on its way over it until + the body is over there too (above where that foot came down). */ +static void crossOver(Body *b, const BodyFrame *f, float s) { + for (int i = 0; i < 2 && !b->crossing; i++) { /* (where the first foot goes over: the body goes that far, no further) */ + const Foot *ft = &b->feet[i]; + if (!ft->stepping || ft->over < CROSS_OVER * s || ft->step < CROSS_FROM) continue; + Vec3 way = flat(vec3_sub(ft->step_to, ft->step_from)); + if (vec3_length(way) < 0.08f * s) continue; /* (lifted on the spot: not going over anything) */ + b->crossing = true; + b->cross_foot = i; + b->cross_to = ft->step_to; + b->cross_dir = vec3_normalize(way); + b->cross_left = vec3_dot(flat(vec3_sub(b->cross_to, f->position)), b->cross_dir); + return; + } + if (!b->crossing) return; + /* (a foot set a little further on, past it: the body goes after that one, no further) */ + float to = 0.0f; + for (int i = 0; i < 2; i++) { + const Foot *ft = &b->feet[i]; + if (!ft->stepping) to = fmaxf(to, fminf(vec3_dot(flat(vec3_sub(ft->ankle, b->cross_to)), b->cross_dir), CROSS_PAST * s)); + } + float left = vec3_dot(flat(vec3_sub(b->cross_to, f->position)), b->cross_dir) + to; + b->cross_left = left; + float back = vec3_dot(flat(vec3_sub(b->feet[b->cross_foot].ankle, b->cross_to)), b->cross_dir); + bool returned = back < -0.25f * s; /* (that foot came back: not going over after all) */ + if (left < CROSS_DONE * s || returned || b->pose != POSE_UP || b->airborne) { + b->crossing = b->cross_force = false; + b->cross_held = 0.0f; + } +} + /* Where a foot lands: not on something lying there. A longer (or shorter) step past it, and the foot lifted over whatever is in the way. Too high to stand on and nowhere free: the foot stays where it is. */ diff --git a/entropie/src/people/body.h b/entropie/src/people/body.h index 6394059..18dff96 100644 --- a/entropie/src/people/body.h +++ b/entropie/src/people/body.h @@ -139,6 +139,14 @@ typedef struct { bool limb_pole_ok[4]; float clock; float stumble; /* > 0 while recovering from a misstep (seconds left) */ + /* Half way over something (a foot stepping over it, or landed past it with + the body still behind): the body carries on over it (see Physics) */ + bool crossing; + bool cross_force; /* ... and now it must (worn out, too long, legs too wide apart) */ + int cross_foot; /* the foot that went over */ + Vec3 cross_to, cross_dir; /* where it lands, and the way over */ + float cross_left; /* how much further the body has to go */ + float cross_held; /* seconds standing still half over (costs energy) */ float flail, flail_v; /* arms out for balance (sprung) */ float idle_s, idle_v; /* standing about (0..1, sprung): hands into their habits */ Vec3 root_vis, root_vel;/* where the hips are drawn over (world, horizontal): sprung, no jitter */ diff --git a/entropie/src/people/hands.c b/entropie/src/people/hands.c index 2d3fe7c..244d9f3 100644 --- a/entropie/src/people/hands.c +++ b/entropie/src/people/hands.c @@ -885,6 +885,14 @@ void Hands_update(Object *who, float dt) { } } } + if (Ladder_isRaised(item)) { /* a slid-out ladder held up: the hand in front, the other higher up the rail */ + local = vec3(sign * 0.1f * s, 0.95f * s, 0.32f * s); + if (h->state[1 - i] == HAND_FREE && item->raised > 0.0f) { + Vec3 rail = vec3_scale(right, -sign * LADDER_WIDTH * 0.5f); + b->hand_active[1 - i] = true; + b->hand_target[1 - i] = vec3_add(Ladder_raisedGrip(item, 1.0f), vec3_add(rail, vec3_scale(right, -sign * 0.02f))); + } + } if (item->type == OBJ_CAN) { /* watering: the arm comes up and forward */ Vec3 lifted = vec3(sign * 0.13f * s, 1.02f * s, 0.40f * s); local = vec3_lerp(local, lifted, smoothstepf(0.0f, 1.0f, who->pour)); diff --git a/entropie/src/world/ladder.c b/entropie/src/world/ladder.c index 5819652..95eea47 100644 --- a/entropie/src/world/ladder.c +++ b/entropie/src/world/ladder.c @@ -1,4 +1,5 @@ #include "world/ladder.h" +#include <stdio.h> #include "world/physics.h" #define SLIDE_SPEED 0.7f /* of the way out per second */ @@ -13,6 +14,18 @@ #define CLIMB_SPEED 0.8f /* meters along the ladder per second */ #define HANDS_ABOVE 1.05f /* hands this far above the feet (along the ladder) */ #define MOUNT_REACH 1.2f +#define RAISE_ANGLE 1.05f /* a raised ladder: this steep (rad) looking straight ahead ... */ +#define RAISE_FOLLOW 0.9f /* ... steeper looking up, flatter looking down ... */ +#define RAISE_LOW 0.75f +#define RAISE_HIGH 1.45f +#define RAISE_SPEED 1.6f /* rad/s it tips up or down */ +#define RAISE_GRIP 0.45f /* held this far up from its foot */ +#define FLATTEST 55.0f /* degrees: no flatter than this (it would slide out) */ +#define AIM_VIEW 1.5f /* looking at it: this much further than the ladder is long */ +#define LOOK_DOWN -0.6f /* looking down this steeply (view y), a click slides a held-up ladder together */ +#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 */ static const Vec3 UP = { 0.0f, 1.0f, 0.0f }; @@ -74,6 +87,7 @@ static void putUp(Object *l, float dt) { 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; l->extend = l->extend < l->extend_goal ? fminf(l->extend + step, l->extend_goal) : fmaxf(l->extend - step, l->extend_goal); @@ -143,9 +157,66 @@ static Object *faceAhead(const World *world, const Object *who, const Object *la return hit; } +/* The first solid thing along a ray (not the ladder, nor who carries it, nor people). */ +static Object *rayHit(const World *world, const Object *who, const Object *ladder, Vec3 origin, Vec3 dir, float max, + Vec3 *point, Vec3 *normal) { + Object *hit = NULL; + float best = max; + for (int i = 0; i < world->count; i++) { + Object *o = world->objects[i]; + if (o == ladder || o == who || o->is_character || o->is_held || o->collider_count == 0 || o->type == OBJ_DOG || + o->type == OBJ_DECAL || o->type == OBJ_PUDDLE || o->type == OBJ_STINK) continue; + if (flatDist(o->position, origin) > 60.0f) continue; + for (int c = 0; c < o->collider_count; c++) { + Vec3 nn; + float t = rayToBox(origin, dir, Object_colliderCenter(o, c), &o->colliders[c], &nn); + if (t >= 0.0f && t < best) { + best = t; + hit = o; + *normal = nn; + *point = vec3_add(origin, vec3_scale(dir, t)); + } + } + } + return hit; +} + +/* Held up in the air: what its top points at (up to a bit past its length). + Its top face: the side of it towards you, just under that edge. */ +static Object *aimedAt(const World *world, const Object *who, const Object *l, Vec3 *face, Vec3 *n, float *aim_y) { + Object *thing = NULL; + if (who->type == OBJ_PLAYER && vec3_length(who->body.look_dir) > 0.5f) { /* where you look */ + Vec3 eye = vec3_add(who->position, vec3(0.0f, 0.93f * who->look.height, 0.0f)); + thing = rayHit(world, who, l, eye, vec3_normalize(who->body.look_dir), lengthOf(l) + AIM_VIEW, face, n); + } + if (!thing) { /* else where its top points */ + Vec3 axis = upAlong(l), from = pointAt(l, RAISE_GRIP); + thing = rayHit(world, who, l, from, axis, lengthOf(l) - RAISE_GRIP + AIM_PAST, face, n); + } + if (!thing) return NULL; + *aim_y = face->y; + if (n->y > 0.5f) { /* on top of it: the edge towards you */ + Vec3 to = flatv(vec3_sub(*face, who->position)); + if (vec3_length(to) < 0.05f) return NULL; + to = vec3_normalize(to); + Vec3 o = vec3(who->position.x, face->y - 0.08f, who->position.z); + Vec3 p, nn; + if (rayHit(world, who, l, o, to, LEAN_REACH + 4.0f, &p, &nn) != thing) return NULL; + *face = p; + *n = nn; + } + return thing; +} + int Ladder_lean(World *world, Object *who, Object *l, bool fit, const char **why) { Vec3 face = vec3_zero(), n = vec3_zero(); - Object *thing = faceAhead(world, who, l, &face, &n); + float aim_y = -1e9f; /* (held up: where its top should go) */ + 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; + Object *thing = raised ? aimedAt(world, who, l, &face, &n, &aim_y) : NULL; + if (!thing) thing = faceAhead(world, who, l, &face, &n); + if (!thing && raised) { *why = "Look at where its top should go (a wall, a tree) and click - look down to slide it together"; return LEAN_NO; } if (!thing) return LEAN_NOTHING; if (fabsf(n.y) > 0.35f) { *why = "No upright side to lean it on there"; return LEAN_NO; } if (thing->type == OBJ_BUSH) { *why = "The bush won't hold a ladder"; return LEAN_NO; } @@ -175,11 +246,27 @@ int Ladder_lean(World *world, Object *who, Object *l, bool fit, const char **why } const float len = Shapes_ladderLength(extend_to); bool reaches = h <= len * sinf(angle) - 0.15f; + /* aimed lower down the face (not at its top): against the face there, as high as it reaches */ + float against = len * sinf(angle) - 0.1f; + float lean = angle; + if (aim_y > -1e8f && aim_y - ground < h - AIM_EDGE && aim_y - ground < against) { + /* (its top there: flatter, the foot further out - a ladder only leans so flat) */ + reaches = false; + against = fmaxf(aim_y - ground, MIN_HEIGHT); + lean = asinf(clampf((against + 0.1f) / len, 0.0f, 1.0f)); + if (lean < FLATTEST * PI_F / 180.0f) { + static char said[96]; + snprintf(said, sizeof said, "Too long to lean that low - aim at least %.1f m up", len * sinf(FLATTEST * PI_F / 180.0f) - 0.1f); + *why = said; + return LEAN_NO; + } + } Vec3 rest = reaches && eaves ? vec3_add(face, vec3_scale(n, EAVES_OUT)) : face; - rest.y = ground + (reaches ? h : len * sinf(angle) - 0.1f); - Vec3 foot = vec3_add(rest, vec3_scale(n, (rest.y - ground) / tanf(angle) + 0.04f)); + rest.y = ground + (reaches ? h : against); + Vec3 foot = vec3_add(rest, vec3_scale(n, (rest.y - ground) / tanf(lean) + 0.04f)); foot.y = World_groundHeight(world, foot.x, foot.z, ground + 0.8f); if (fabsf(foot.y - ground) > 0.3f) { *why = "No level ground for its feet there"; return LEAN_NO; } + if (raised && flatDist(foot, who->position) > AIM_FOOT) { *why = "Too far away - step closer to put it up there"; return LEAN_NO; } Vec3 across = vec3_cross(n, UP); for (int s = -1; s <= 1; s += 2) { Vec3 p = vec3_add(foot, vec3_scale(across, (float)s * LADDER_WIDTH * 0.5f)); @@ -403,14 +490,45 @@ void Ladder_poseFeet(Object *who, float dt) { who->ladder_limbs_ok = true; } +bool Ladder_isRaised(const Object *l) { + return l->type == OBJ_LADDER && 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). */ +static float raiseGoal(const Object *who) { + float pitch = asinf(clampf(who->body.look_dir.y, -1.0f, 1.0f)); + if (who->type != OBJ_PLAYER) pitch = 0.0f; + return clampf(RAISE_ANGLE + RAISE_FOLLOW * pitch, RAISE_LOW, RAISE_HIGH); +} + +/* Up the rail from the foot to where a raised ladder is held, and to the other hand. */ +Vec3 Ladder_raisedGrip(const Object *l, float along) { + return pointAt(l, along); +} + void Ladder_placeHeld(Object *who, int hand, Object *l) { Vec3 facing = Object_facing(who); Vec3 right = vec3_normalize(vec3_cross(facing, UP)); + int jh = hand == HAND_LEFT ? JOINT_HAND_L : JOINT_HAND_R; + if (Ladder_isRaised(l)) { /* slid out: held near its foot, reaching up into the air in front */ + l->raise_goal = raiseGoal(who); /* (eased in Ladder_update) */ + if (l->raised <= 0.0f) l->raised = 0.15f; + Vec3 axis = vec3_normalize(vec3_add(vec3_scale(facing, cosf(l->raised)), vec3_scale(UP, sinf(l->raised)))); + Vec3 z = vec3_normalize(vec3_cross(right, axis)); /* (rungs across, the rails side by side) */ + l->rotation = euler_yxz_from_axes(axis, z); + Object_syncColliders(l); + Vec3 palm = Figure_toWorld(who->position, facing, who->skeleton.joints[jh]); + Vec3 foot = vec3_sub(palm, vec3_add(vec3_scale(axis, RAISE_GRIP), vec3_scale(right, (hand == HAND_LEFT ? -1.0f : 1.0f) * LADDER_WIDTH * 0.5f))); + l->position = foot; + Object_syncColliders(l); + l->velocity = vec3_zero(); + return; + } + l->raised = 0.0f; /* on edge, along the way you walk: local Y forward, the rungs upright */ l->rotation = euler_yxz_from_axes(facing, vec3_scale(right, -1.0f)); Object_syncColliders(l); - int joint = hand == HAND_LEFT ? JOINT_HAND_L : JOINT_HAND_R; - Vec3 palm = Figure_toWorld(who->position, facing, who->skeleton.joints[joint]); + Vec3 palm = Figure_toWorld(who->position, facing, who->skeleton.joints[jh]); Vec3 center = vec3_sub(palm, vec3(0.0f, LADDER_WIDTH * 0.5f - 0.02f, 0.0f)); /* holding the upper rail */ center = vec3_add(center, vec3_scale(right, hand == HAND_LEFT ? -0.05f : 0.05f)); l->position = vec3_sub(center, l->colliders[0].offset); diff --git a/entropie/src/world/ladder.h b/entropie/src/world/ladder.h index 38c91e3..b4957c2 100644 --- a/entropie/src/world/ladder.h +++ b/entropie/src/world/ladder.h @@ -43,7 +43,10 @@ Vec3 Ladder_edge(const Object *ladder); /* Climbing: hands on the rungs (after Hands_update) and feet on them (after Body_update). */ void Ladder_poseHands(Object *who, float dt); void Ladder_poseFeet(Object *who, float dt); -/* Carried at the side, on edge. */ +/* Slid out and in hand: held near its foot, reaching up in front (steeper looking up). */ +bool Ladder_isRaised(const Object *ladder); +Vec3 Ladder_raisedGrip(const Object *ladder, float along); +/* Carried at the side, on edge; slid out, raised. */ void Ladder_placeHeld(Object *who, int hand, Object *ladder); #endif /* WORLD_LADDER_H */ diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h index 60d7c17..0b3ab8a 100644 --- a/entropie/src/world/object.h +++ b/entropie/src/world/object.h @@ -202,6 +202,7 @@ typedef struct Object { float bump_cool; /* seconds until a bump counts again */ Vec3 pee_hit; /* characters: where the stream comes down */ float extend, extend_goal; /* ladders: 0 = slid together .. 1 = all the way out */ + float raised, raise_goal; /* ladders carried slid out: held near the foot, this steep up in front (radians, 0 = carried at the side) */ bool leaning; /* ladders: leaning against something (stands still) */ Vec3 lean_normal; /* ladders: away from what it leans on */ float lean_top; /* ladders: where along it the edge is (0 = against a face, no getting off) */ diff --git a/entropie/src/world/physics.c b/entropie/src/world/physics.c index 4644661..757f24b 100644 --- a/entropie/src/world/physics.c +++ b/entropie/src/world/physics.c @@ -446,6 +446,20 @@ static Vec3 slopeAdjusted(const Object *obj, const Terrain *terrain) { return vec3(m.x * k, m.y, m.z * k); } +#define CROSS_SPEED 0.6f /* m/s the body carries on over something when you stop half way */ + +/* Half way over something and stopping (or made to): the body carries on + over it, after the foot that went over. Walking back takes you back. */ +static Vec3 carryOver(const Object *obj, Vec3 m) { + const Body *b = &obj->body; + if (!b->crossing || Body_isBusy(b)) return m; + float speed = sqrtf(m.x * m.x + m.z * m.z); + if (!b->cross_force && speed > 0.3f) return m; /* (walking on, or back: as you like) */ + const float s = obj->look.height / 1.75f; + float go = CROSS_SPEED * s * smoothstepf(0.0f, 0.15f * s, b->cross_left); /* (easing in at the end) */ + return vec3(b->cross_dir.x * fmaxf(go, 0.15f * s), m.y, b->cross_dir.z * fmaxf(go, 0.15f * s)); +} + /* Something to step over ahead (or underfoot): slow down for it, the more the higher it is. */ static Vec3 steppingOver(const Object *obj, Vec3 m, Object *const *objects, int count) { float speed = sqrtf(m.x * m.x + m.z * m.z); @@ -581,6 +595,7 @@ void Physics_step(Object *obj, Object *const *objects, int count, const Terrain } if (obj->is_character && obj->on_terrain) obj->movement = slopeAdjusted(obj, terrain); if (obj->is_character && obj->is_grounded) hurdle(obj, objects, count); + if (obj->is_character && obj->is_grounded) obj->movement = carryOver(obj, obj->movement); if (obj->is_character && obj->is_grounded) obj->movement = steppingOver(obj, obj->movement, objects, count); Vec3 motion = vec3_add(obj->movement, obj->velocity); const Vec3 start = obj->position; diff --git a/entropie/src/world/world.c b/entropie/src/world/world.c index 48db74f..d266f65 100644 --- a/entropie/src/world/world.c +++ b/entropie/src/world/world.c @@ -331,6 +331,33 @@ static void runInto(World *world, Object *o, float into) { } } +#define HALF_OVER_STRAIN 0.025f /* energy a second standing half over something (legs apart) ... */ +#define HALF_OVER_LIFTED 0.06f /* ... and with a leg held up over it */ + +/* Standing still half over something is hard work, a leg held up over it + harder still: it costs energy. Worn out, too long, or the legs too far + apart: over you go (the body carries on); can't get over: down you go. */ +static void halfOver(Object *o, float dt) { + Body *b = &o->body; + if (!b->crossing || o->type == OBJ_DOG) return; + const float s = o->look.height / 1.75f; + const Foot *lead = &b->feet[b->cross_foot], *other = &b->feet[1 - b->cross_foot]; + float apart = vec3_length(vec3(lead->ankle.x - other->ankle.x, 0.0f, lead->ankle.z - other->ankle.z)); + if (o->ground_speed > 0.12f) { b->cross_held = fmaxf(0.0f, b->cross_held - 2.0f * dt); return; } + b->cross_held += dt; + float strain = (lead->stepping ? HALF_OVER_LIFTED : HALF_OVER_STRAIN) * (1.4f - 0.8f * o->fitness); + o->needs.stamina = fmaxf(0.0f, o->needs.stamina - strain * dt); + float hold = 1.0f + 5.0f * o->fitness * o->needs.stamina; /* (how long anyone keeps that up) */ + bool broken = apart > 0.95f * s; /* (a split: nobody stands like that) */ + if (b->cross_held > 0.4f && (o->needs.stamina < 0.03f || b->cross_held > hold || broken)) b->cross_force = true; + /* made to and still not getting over it: down */ + if (b->cross_force && b->cross_held > hold + 1.5f) { + Body_fall(b, b->cross_dir); + b->crossing = b->cross_force = false; + b->cross_held = 0.0f; + } +} + static void takeBump(World *world, Object *o, float dt) { float into = o->bump; o->bump = 0.0f; @@ -801,6 +828,7 @@ void World_update(World *world, float dt, float game_dt) { if (!o->is_character) continue; takeKnock(o); takeBump(world, o, dt); + halfOver(o, dt); animateCharacter(world, o, dt); keepCarriedClear(world, o); pushOffCarried(world, o);