#include "people/touch.h" #include "render/figure.h" #include #include static const char *const NAMES[TOUCH_PARTS] = { "hand", "arm", "shoulder", "back", "head", "waist", "leg", "chest", "bottom", "crotch" }; const char *Touch_partName(int part) { return NAMES[part < 0 || part >= TOUCH_PARTS ? 0 : part]; } bool Touch_intimate(int part) { return part == TOUCH_CHEST || part == TOUCH_BUTT || part == TOUCH_GROIN || part == TOUCH_LEG; } int Touch_part(const Object *whom, int joint, Vec3 hand) { Vec3 local = Figure_toLocal(whom->position, Object_facing(whom), hand); /* (z: in front of them, x: their right) */ switch (joint) { case JOINT_HAND_L: case JOINT_HAND_R: case JOINT_WRIST_L: case JOINT_WRIST_R: return TOUCH_HAND; case JOINT_ELBOW_L: case JOINT_ELBOW_R: return TOUCH_ARM; case JOINT_SHOULDER_L: case JOINT_SHOULDER_R: return TOUCH_SHOULDER; case JOINT_NECK: case JOINT_HEAD: return TOUCH_HEAD; case JOINT_CHEST: return local.z > 0.04f ? TOUCH_CHEST : local.z < -0.04f ? TOUCH_BACK : TOUCH_WAIST; case JOINT_PELVIS: case JOINT_HIP_L: case JOINT_HIP_R: if (local.z > 0.06f && fabsf(local.x) < 0.12f) return TOUCH_GROIN; if (local.z < -0.04f) return TOUCH_BUTT; return TOUCH_WAIST; default: return TOUCH_LEG; /* (a knee, an ankle) */ } } /* Two who belong together (they share a tent). */ static bool couple(const Object *a, const Object *b) { return (!strcmp(a->name, "Volkram") && !strcmp(b->name, "Jana")) || (!strcmp(a->name, "Jana") && !strcmp(b->name, "Volkram")); } void Touch_feelings(const Object *whom, const Object *by, float *like, float *love) { if (by->type == OBJ_PLAYER) { *like = whom->like; *love = whom->love; return; } if (couple(whom, by)) { *like = 0.9f; *love = 0.9f; return; } bool known = whom->cast_id > 0 && by->cast_id > 0; /* (the crew know each other) */ *like = known ? 0.55f : 0.25f; *love = 0.0f; } /* How much of each it takes for a touch there to be welcome (and its being just too much). */ static void needs(int part, float *like, float *love) { *like = 2.0f; *love = 2.0f; /* (out of reach) */ switch (part) { case TOUCH_ARM: case TOUCH_SHOULDER: case TOUCH_BACK: *like = 0.25f; break; case TOUCH_HAND: *like = 0.5f; *love = 0.35f; break; case TOUCH_HEAD: *like = 0.6f; *love = 0.45f; break; case TOUCH_WAIST: *like = 0.85f; *love = 0.5f; break; case TOUCH_LEG: *love = 0.6f; break; default: *love = 0.8f; break; /* (the chest, the bottom, between the legs) */ } } static void note(World *world, Object *by, Object *whom, int what, int part) { if (world->touched_n >= 8) return; world->touched[world->touched_n].by = by; world->touched[world->touched_n].whom = whom; world->touched[world->touched_n].what = what; world->touched[world->touched_n].part = part; world->touched_n++; } static void sayIt(Object *whom, const char *line) { if (whom->type == OBJ_HUMAN) Ai_say(whom, line); } int Touch_react(World *world, Object *whom, Object *by, int part) { float like, love, need_like, need_love; Touch_feelings(whom, by, &like, &love); needs(part, &need_like, &need_love); float mood = -0.15f * whom->ai.annoyed - 0.1f * fmaxf(whom->temperament.nerves, 0.0f) + 0.1f * fminf(whom->drunk, 1.0f) * (love > 0.2f ? 1.0f : 0.0f); float margin = fmaxf(like + mood - need_like, love + mood - need_love); /* (either will do) */ bool you = by->type == OBJ_PLAYER; int what; if (margin >= 0.0f) { what = TOUCHED_WELCOME; if (you) { /* closeness that's welcome brings you closer */ whom->like = fminf(1.0f, whom->like + 0.01f); if (part == TOUCH_HAND || part == TOUCH_WAIST || Touch_intimate(part)) whom->love = fminf(1.0f, whom->love + 0.025f); } static const char *const OK[TOUCH_PARTS] = { "Hey, you.", "Mm?", "What's up?", "Hm?", "Hey...", "Come here, you.", "Oh - hi.", "Mm...", "Cheeky.", "Not here - later." }; if (whom->ai.speech_time <= 0.0f) sayIt(whom, OK[part]); } else if (margin > -0.15f && !Touch_intimate(part) && part != TOUCH_WAIST) { what = TOUCHED_TOLERATED; if (you) whom->like = fmaxf(0.0f, whom->like - 0.01f); sayIt(whom, part == TOUCH_HAND ? "Uh... okay?" : "Can I help you?"); } else if (!Touch_intimate(part)) { what = TOUCHED_UNWELCOME; if (you) whom->like = fmaxf(0.0f, whom->like - 0.06f); whom->ai.annoyed = fminf(1.0f, whom->ai.annoyed + 0.4f); sayIt(whom, "Don't touch me, please."); } else { what = TOUCHED_HARASSED; if (you) { whom->like = fmaxf(0.0f, whom->like - 0.3f); whom->love = fmaxf(0.0f, whom->love - 0.4f); } whom->ai.annoyed = 1.0f; whom->commitment = fmaxf(0.0f, whom->commitment - 0.1f); if (whom->guest) whom->fun = fmaxf(0.0f, whom->fun - 0.2f); static const char *const NO[] = { "Hey! Get your hands off me!", "What the hell?! Don't touch me!", "Back off! Seriously!" }; sayIt(whom, NO[(int)(fabsf(whom->position.x * 7.0f + whom->position.z)) % 3]); } note(world, by, whom, what, part); return what; } bool Touch_calm(World *world, Object *whom, Object *by, int part) { float like, love; Touch_feelings(whom, by, &like, &love); if (Touch_intimate(part) || like < 0.3f) return Touch_react(world, whom, by, part) == TOUCHED_WELCOME; whom->ai.annoyed = fmaxf(0.0f, whom->ai.annoyed - 0.5f); whom->temperament.nerves = fmaxf(-0.5f, whom->temperament.nerves - 0.05f); if (by->type == OBJ_PLAYER) whom->like = fminf(1.0f, whom->like + 0.03f); if (whom->guest) whom->fun = fminf(1.0f, whom->fun + 0.03f); sayIt(whom, like > 0.6f ? "Thanks. I needed that." : "...Thanks. Better."); note(world, by, whom, TOUCHED_WELCOME, -1); /* (a kind act: seen) */ return true; }