Nimbin[12]?Games / Entropie / entropie/src/people/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/touch.c 5.8 KB · 115 lines raw
#include "people/touch.h"
#include "render/figure.h"

#include <math.h>
#include <string.h>

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;
}