/* * 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 #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); }