/* * Broken bones (world/injury.h): somebody lies there hurt - an ambulance is * called. A while later it comes in along the way in by car (lights, siren), * stops by the yard, the paramedics fetch them, off it goes. They're at the * hospital (out of sight: in Berlin with the others between weekends) and * come back a day and a half later - or with the next weekend, whichever is * first - with a cast (an arm: weaker, slower at work; a leg: slow on their * feet). The cast comes off two weekends on. * * You: the ambulance takes you; six hours later you're back with a cast. * A guest: the ambulance takes them, and that's their festival over. */ #include #include "game/game_internal.h" #include "world/car.h" #include "world/cast.h" #include "world/injury.h" #include "world/manor.h" #define AMB_COMES 1200.0f /* game s till it's here (out in the country) */ #define AMB_SPEED 9.0f /* m/s along the way in */ #define AMB_STOP 12.0f /* m short of where your car stops */ #define AMB_LOADING 10.0f /* real s the paramedics take */ #define AWAY_HOURS 36.0f /* story hours at the hospital (a cast on, then back) */ #define YOU_AWAY 6.0f /* ... you: hours */ #define CAST_WEEKENDS 2 /* the cast comes off this many weekends on */ #define YOU_WAIT 4.0f /* real s lying there before you're taken */ static const char *limb(int kind) { return kind == INJ_ARM ? "arm" : "leg"; } static void putOnGround(Game *game, Object *o) { Vec3 path[16]; int n = 0; float yaw; Site_arrivalPath(path, &n, &yaw); Vec3 at = n > 4 ? path[4] : Site_yard(); at.y = World_groundHeight(&game->world, at.x, at.z, at.y + 3.0f); o->sitting = false; o->seat = NULL; o->ai.meeting = false; o->position = at; o->velocity = o->movement = vec3_zero(); o->body.replant = true; Object_syncColliders(o); } /* Back from the hospital: the cast on. */ static void back(Game *game, Object *o, bool to_ground) { int kind = o->injured ? o->injured : INJ_LEG; Injury_setCast(o, kind, o->injury_side, true); o->injured = INJ_NONE; o->hospital_until = 0.0f; o->cast_until = game->weekend + CAST_WEEKENDS; if (to_ground) putOnGround(game, o); if (o->type == OBJ_HUMAN) Ai_say(o, kind == INJ_ARM ? "I'm back! Arm in plaster - I'll do what I can one-handed." : "Back again! Slow on this leg, but I'm here."); snprintf(game->story_line, sizeof game->story_line, "%s %s back from the hospital - with a cast on the %s", o->type == OBJ_PLAYER ? "You" : o->name, o->type == OBJ_PLAYER ? "are" : "is", limb(kind)); say(game, game->story_line); game->message_time = 6.0f; } /* You: taken to the hospital, back hours later with the cast. */ static void youHurt(Game *game, float dt) { Object *p = game->player; if (!p->injured) { game->you_hurt_t = 0.0f; return; } if (game->you_hurt_t == 0.0f) { snprintf(game->story_line, sizeof game->story_line, "Your %s - it's broken. Somebody calls the ambulance...", limb(p->injured)); say(game, game->story_line); game->message_time = YOU_WAIT + 1.0f; } if ((game->you_hurt_t += dt) < YOU_WAIT) return; game->you_hurt_t = 0.0f; float add = YOU_AWAY; game->story_hours += add; game->daycycle.seconds = fmodf(game->daycycle.seconds + add * 3600.0f, 24.0f * 3600.0f); p->body.down_time = 0.0f; p->body.pose = POSE_UP; p->body.replant = true; Vec3 at = Site_yard(); at.y = World_groundHeight(&game->world, at.x, at.z, at.y + 3.0f); p->position = at; p->velocity = p->movement = vec3_zero(); Object_syncColliders(p); game->on_rope = false; back(game, p, false); } static Object *waiting(const Game *game) { for (int i = 0; i < game->world.count; i++) { Object *o = game->world.objects[i]; if (o->type == OBJ_HUMAN && o->injured && o->hospital_until <= 0.0f && !o->gone_home) return o; } return NULL; } static Object *crewNear(const Game *game, const Object *hurt) { Object *best = NULL; float best_d = 60.0f; for (int i = 0; i < game->world.count; i++) { Object *o = game->world.objects[i]; if (o == hurt || o->type != OBJ_HUMAN || o->cast_id <= 0 || o->injured || Body_isDown(&o->body) || Game_inBerlin(o)) continue; float d = hypotf(o->position.x - hurt->position.x, o->position.z - hurt->position.z); if (d < best_d) { best_d = d; best = o; } } return best; } static void ambulance(Game *game, float dt) { Ambulance *a = &game->ambulance; World *w = &game->world; switch (a->stage) { case AMB_NONE: { Object *hurt = waiting(game); if (!hurt) return; a->who = hurt; a->stage = AMB_CALLED; a->t = 0.0f; Object *c = crewNear(game, hurt); if (c) Ai_say(c, "Don't move - I'm calling 112! The ambulance is on its way."); snprintf(game->story_line, sizeof game->story_line, "%s is hurt - a broken %s. The ambulance is called; out here it takes a while", hurt->name, limb(hurt->injured)); say(game, game->story_line); game->message_time = 6.0f; return; } case AMB_CALLED: if ((a->t += w->game_dt) < AMB_COMES || !game_drivePath(game)) return; { Vec3 pos, dir; game_driveAt(&game->arrival, 0.0f, &pos, &dir); Object *car = Car_create(pos, yaw_facing(dir)); if (!car) return; car->variant = 1; Mesh_destroy(&car->mesh); car->mesh = Shapes_ambulance(); snprintf(car->name, sizeof car->name, "Ambulance"); if (!World_add(w, car)) return; a->car = car; a->s = 0.0f; a->stage = AMB_IN; say(game, "Blue lights on the gravel road - the ambulance is here"); } return; case AMB_IN: case AMB_OUT: { float stop = game->arrival.length - AMB_STOP; a->s += (a->stage == AMB_IN ? 1.0f : -1.0f) * AMB_SPEED * dt; if (a->stage == AMB_IN && a->s >= stop) { a->s = stop; a->stage = AMB_PARKED; a->t = 0.0f; } Vec3 pos, dir; game_driveAt(&game->arrival, a->s, &pos, &dir); Car_place(w, a->car, pos, yaw_facing(a->stage == AMB_OUT ? vec3_scale(dir, -1.0f) : dir)); if (a->stage == AMB_OUT && a->s <= 0.0f) { /* (gone: to the hospital) */ Object *o = a->who; if (o && o->riding == a->car) Car_getOut(o); if (o && o->guest) { o->gone_home = true; o->inside_door = pos; o->indoors = 1e7f; o->injured = INJ_NONE; } else if (o) { o->hospital_until = game->story_hours + AWAY_HOURS; o->position = vec3(BERLIN_X + BERLIN_BAR + 1.5f, 0.05f, 2.0f); o->velocity = o->movement = vec3_zero(); o->ai.meeting = true; Object_syncColliders(o); } Object_useUp(a->car); memset(a, 0, sizeof *a); } return; } case AMB_PARKED: { Object *o = a->who; if ((a->t += dt) < AMB_LOADING) return; if (o && !o->riding) { o->body.down_time = 0.0f; /* (on the stretcher, in) */ o->body.pose = POSE_UP; Car_getIn(a->car, o, 2); snprintf(game->story_line, sizeof game->story_line, "The paramedics carry %s to the ambulance on a stretcher", o->name); say(game, game->story_line); } a->stage = AMB_OUT; return; } default: return; } } void game_injury(Game *game, float dt) { World *w = &game->world; if (game->meeting.kind) return; Object *you = game->player; if (you->sprain > 0.0f && !game->sprain_told) { game->sprain_told = true; say(game, "Ow - you twisted your ankle. A few hours of limping"); } else if (you->sprain <= 0.0f && game->sprain_told) { game->sprain_told = false; say(game, "Your ankle's fine again"); } youHurt(game, dt); ambulance(game, dt); for (int i = 0; i < w->count; i++) { Object *o = w->objects[i]; if (!o->is_character || o->type == OBJ_DOG) continue; if (o->hospital_until > 0.0f) { /* at the hospital: back when it's time (or the next weekend brought them) */ if (!Game_inBerlin(o)) back(game, o, false); else if (game->story_hours >= o->hospital_until) back(game, o, true); } if (o->look.cast && game->weekend >= o->cast_until && o->cast_until > 0) { int kind = Injury_castKind(o), side = (o->look.cast - 1) % 2; Injury_setCast(o, kind, side, false); o->cast_until = 0; if (!Game_inBerlin(o)) { snprintf(game->story_line, sizeof game->story_line, "%s%s cast is off - good as new", o->type == OBJ_PLAYER ? "Your" : o->name, o->type == OBJ_PLAYER ? "" : "'s"); say(game, game->story_line); } } } } /* Dev: all well again - out of the ambulance, back from the hospital, the cast and the sprain off. */ void game_injuryHeal(Game *game, Object *o) { Ambulance *a = &game->ambulance; if (a->who == o) { if (a->car && o->riding == a->car) Car_getOut(o); a->who = NULL; if (a->stage == AMB_CALLED) { a->stage = AMB_NONE; a->t = 0.0f; } /* (not on its way yet: called off) */ } if (o == game->player) game->you_hurt_t = 0.0f; if (o->hospital_until > 0.0f) { o->hospital_until = 0.0f; o->ai.meeting = false; if (Game_inBerlin(o) && !game->meeting.kind) putOnGround(game, o); } Injury_heal(o); game->sprain_told = false; } bool game_ambulanceHere(const Game *game) { return game->ambulance.stage >= AMB_IN; }