Nimbin[12]?Games / Entropie / entropie/src/people/ai_touch.c

Entropie git · main

Entropie festival game; first steps

sdl c game entropie cmake · first commit 2026-10-10 · last commit 2026-11-02 (18 hours ago) · synced 10 hours ago

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.gz
entropie/src/people/ai_touch.c 5.3 KB · 106 lines raw
/*
 * People and people (people/touch.h): walking along hand in hand with whoever
 * leads them (they hold your hand: Object.led_by); stepping in when somebody
 * bothers somebody (game_touch.c sends them: Ai.step_in); and, at the
 * festival, the odd drunk guest who gets too close to somebody dancing.
 */

#include <math.h>

#include "people/ai_internal.h"
#include "people/touch.h"

#define LED_SIDE     0.5f   /* m beside the one leading them */
#define LED_LOST     3.0f   /* m apart: the hand's gone */
#define STEP_REACH   1.3f   /* m: near enough to say it */
#define STEP_GIVE_UP 45.0f  /* real s on the way: they've gone */
#define HARASS_REACH 0.95f  /* m: (people keep a step apart walking up: an arm reaches) */
#define HARASS_AGAIN (2.5f * 3600.0f) /* game s before a told-off one tries it again */

/* Hand in hand: beside them, the hand on theirs, the way they go. */
static bool led(Object *self, float dt) {
    Object *by = self->led_by;
    if (!by) return false;
    int hand = Hands_holding(by, self);
    if (hand < 0 || Body_isBusy(&self->body) || flatDistance(by->position, self->position) > LED_LOST) { self->led_by = NULL; return false; }
    Vec3 f = Object_facing(by), right = vec3(-f.z, 0.0f, f.x);
    float side = hand == HAND_LEFT ? -1.0f : 1.0f; /* (their left hand holds mine: I'm on their left) */
    Vec3 spot = vec3_add(by->position, vec3_scale(right, side * LED_SIDE));
    self->sitting = false;
    Vec3 gap = vec3_sub(spot, self->position);
    gap.y = 0.0f;
    float d = vec3_length(gap), pace = hypotf(by->velocity.x, by->velocity.z);
    if (d > 0.12f || pace > 0.3f) { /* (straight along with them: no way round to find, they're right there) */
        Vec3 dir = d > 0.12f ? vec3_scale(gap, 1.0f / d) : f;
        if (d > 0.12f && pace > 0.3f) dir = vec3_normalize(vec3_add(vec3_scale(f, pace), vec3_scale(dir, 2.0f * d))); /* (on, and back beside them) */
        ai_walk(self, dir, fminf(runSpeed(self), pace + 2.0f * d), dt);
    } else { stand(self); turnTowards(self, f, dt); }
    int joint = hand == HAND_LEFT ? JOINT_HAND_L : JOINT_HAND_R;
    Hands_touch(self, Figure_toWorld(by->position, f, by->skeleton.joints[joint]));
    return true;
}

/* Off to tell somebody to stop. */
static bool stepIn(Object *self, Object *const *objects, int count, float dt) {
    Ai *ai = &self->ai;
    Object *who = ai->step_in;
    if (!who) return false;
    if ((ai->step_t += dt) > STEP_GIVE_UP || who->indoors > 0.0f || who->gone_home) { ai->step_in = ai->step_for = NULL; return false; }
    self->sitting = false;
    if (!ai_goToPoint(self, who->position, STEP_REACH, objects, count, dt)) return true;
    stand(self);
    turnTowards(self, vec3_sub(who->position, self->position), dt);
    static const char *const SAY[] = { "Hey! Leave them alone - that's not okay.", "Stop that. Now. Nobody wants that here.",
                                       "Back off - you heard them. Not here, not anywhere." };
    Ai_say(self, SAY[(int)(random01(ai) * 3.0f) % 3]);
    if (who->type == OBJ_HUMAN) { /* (one of us: they back off) */
        who->ai.harass = NULL;
        who->harass_t = HARASS_AGAIN;
        if (who->ai.speech_time <= 0.0f) Ai_say(who, random01(ai) < 0.5f ? "Alright, alright... sorry." : "Whoa - okay. I'm going.");
        who->ai.mode = AI_WANDER;
    }
    if (ai->step_for && ai->step_for->type == OBJ_HUMAN && ai->step_for->ai.speech_time <= 0.0f) Ai_say(ai->step_for, "Thank you.");
    if (ai->step_for && ai->step_for->guest) ai->step_for->fun = fminf(1.0f, ai->step_for->fun + 0.12f); /* (somebody had her back) */
    ai->step_in = ai->step_for = NULL;
    return true;
}

/* The festival: a drunk guest after somebody dancing. */
static bool bother(Object *self, Object *const *objects, int count, float dt) {
    Ai *ai = &self->ai;
    self->harass_t = fmaxf(0.0f, self->harass_t - (g_world ? g_world->game_dt : dt));
    Object *who = ai->harass;
    if (!who) return false;
    if ((ai->harass_walk += dt) > 60.0f || who->indoors > 0.0f || who->gone_home || Body_isDown(&who->body)) { ai->harass = NULL; return false; }
    self->sitting = false;
    self->body.dance_goal = 0.0f;
    if (!ai_goToPoint(self, who->position, HARASS_REACH, objects, count, dt)) return true;
    Vec3 f = Object_facing(who);
    Vec3 at = vec3_add(who->position, vec3_add(vec3_scale(f, -0.12f), vec3(0.0f, 0.85f * who->look.height / 1.75f, 0.0f))); /* (from behind: the bottom) */
    Hands_touch(self, at);
    Touch_react(g_world, who, self, TOUCH_BUTT);
    who->ai.fest_floor = 0; /* (she's off out of it) */
    who->ai.fest_time = -120.0f;
    self->harass_t = HARASS_AGAIN;
    ai->harass = NULL;
    return true;
}

/* You've got hold of them (and they let you): they stay, and look at you. */
static bool held(Object *self, Object *const *objects, int count, float dt) {
    if (self->led_by) return false;
    Object *you = findPlayer(objects, count);
    if (!you || Hands_holding(you, self) < 0) return false;
    if (self->ai.dodge_t > 0.0f) return false; /* (pulled closer: let them come) */
    self->sitting = false;
    stand(self);
    turnTowards(self, vec3_sub(you->position, self->position), dt);
    return true;
}

bool ai_touch(Object *self, Object *const *objects, int count, float dt) {
    if (stepIn(self, objects, count, dt)) return true;
    if (bother(self, objects, count, dt)) return true;
    if (held(self, objects, count, dt)) return true;
    return led(self, dt);
}