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/game/game_shop.c raw
/*
* The shopping run: asked in a talk (talk.c NODE_SHOP), the one with the car
* keys walks over to the car, backs it out of the yard, turns and drives off
* along the way in; an hour later (game time) the car comes back the same way
* with the boot full - boards, nails, screws, paint, food and drink - parks
* where it stood and the boot opens: the driver and whoever is idle nearby
* unload it (ai.c unloadCar; material in front of the car room, food into the
* kitchen: car.c Car_dropSpot). The money comes out of the cash box as they
* leave.
*/
#include <math.h>
#include "game/game_internal.h"
#include "world/car.h"
#define SHOP_AWAY 3600.0f /* game s at the shops (there and back included) */
#define TO_CAR_MAX 120.0f /* s walking to the car: then they're in it anyway (stuck somewhere) */
#define BACK_OUT 10.0f /* m backed out of the parking spot ... */
#define BACK_SPEED 1.6f /* ... this fast ... */
#define TURN_TIME 3.0f /* ... turned round in this long (s) ... */
#define DRIVE_TOP 6.0f /* ... then off (m/s) */
#define DRIVE_ACCEL 1.6f
#define DRIVE_BRAKE 1.4f
#define HIDDEN_DEPTH 300.0f /* m under the ground while it's away (out of sight, out of the way) */
#define HELPERS 2 /* idle people near who come and unload too */
#define HELPER_NEAR 45.0f
static Object *theCar(Game *game) {
if (game->arrival.car) return game->arrival.car;
for (int i = 0; i < game->world.count; i++)
if (game->world.objects[i]->type == OBJ_CAR) return game->arrival.car = game->world.objects[i];
return NULL;
}
static int total(const int list[SHOP_ITEMS]) {
int sum = 0;
for (int k = 0; k < SHOP_ITEMS; k++) sum += list[k] * SHOP_PRICE[k];
return sum;
}
void game_shopStart(Game *game, Object *driver, const int list[SHOP_ITEMS]) {
Errand *e = &game->errand;
Object *car = theCar(game);
if (!car || !driver || e->stage != ERRAND_NONE || !game_drivePath(game)) return;
memset(e, 0, sizeof *e);
for (int k = 0; k < SHOP_ITEMS; k++) e->list[k] = list[k];
e->driver = driver;
e->stage = ERRAND_TO_CAR;
if (Ai_isHelping(driver)) Ai_stopHelping(driver);
driver->ai.mode = AI_IDLE;
for (int h = 0; h < 2; h++) if (driver->hands.item[h] && driver->hands.state[h] == HAND_HOLDING) Hands_drop(driver, h); /* (hands free to drive) */
driver->ai.errand = true;
driver->ai.errand_with = NULL;
driver->ai.errand_there = false;
driver->ai.errand_to = Car_door(car, 0);
game->world.shopping = true;
}
/* Out of sight while it's away: down under the ground (and the driver in it). */
static void hide(Game *game, Object *car) {
Car_place(&game->world, car, game->arrival.path[0], car->rotation.y);
car->position.y -= HIDDEN_DEPTH;
Object_syncColliders(car);
for (int i = 0; i < game->world.count; i++) {
Object *o = game->world.objects[i];
if (o->riding != car) continue;
o->position.y -= HIDDEN_DEPTH;
o->sit_at = o->position;
Object_syncColliders(o);
}
}
/* The boot full of what was on the list. */
static void loadUp(Object *car, const int list[SHOP_ITEMS]) {
for (int k = 0; k < list[SHOP_FOOD]; k++) { /* (food and drink at the bottom, the material on top) */
Car_load(car, CARGO_WATER, NULL);
Car_load(car, CARGO_BEER, NULL);
Car_load(car, CARGO_SAUSAGES, NULL);
Car_load(car, CARGO_FOOD, NULL);
}
for (int k = 0; k < list[SHOP_PAINT]; k++) Car_load(car, CARGO_PAINT, NULL);
for (int k = 0; k < list[SHOP_FIXINGS]; k++) { Car_load(car, CARGO_SCREWS, NULL); Car_load(car, CARGO_NAILS, NULL); }
for (int k = 0; k < list[SHOP_BOARDS]; k++) Car_load(car, CARGO_BOARDS, NULL);
}
/* Parked again: out, the boot up, unloading (the driver and a couple of idle people near). */
static void arrived(Game *game, Object *car) {
Errand *e = &game->errand;
Object *d = e->driver;
Car_place(&game->world, car, game->arrival.path[ARRIVAL_SAMPLES - 1], game->arrival.park_yaw);
if (d && d->riding) Car_getOut(d);
Car_openBoot(car, true);
int n = 0;
for (int i = 0; i < game->world.count; i++) {
Object *o = game->world.objects[i];
bool idle = o->ai.mode == AI_IDLE || o->ai.mode == AI_WANDER;
if (o->type != OBJ_HUMAN || o->riding || o->lives_here || Body_isDown(&o->body) || o->in_tent || o->indoors > 0.0f) continue;
if (o != d && (n >= HELPERS || !idle || o->ai.on_project || hypotf(o->position.x - car->position.x, o->position.z - car->position.z) > HELPER_NEAR)) continue;
o->ai.unload = true;
o->ai.unload_car = car;
o->ai.unload_time = 0.0f;
o->ai.side = (float)(n % 3 - 1);
if (o != d) n++;
}
if (d) Ai_say(d, "We're back! Come on, help me unload - the boot's full.");
static char line[96];
snprintf(line, sizeof line, "%s is back from the hardware store - help unload the car", d ? d->name : "The car");
say(game, line);
game->world.shopping = false;
memset(e, 0, sizeof *e);
}
void game_shop(Game *game, float dt) {
Errand *e = &game->errand;
if (e->stage == ERRAND_NONE) return;
Object *car = theCar(game), *d = e->driver;
Arrival *a = &game->arrival;
if (!car || !game_drivePath(game)) { memset(e, 0, sizeof *e); game->world.shopping = false; return; }
e->time += e->stage == ERRAND_AWAY ? game->world.game_dt : dt;
Vec3 pos, dir;
static char line[96];
switch (e->stage) {
case ERRAND_TO_CAR: /* walking over (ai_project.c ai_errand); then in, the money out of the box, off */
if (!d || Body_isDown(&d->body)) {
if (d) d->ai.errand = false;
memset(e, 0, sizeof *e);
game->world.shopping = false;
return;
}
if (!d->ai.errand_there && e->time < TO_CAR_MAX) return;
d->ai.errand = d->ai.errand_there = false;
Car_openBoot(car, false);
Car_getIn(car, d, 0);
game->world.cash -= (float)total(e->list);
e->stage = ERRAND_LEAVING;
e->time = 0.0f;
e->s = a->length;
e->speed = 0.0f;
snprintf(line, sizeof line, "%s drives off to the hardware store (%d EUR from the cash box)", d->name, total(e->list));
say(game, line);
break;
case ERRAND_LEAVING: {
float back_to = a->length - BACK_OUT;
if (e->s > back_to) { /* backing out, still nose to the barn */
e->s = fmaxf(back_to, e->s - BACK_SPEED * dt);
game_driveAt(a, e->s, &pos, &dir);
Car_place(&game->world, car, pos, yaw_facing(dir));
e->time = 0.0f;
return;
}
game_driveAt(a, e->s, &pos, &dir);
if (e->time < TURN_TIME) { /* turning round where it is */
float t = smoothstepf(0.0f, TURN_TIME, e->time);
Car_place(&game->world, car, pos, yaw_facing(dir) + PI_F * t);
return;
}
e->speed = fminf(e->speed + DRIVE_ACCEL * dt, DRIVE_TOP);
e->s = fmaxf(0.0f, e->s - e->speed * dt);
game_driveAt(a, e->s, &pos, &dir);
Car_place(&game->world, car, pos, yaw_facing(vec3_scale(dir, -1.0f)));
if (e->s <= 0.0f) { hide(game, car); e->stage = ERRAND_AWAY; e->time = 0.0f; }
break;
}
case ERRAND_AWAY:
if (e->time < SHOP_AWAY) return;
loadUp(car, e->list);
e->stage = ERRAND_BACK;
e->time = 0.0f;
e->s = 0.0f;
e->speed = 3.0f;
break;
case ERRAND_BACK: { /* in again the way it came the first time, braking for its spot */
float left = a->length - e->s;
float brake = sqrtf(fmaxf(0.0f, 2.0f * DRIVE_BRAKE * left));
e->speed = fminf(fminf(e->speed + DRIVE_ACCEL * dt, DRIVE_TOP), brake + 0.15f);
e->s = fminf(a->length, e->s + e->speed * dt);
game_driveAt(a, e->s, &pos, &dir);
float yaw = yaw_facing(dir);
if (left < 4.0f) yaw = lerpf(yaw, a->park_yaw, smoothstepf(4.0f, 0.0f, left));
Car_place(&game->world, car, pos, yaw);
if (e->s >= a->length - 0.01f) arrived(game, car);
break;
}
default: break;
}
}
void game_shopLoaded(Game *game) {
Errand *e = &game->errand;
Object *car = theCar(game);
game->world.shopping = e->stage != ERRAND_NONE;
if (e->stage == ERRAND_NONE || !car || !game_drivePath(game)) return;
if (e->stage == ERRAND_TO_CAR && e->driver) { /* (on the way over again) */
e->driver->ai.errand = true;
e->driver->ai.errand_there = false;
e->driver->ai.errand_to = Car_door(car, 0);
} else if (e->stage == ERRAND_LEAVING) { /* (gone, as good as) */
hide(game, car);
e->stage = ERRAND_AWAY;
e->time = 0.0f;
} else if (e->stage == ERRAND_AWAY) hide(game, car);
else if (e->stage == ERRAND_BACK) arrived(game, car);
}