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/people/climb.c raw
#include "people/climb.h"
#include "world/ladder.h"
#include <math.h>
/* What's in front at climbing distance and could be climbed from feet_y:
its top (the edge), or -1e9 if nothing (too high, no room up there). */
#define CLIMB_CLEAR 0.05f /* m over where the hands go ... */
#define SIT_LOW 0.55f /* the careful way up (sitting on it): something this high (scaled) ... */
#define SIT_HIGH 1.05f /* ... up to this */
#define SIT_FIT 0.45f /* ... for those less sporty than this (or old: slow on their feet) */
#define SIT_TIME 3.2f /* s it takes (fresh) */
#define BELLY_LOW 1.0f /* the belly way: ledges this high (scaled) ... */
#define BELLY_HIGH 1.55f /* ... up to this (higher: hanging on and pulling up) ... */
#define BELLY_FIT 0.45f /* ... for those less sporty than this (or drunk) */
#define BELLY_DROP 0.9f /* lying over it: the feet this far below the edge (scaled: the hips just over it) */
#define BELLY_TIME 2.6f /* s it takes (fresh) */
#define ROLL_DRUNK 0.55f /* the roll way: this drunk, belly first and then down flat on it and rolled over */
#define ROLL_ONTO 0.42f /* ... the belly over the edge by then (of the climb): the body takes over */
#define REACH_TURN 0.35f /* rad the shoulders turn, the first hand's side going up ahead */
#define CLIMB_OVER 0.5f /* ... from this high over it at the body: the way there is clear */
static bool onlyWalls(const Object *o, const void *ctx) {
(void)ctx;
return !o->is_static || o->is_character || o->type == OBJ_LADDER; /* (only fixed things block it) */
}
float Climb_ledgeAhead(const World *world, const Object *p, Vec3 *probe_out) {
const float s = p->look.height / 1.75f;
Vec3 f = Object_facing(p);
float reach = p->position.y + CLIMB_REACH * s;
Vec3 side = vec3(-f.z, 0.0f, f.x);
/* right in front, or a little further (a sloping roof); or to one side (up against a corner) */
static const float PROBE[][2] = { { 0.45f, 0.0f }, { 0.6f, 0.0f }, { 0.45f, -0.25f }, { 0.45f, 0.25f } };
for (int k = 0; k < (int)(sizeof(PROBE) / sizeof(PROBE[0])); k++) {
Vec3 probe = vec3_add(p->position, vec3_add(vec3_scale(f, PROBE[k][0] * s), vec3_scale(side, PROBE[k][1] * s)));
float top = World_groundHeight(world, probe.x, probe.z, reach + 0.05f);
float rise = top - p->position.y;
if (rise <= p->step_height * 0.9f || rise > CLIMB_REACH * s) continue;
if (PROBE[k][1] != 0.0f && rise > CLIMB_EASY * s) continue; /* (to one side: only something low enough to step up on) */
/* room to stand up there, and not a step beside us (the edge is ahead, not underfoot) */
if (World_groundHeight(world, probe.x, probe.z, top + 1.8f * s) > top + 0.05f) continue;
/* nothing solid in between, over the edge to it (a wall with something behind it: not
through the wall; down onto it, so a roof rising beyond its edge doesn't count) */
Vec3 from = vec3(p->position.x, top + CLIMB_OVER, p->position.z), to = vec3_sub(vec3(probe.x, top + CLIMB_CLEAR, probe.z), from);
float d = vec3_length(to);
if (d > 1e-3f && World_rayCast(world, from, vec3_scale(to, 1.0f / d), d, onlyWalls, NULL, NULL, NULL) >= 0.0f) continue;
*probe_out = probe;
return top;
}
return -1e9f;
}
/* Where the feet are as a climb starts: they go from there (no jump onto the climb's own path). */
void Climb_noteFeet(Object *p) {
for (int i = 0; i < 2; i++) {
p->climb_move.ankle0[i] = p->body.feet[i].ankle;
p->climb_move.toe0[i] = p->body.feet[i].toe;
}
}
/* Where the body will stand up there: the highest point under its footprint,
as the physics stands you - no hop once up. */
float Climb_standHeight(const World *world, const Object *p, Vec3 to, float s) {
const Collider *pc = &p->colliders[0];
float under = to.y;
for (int cx = -1; cx <= 1; cx += 2)
for (int cz = -1; cz <= 1; cz += 2) {
Vec3 q = vec3_add(to, vec3_add(vec3_scale(pc->axes[0], (float)cx * pc->half_extents.x), vec3_scale(pc->axes[2], (float)cz * pc->half_extents.z)));
float g = World_groundHeight(world, q.x, q.z, to.y + 0.4f * s);
if (g > under && g < to.y + 0.3f * s) under = g;
}
return under;
}
/* Hands on the edge: climb up. Easy ledges with a hand on them; higher up
(as far as the hands reach, standing or jumping) hanging on and pulling up. */
int Climb_start(const World *world, Object *p, float top, Vec3 probe) {
const float s = p->look.height / 1.75f;
Vec3 f = Object_facing(p);
float rise = top - p->position.y;
bool hard = rise > CLIMB_EASY * s || !p->is_grounded;
if (hard) {
if (p->hands.state[HAND_LEFT] != HAND_FREE || p->hands.state[HAND_RIGHT] != HAND_FREE) return CLIMB_NO_HANDS;
if (p->needs.stamina < 0.12f) return CLIMB_NO_STRENGTH;
if (p->drunk > 0.75f) return CLIMB_NO_GRIP;
}
Vec3 to = vec3(probe.x + f.x * 0.25f * s, top, probe.z + f.z * 0.25f * s);
to.y = World_groundHeight(world, to.x, to.z, top + 0.6f * s); /* (a sloping roof goes on up) */
if (to.y < top - 0.3f || to.y > top + 0.4f * s) to.y = top;
to.y = Climb_standHeight(world, p, to, s);
p->climb_move.from = p->position;
p->climb_move.to = to;
p->climb_move.across = vec3_normalize(vec3_cross(f, vec3(0.0f, 1.0f, 0.0f)));
p->climb_move.hang = fmaxf(p->position.y, top - 1.95f * s); /* hanging on, arms straight up */
p->climb_move.face = 0.45f * s; /* where the face of it is: the first point ahead that stands up */
for (float d = 0.05f; d < 0.8f * s; d += 0.02f) {
Vec3 q = vec3_add(p->position, vec3_scale(f, d));
if (World_groundHeight(world, q.x, q.z, top + 0.05f) > p->position.y + 0.25f * s) {
p->climb_move.face = d;
break;
}
}
float grip = p->climb_move.face + 0.1f * s; /* the hands on it, just over the edge */
p->climb_move.edge = vec3(p->position.x + f.x * grip, top + 0.01f, p->position.z + f.z * grip);
float vigor = Object_vigor(p) * (1.0f - 0.3f * fminf(p->drunk, 1.0f));
float sport = 0.6f + 0.9f * p->fitness; /* (the sporty pull up much quicker, the hard ones most of all) */
p->climb_move.time = (hard ? 1.3f + 0.7f * fmaxf(rise / s - CLIMB_EASY, 0.0f) : 0.6f + rise * 0.6f) / fmaxf(vigor, 0.4f)
/ (hard ? sport : sqrtf(sport));
p->climb_move.t = 0.0f;
/* hip high, and careful (not so sporty, or old): turned round, sat on it, legs up and over */
p->climb_move.sit_way = !hard && rise > SIT_LOW * s && rise < SIT_HIGH * s && (p->fitness < SIT_FIT || (p->pace > 0.05f && p->pace < 0.8f));
p->climb_move.sit_now = false;
p->climb_move.yaw0 = p->rotation.y;
p->climb_move.swing = p->main_hand == HAND_LEFT ? -1.0f : 1.0f;
if (p->climb_move.sit_way) p->climb_move.time = SIT_TIME / fmaxf(vigor, 0.4f);
/* chest high and not so sporty (or drunk): pushed up onto the belly, lying over it, a knee up, stand up */
bool hands_free = p->hands.state[HAND_LEFT] == HAND_FREE && p->hands.state[HAND_RIGHT] == HAND_FREE;
p->climb_move.belly_way = !p->climb_move.sit_way && hands_free && p->is_grounded && rise > BELLY_LOW * s && rise < BELLY_HIGH * s
&& (p->fitness < BELLY_FIT || p->drunk > 0.4f);
p->climb_move.roll_way = p->climb_move.belly_way && p->drunk > ROLL_DRUNK; /* (too drunk for a knee up: onto it and rolled over) */
p->climb_move.belly = 0.0f;
if (p->climb_move.belly_way) {
p->climb_move.time = BELLY_TIME / fmaxf(vigor, 0.4f);
p->climb_move.hang = p->position.y;
if (!hard) p->needs.stamina = fmaxf(0.0f, p->needs.stamina - 0.02f * rise / s);
}
Climb_noteFeet(p);
p->climbing = true;
p->velocity = vec3_zero();
if (hard) p->needs.stamina = fmaxf(0.0f, p->needs.stamina - 0.04f * rise / s);
return hard ? CLIMB_HARD : CLIMB_EASY_UP;
}
/* The feet while pulling up: against the face of it below the edge (pushing
up), then one knee comes up and that foot goes over onto the top, then the
other. Never through what you climb. */
#define KNEE_LEG 0.45f /* thigh and shin (scaled) ... */
#define KNEE_HIP 0.86f /* ... the hips this high above the feet ... */
#define KNEE_GAP 0.08f /* ... and the knee kept this clear of the face */
#define LOW_RISE 0.65f /* ... the body up on it by then (of the climb) ... */
#define LOW_EASE 0.0f /* ... (0: rising fastest at the start) */
#define LOW_BACK 0.25f /* climbing onto something low: the foot that pushes off stays this far back from its face */
/* A foot below the edge: where the knee would be (bent forward, in the plane
of the climb). Too close to the face under the edge: the foot goes back
until the knee is clear of it. */
static float kneeClear(float along, float y, float root_along, float root_y, float face, float top, float s) {
const float leg = KNEE_LEG * s, hip_a = root_along + 0.04f * s, hip_y = root_y + KNEE_HIP * s;
float pushed = along, knee_y = top;
for (int k = 0; k < 42; k++, pushed -= 0.01f * s) {
float dx = pushed - hip_a, dy = y - hip_y, d = sqrtf(dx * dx + dy * dy);
if (d < 1e-3f) continue;
float h = d < 2.0f * leg ? sqrtf(leg * leg - 0.25f * d * d) : 0.0f;
float px = dy / d, py = -dx / d; /* (perpendicular, the forward one) */
if (px < 0.0f) { px = -px; py = -py; }
float ka = hip_a + 0.5f * dx + px * h, ky = hip_y + 0.5f * dy + py * h;
if (k == 0) knee_y = ky;
if (ka < face - KNEE_GAP * s) break;
}
/* (the knee up over the top already: no need to keep the foot back - eased off, no jump) */
return lerpf(pushed, along, smoothstepf(top - 0.08f * s, top + 0.06f * s, knee_y));
}
static void climbFeet(const World *world, Object *p, float t, bool high, float s) {
const Vec3 a = p->climb_move.from, b = p->climb_move.to;
Vec3 f = vec3(b.x - a.x, 0.0f, b.z - a.z);
f = vec3_length(f) > 1e-3f ? vec3_normalize(f) : Object_facing(p);
const Vec3 side = p->climb_move.across;
const float top = b.y, face = p->climb_move.face;
const float ground = a.y;
float root_along = vec3_dot(vec3(p->position.x - a.x, 0.0f, p->position.z - a.z), f);
for (int i = 0; i < 2; i++) {
float sign = i == 0 ? -1.0f : 1.0f;
bool lead = i == 1; /* (the right knee comes up first) */
float u = high ? (lead ? smoothstepf(0.56f, 0.86f, t) : smoothstepf(0.74f, 1.0f, t)) /* (once the hips are up near the edge) */
: (lead ? smoothstepf(0.12f, 0.4f, t) : smoothstepf(0.5f, 0.85f, t));
/* below: under you, but in front of the face, no higher than just under the edge */
float along_below = fminf(root_along - 0.06f * s, face - (!high && top - ground < 1.0f * s && !lead ? LOW_BACK : 0.25f) * s); /* (the knees bend forward: room for them; low, the push-off foot further back) */
float y_below = clampf(p->position.y + 0.08f * s, ground + 0.08f * s, top - 0.12f * s);
/* on top: a little over the edge, or under you once you're up there */
float along_top = fmaxf(face + 0.2f * s, root_along);
float y_top = top + 0.08f * s;
{ /* (on what's under that foot where it ends up - the top isn't always level; the same all along: no jumps) */
Vec3 q = vec3_add(b, vec3_scale(side, sign * 0.11f * s));
float g = World_groundHeight(world, q.x, q.z, top + 1.0f * s); /* (from well above: a roof slope may rise there) */
if (g > top - 0.6f * s) y_top = clampf(g, top - 0.3f * s, top + 0.15f * s) + 0.08f * s; /* (clamped, not cut off: no jumps on a slope) */
}
/* up first (clear of the edge), then over */
float up = smoothstepf(0.0f, 0.6f, u), across = smoothstepf(0.35f, 1.0f, u);
if (top - ground < 1.0f * s) { /* low: up and over in one swing (the knee comes over the edge) */
up = smoothstepf(0.0f, 0.3f, u);
across = smoothstepf(0.25f, 0.85f, u);
}
float along = lerpf(along_below, along_top, across);
float yy = lerpf(y_below, y_top, up) + 0.12f * s * sinf(PI_F * u);
/* over it: on it, not in it; below the edge: the knee kept clear of the face - one into the other, no jump */
float over = smoothstepf(face - 0.14f * s, face - 0.03f * s, along);
yy = lerpf(yy, fmaxf(yy, y_top), over);
/* (the foot up over the top already needs no room for the knee) */
float free = fmaxf(over, smoothstepf(top - 0.04f * s, top + 0.08f * s, yy));
if (free < 1.0f) along = lerpf(kneeClear(along, yy, root_along, p->position.y, face, top, s), along, free);
Vec3 ankle = vec3_add(a, vec3_add(vec3_scale(f, along), vec3_scale(side, sign * 0.11f * s)));
ankle.y = yy;
/* toes: at the wall pointing into it (on the toes), on top flat ahead */
Vec3 toe = vec3_add(ankle, vec3_scale(f, 0.14f * s));
toe.y = ankle.y - lerpf(0.06f, 0.05f, across) * s;
/* (from where they were when it started: eased onto the climb's path) */
float onto = smoothstepf(0.0f, 0.15f, t);
p->climb_ankle[i] = vec3_lerp(p->climb_move.ankle0[i], ankle, onto);
p->climb_toe[i] = vec3_lerp(p->climb_move.toe0[i], toe, onto);
}
p->climb_feet = true;
}
/* The careful way up (hip high): turn round with the back to it, sit up on
the edge (hands behind on it), swing the legs up and round onto it (sitting
cross-legged on top for a moment), and stand up. */
static void sitWay(const World *world, Object *p, float t, float s) {
ClimbMove *c = &p->climb_move;
const Vec3 a = c->from, b = c->to;
Vec3 f = vec3(b.x - a.x, 0.0f, b.z - a.z);
f = vec3_length(f) > 1e-3f ? vec3_normalize(f) : Object_facing(p);
const float rise = b.y - a.y;
/* where the hips sit on the edge: just over it; standing there (facing out) a step in front of it */
Vec3 edge_in = vec3_add(vec3(a.x, a.y, a.z), vec3_scale(f, c->face + 0.12f * s));
Vec3 stand = vec3_sub(edge_in, vec3_scale(f, 0.36f * s));
float turn = smoothstepf(0.04f, 0.24f, t), swing = smoothstepf(0.48f, 0.76f, t), rise_up = smoothstepf(0.8f, 1.0f, t);
/* facing: round with the back to it, then round again (the legs coming up) to face onto it */
p->rotation.y = c->yaw0 + c->swing * PI_F * (turn - swing);
Vec3 pos = vec3_lerp(vec3(a.x, a.y, a.z), stand, turn); /* a step back to it, turning */
pos = vec3_lerp(pos, vec3(b.x, b.y, b.z), smoothstepf(0.5f, 0.8f, t)); /* up and onto it, swinging round */
/* a hop up onto the edge (the hips on it, the legs hanging), then the legs up onto it */
const float hang = fminf(0.45f * s, rise - 0.05f), hop = smoothstepf(0.24f, 0.4f, t), legs_up = smoothstepf(0.48f, 0.72f, t);
pos.y = lerpf(lerpf(a.y, b.y - hang, hop), b.y, legs_up);
p->position = pos;
p->velocity = vec3_zero();
Object_syncColliders(p);
c->sit_now = turn > 0.7f && rise_up < 0.5f; /* (sitting down already as the turn ends: the body takes a moment to get down) */
p->sit_style = swing > 0.5f ? SIT_GROUND : SIT_SEAT; /* on the edge, then cross-legged on top */
p->body.seat_height = lerpf(lerpf(rise, hang, hop), 0.0f, legs_up) + 0.02f;
if (t > 0.2f && t < 0.62f) /* the hands behind on the edge, pushing up */
Hands_holdEdge(p, vec3_add(edge_in, vec3(0.0f, rise, 0.0f)), c->across, 0.0f);
(void)world;
}
bool Climb_update(const World *world, Object *p, float dt) {
const float s = p->look.height / 1.75f;
ClimbMove *c = &p->climb_move;
c->t += dt / c->time;
if (c->sit_way) {
float ts = clampf(c->t, 0.0f, 1.0f);
sitWay(world, p, ts, s);
if (ts >= 1.0f) {
p->climbing = false;
c->sit_now = false;
p->sit_style = SIT_SEAT;
p->body.seat_height = SEAT_HEIGHT;
p->is_grounded = true;
p->body.replant = true;
p->body.velocity = vec3_zero();
}
return !p->climbing;
}
float t = clampf(c->t, 0.0f, 1.0f);
const Vec3 a = c->from, b = c->to;
if (c->roll_way && t >= ROLL_ONTO) { /* lying over it: flat onto the top along the edge, then rolled over onto it */
Vec3 f = vec3(b.x - a.x, 0.0f, b.z - a.z);
f = vec3_length(f) > 1e-3f ? vec3_normalize(f) : Object_facing(p);
Vec3 along = c->across;
float side = vec3_dot(vec3_cross(along, vec3(0.0f, 1.0f, 0.0f)), f) > 0.0f ? 1.0f : -1.0f; /* (rolling inwards) */
Vec3 on = vec3_add(c->edge, vec3_scale(f, 0.12f * s));
p->position = vec3(on.x, b.y, on.z);
p->climbing = false;
p->climb_feet = false;
p->is_grounded = true;
p->velocity = vec3_zero();
c->belly = 0.0f;
Object_syncColliders(p);
Body_rollOnto(&p->body, along, side);
return true;
}
const float hang = c->hang;
bool high = hang > a.y + 0.02f || c->time > 1.2f || c->belly_way;
float y, over;
float knees = 0.0f;
if (c->belly_way) { /* a push up on the arms, the belly onto the edge, lying over it, a knee up onto it, standing up */
float push = smoothstepf(0.1f, 0.34f, t), rise = smoothstepf(0.56f, 0.88f, t);
float belly_y = fminf(b.y - BELLY_DROP * s, a.y + 0.6f * s);
y = lerpf(lerpf(a.y, belly_y, push), b.y + 0.02f, rise);
over = 0.12f * smoothstepf(0.3f, 0.5f, t) + 0.88f * smoothstepf(0.62f, 1.0f, t); /* (wriggling on a little, then onto it) */
c->belly = smoothstepf(0.22f, 0.4f, t) * (1.0f - smoothstepf(0.62f, 0.92f, t));
knees = 0.7f * smoothstepf(0.58f, 0.78f, t);
} else if (high) { /* up to hang on (a little hop), hang a moment, pull up, then over the edge */
/* pulled up till the arms press down on it (hips near the edge), then
the knee comes up and you rise onto it - the hands stay on it all along */
float grab = smoothstepf(0.0f, 0.18f, t), pull = smoothstepf(0.28f, 0.62f, t), rise = smoothstepf(0.56f, 0.88f, t);
float press = fmaxf(hang, fminf(b.y, b.y - 0.75f * s));
y = lerpf(lerpf(lerpf(a.y, hang, grab), press, pull), b.y + 0.02f, rise);
over = smoothstepf(0.62f, 1.0f, t);
knees = 0.7f * smoothstepf(0.6f, 0.8f, t); /* a knee comes up onto the edge */
} else {
if (b.y - a.y < 1.0f * s) { /* low: up quickly first (the hips over the knee coming up, not the knee into the face) */
float k = clampf(t / LOW_RISE, 0.0f, 1.0f);
y = lerpf(a.y, b.y + 0.02f, 1.0f - (1.0f - k) * (1.0f - k) * (1.0f - LOW_EASE * k));
} else {
y = lerpf(a.y, b.y + 0.02f, smoothstepf(0.0f, 0.7f, t));
}
over = smoothstepf(0.45f, 1.0f, t);
}
p->position = vec3(lerpf(a.x, b.x, over), y, lerpf(a.z, b.z, over));
p->velocity = vec3_zero();
Object_syncColliders(p);
climbFeet(world, p, t, high, s);
if (high && t < 0.84f) { /* the hands come up in front of the face first, then over onto the edge */
Vec3 fwd = vec3(b.x - a.x, 0.0f, b.z - a.z);
fwd = vec3_length(fwd) > 1e-3f ? vec3_normalize(fwd) : Object_facing(p);
/* (up wide, outside the shoulders - pressed against it there's no room in front - then in onto it) */
float onto = smoothstepf(0.05f, 0.16f, t);
Vec3 before = vec3_add(c->edge, vec3_add(vec3_scale(fwd, -0.12f * s), vec3(0.0f, 0.1f * s, 0.0f)));
/* (coming over the edge the hands go on ahead onto the top: pressed flat on it, under the shoulders) */
Vec3 on = vec3_add(c->edge, vec3_scale(fwd, 0.35f * s * smoothstepf(0.55f, 0.9f, t)));
if (c->belly_way) on = vec3_add(c->edge, vec3_scale(fwd, 0.3f * s * smoothstepf(0.25f, 0.45f, t) + 0.1f * s * smoothstepf(0.6f, 0.9f, t))); /* (forearms flat on it, lying over) */
Hands_holdEdge(p, vec3_lerp(before, on, onto), vec3_scale(c->across, lerpf(1.9f, 1.0f, onto)), knees);
/* (one hand reaches up first, the other after it - the sporty quicker; that shoulder goes ahead) */
p->hands.edge_lead = p->main_hand;
p->hands.edge_trail = 1.0f - smoothstepf(0.1f, 0.24f + 0.08f * (1.0f - p->fitness), t);
p->hands.edge_turn = (p->main_hand == HAND_LEFT ? REACH_TURN : -REACH_TURN) * smoothstepf(0.0f, 0.08f, t) * p->hands.edge_trail;
}
else if (t < 0.8f) Hands_touch(p, c->edge);
if (t >= 1.0f) {
p->climbing = false;
p->is_grounded = true;
/* (the feet are on it already - climbFeet - and stay put: no replanting, no jump) */
p->body.velocity = vec3_zero(); /* (standing there, not a stride ahead) */
}
return !p->climbing;
}
bool Climb_overLadderTop(const World *world, Object *p, const Object *ladder) {
Vec3 n = ladder->lean_normal, edge = Ladder_edge(ladder), over = edge;
float top = -1e9f;
static const float INWARD[] = { 1.0f, 0.6f, 0.35f }; /* well onto it if there's room (a roof), else a narrow top */
for (int k = 0; k < 3; k++) {
over = vec3_sub(edge, vec3_scale(n, INWARD[k]));
top = World_groundHeight(world, over.x, over.z, edge.y + 1.5f);
if (top > edge.y - 0.6f && World_groundHeight(world, over.x, over.z, top + 1.8f) <= top + 0.05f) break;
top = -1e9f;
}
if (top < -1e8f) return false;
Climb_noteFeet(p);
Ladder_getOff(p);
p->climbing = true; /* pulled over the edge like any ledge */
ClimbMove *c = &p->climb_move;
c->from = p->position;
c->to = vec3(over.x, Climb_standHeight(world, p, vec3(over.x, top, over.z), p->look.height / 1.75f), over.z);
c->edge = edge;
c->hang = p->position.y; /* (already up there: no hanging on) */
c->across = vec3_normalize(vec3_cross(Object_facing(p), vec3(0.0f, 1.0f, 0.0f)));
{ /* (the face of it: where the ladder's top rests) */
Vec3 to_edge = vec3_sub(edge, p->position);
c->face = fmaxf(0.05f, -(to_edge.x * n.x + to_edge.z * n.z));
}
c->time = 1.6f / (0.6f + 0.9f * p->fitness); /* (pulled up over it like a high ledge: from the ladder the feet start well below; quicker sporty) */
c->t = 0.0f;
return true;
}