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 2.29.0: soft smoke, voices
- Smoke is soft and see-through, fading at its edges - Sounds: people talking (a murmur), Alice barking and crunching, sips and bites, the toilet
entropie/Makefile | 2 +- entropie/README.md | 5 +++-- entropie/STATE.md | 13 +++++++++---- entropie/assets/shaders/scene.frag | 9 ++++++++- entropie/commit_message.txt | 17 ++++------------- entropie/src/game/sound.c | 32 ++++++++++++++++++++++++++++++++ entropie/src/people/ai.c | 14 ++++++++++++++ entropie/src/render/renderer.c | 16 ++++++++++++++++ entropie/src/render/renderer.h | 3 +++ entropie/src/render/shapes.h | 2 ++ entropie/src/render/shapes_props.c | 18 ++++++++++++++++++ entropie/src/world/chips.c | 6 +++++- entropie/src/world/chips.h | 3 ++- entropie/src/world/world.c | 5 +++++ entropie/src/world/world.h | 5 +++++ 15 files changed, 127 insertions(+), 23 deletions(-) diff --git a/entropie/Makefile b/entropie/Makefile index d8c0ee3..061dc47 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 2.28.0 +VERSION := 2.29.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/README.md b/entropie/README.md index 3caa43e..bf3dbde 100644 --- a/entropie/README.md +++ b/entropie/README.md @@ -31,7 +31,8 @@ people living there. - **Drinks:** crates of beer, water, soda and mate. Empties go back into their crates; bottles in the wrong crate get sorted out - **Sound:** steps on every ground, wind, rain, thunder, the fire, axe and - chainsaw, glass, birds by day and crickets at night (all made up on the fly) + chainsaw, glass, voices, Alice barking, birds by day and crickets at night + (all made up on the fly) - **Drink:** too much and you sway, stumble, get sick or pass out - **Alice:** the dachshund. She fetches, begs and loves being petted - **Saves:** autosave, quick save and named saves. Old saves keep working @@ -123,5 +124,5 @@ Dependencies point downward: `main → game → world/people → render → core ## Version -`make version` prints e.g. `entropie 2.28.0 (build e2f9b0f)`. The build id is +`make version` prints e.g. `entropie 2.29.0 (build 58db6f4)`. The build id is an md5 of all sources and assets. diff --git a/entropie/STATE.md b/entropie/STATE.md index a144987..540d40f 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -930,7 +930,9 @@ say no more often and don't come to you for a bit. stronger), rain (drumming and dull in your tent), thunder after the lightning, the fire crackling, axe strokes, the chainsaw, the car coming in, bottles clinking, glass breaking, somebody hitting the ground, a tree - coming down, twigs snapping, the hose, birds by day, crickets at night + coming down, twigs snapping, the hose, birds by day, crickets at night; + people talking (a murmur, deeper or higher by who it is, the words are + on screen), Alice barking and crunching, sips and bites, the toilet - Further away is quieter and duller, and comes from its side - M: sound off / on; the menus pause it @@ -1167,6 +1169,10 @@ Round 2.28: - Hoods (`look.hoodie`: jackets 1, 3, 4 of the five; `hood_up`; `figure.frag` hoodDistance, in the jacket's colour): out in the rain the jacket goes on, the hood up. Umbrellas (OBJ_UMBRELLA, two: by the house door, in the garage kitchen; one-handed): open held in the rain (`world.c` underUmbrella: held up over the head, `hands.c`), dry under it (rain_out off); NPCs fetch one when the rain starts (`ai.c` shelter: idle, a hand free, UMBRELLA_FETCH 25 m) and stay out; ai_putAway takes it home once it's dry. - Landing: the very sporty (`sporty`: fitness 0.6..0.95, sober, fresh) roll off up to LAND_BREAK + LAND_SPORT (10.8 m/s, about 6 m), the risk mostly gone. Checked: fitness 0.95 from 3, 4.5, 5.5 m: no crash in 24; 0.6: crashes from 3 m on. +Round 2.29: +- Smoke soft and see-through (`Shapes_puff`: a ball; `Renderer_drawPuff`: blended, no depth, no outline; `scene.frag` `puff`: thickest facing you, fading at the rim, SMOKE_ALPHA 0.5, thinning out at the end). Checked: the fire's smoke against the sky and the house. +- More sounds: voices (`Ai_say` -> NOISE_VOICE: a murmur of saw syllables, deep / man / woman, by the length of the line; nothing for "*...*"), Alice (Woof -> NOISE_BARK, crunch), a sip or a bite (`world.c`: the moment `drink_sipped` turns on -> NOISE_GULP), the toilet (NOISE_FLUSH). Checked: compiles, the game's sound stays clean (no NaN, no clipping); not listened to. + ## Open tasks Most important first. @@ -1190,8 +1196,7 @@ Most important first. - Clothing, still to do: more outfits for extra people (only the performance test has any: they all wear trousers) - Tents, still to do: NPCs taking theirs down on their own (leaving) - Weather, still to do: hats (nobody wears one yet) blown off; umbrellas while working (now only idle people fetch one); an umbrella blown inside out -- Sound, still to do: voices (talking is text only), Alice barking, the toilet, eating and drinking -- Smoke: drawn as pale boxes (no softness) - a softer look would be nicer +- Sound, still to do: doors and gates, the car's doors and boot, rustling bushes walking through **Saves, still to do** - Things being done come back fresh: a person's part of a job half way (its progress on the thing is kept, and their order), carrying something together @@ -1203,4 +1208,4 @@ Most important first. - Drawing many figures on a real graphics card: not measured (software GL: 300 people cost 3x the rest of the scene) --- -State of `entropie 2.28.0 (build e2f9b0f)` +State of `entropie 2.29.0 (build 58db6f4)` diff --git a/entropie/assets/shaders/scene.frag b/entropie/assets/shaders/scene.frag index 83ff6c9..aa99155 100644 --- a/entropie/assets/shaders/scene.frag +++ b/entropie/assets/shaders/scene.frag @@ -29,6 +29,7 @@ uniform vec4 holes[8]; // holes in the ground (basements): middle xz, x axis uniform vec2 holeSize[8]; // ... half sizes uniform int holeCount; uniform vec4 tint; // rgb: darkens (charred wood), a: glows like embers +uniform float puff; // > 0: smoke - soft and see-through, this thick (edges fading out) const vec3 PATH_EARTH = vec3(0.58, 0.44, 0.28); const vec3 MUD = vec3(0.31, 0.23, 0.15); @@ -149,6 +150,12 @@ void main() { } color = applyFog(color, length(viewPos - WorldPos), skyHorizon); - OutColor = vec4(color, 1.0); + float alpha = 1.0; + if (puff > 0.0) { // smoke: thickest where it faces you, fading to nothing at the rim + float facing = abs(normalize(ViewNormal).z); + alpha = puff * smoothstep(0.05, 0.85, facing); + color = mix(color, skyHorizon, 0.15); + } + OutColor = vec4(color, alpha); OutNormalDepth = vec4(normalize(ViewNormal), ViewDepth); } diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt index ff4e5ee..bf15895 100644 --- a/entropie/commit_message.txt +++ b/entropie/commit_message.txt @@ -1,14 +1,5 @@ -entropie 2.28.0: sound, sorted crates, hoods and umbrellas +entropie 2.29.0: soft smoke, voices -- Sound: steps on every ground, wind, rain, thunder, fire, axe, chainsaw, - the car, bottles, glass, falls, a tree coming down, birds and crickets - - all made up on the fly (M: off / on) -- Arrival: everybody gets a seat by the fire (people move up on the bench; - no room: they stand with the others) -- Crates know each bottle: put a bottle down next to a crate and it goes in; - right click at a crate takes out one that does not belong; the tidy sort - the crates -- Rain and smoke show against the sky; the sky clouds over with the weather -- Rain soaks the ground wherever people walk: mud on trodden paths -- Hoods go up in the rain; two umbrellas to fetch and carry -- The very sporty roll off drops of 5 m and more +- Smoke is soft and see-through, fading at its edges +- Sounds: people talking (a murmur), Alice barking and crunching, sips + and bites, the toilet diff --git a/entropie/src/game/sound.c b/entropie/src/game/sound.c index e583035..36f00b2 100644 --- a/entropie/src/game/sound.c +++ b/entropie/src/game/sound.c @@ -94,6 +94,38 @@ static void noiseOut(const Noise *n, const Ear *ear) { play(WAVE_NOISE, 0.0f, 0.0f, 260.0f, 25.0f, 0.03f, 0.7f, 2.5f, 1.0f * g, pan, 0.05f); play(WAVE_SINE, 55.0f, 35.0f, 0.0f, 0.0f, 0.01f, 0.4f, 1.5f, 0.8f * g, pan, 0.05f); break; + case NOISE_VOICE: { /* words heard, not made out: a murmur, syllable after syllable, rising and falling */ + static const float PITCH[3] = { 95.0f, 125.0f, 215.0f }; + int voice = n->surface & 3, syllables = n->surface >> 2; + float f = PITCH[voice > 2 ? 1 : voice] * (0.92f + 0.16f * dice()), at = 0.0f; + for (int k = 0; k < syllables; k++) { + float lift = 1.0f + 0.12f * sinf(1.7f * (float)k + 6.0f * dice()) - (k == syllables - 1 ? 0.1f : 0.0f); /* (and down at the end) */ + float len = 0.09f + 0.08f * dice(); + play(WAVE_SAW, f * lift, f * lift * (0.95f + 0.1f * dice()), (700.0f + 1100.0f * dice()) * dull, 150.0f, 0.02f, 0.05f, len, 0.22f * g, pan, at); + at += len + 0.03f + (dice() < 0.15f ? 0.12f : 0.0f); /* (a pause now and then) */ + } + break; + } + case NOISE_BARK: /* woof */ + for (int k = 0; k < n->surface && k < 4; k++) { + float at = 0.3f * (float)k; + play(WAVE_SAW, 420.0f + 40.0f * dice(), 250.0f, 1900.0f * dull, 220.0f, 0.004f, 0.06f, 0.16f, 0.7f * g, pan, at); + play(WAVE_NOISE, 0.0f, 0.0f, 2500.0f * dull, 400.0f, 0.003f, 0.04f, 0.1f, 0.35f * g, pan, at); + } + break; + case NOISE_GULP: + if (n->surface == 1) /* a bite: crunch */ + for (int k = 0; k < 3; k++) play(WAVE_CRACKLE, 900.0f, 0.0f, 4500.0f * dull, 500.0f, 0.003f, 0.04f, 0.1f, 0.5f * g, pan, 0.13f * (float)k); + else { /* a swallow */ + play(WAVE_SINE, 320.0f, 150.0f, 0.0f, 0.0f, 0.01f, 0.05f, 0.12f, 0.35f * g, pan, 0.0f); + play(WAVE_NOISE, 0.0f, 0.0f, 900.0f * dull, 100.0f, 0.01f, 0.06f, 0.15f, 0.2f * g, pan, 0.02f); + } + break; + case NOISE_FLUSH: /* the cistern: a rush, then gurgling down */ + play(WAVE_NOISE, 0.0f, 0.0f, 2600.0f * dull, 300.0f, 0.25f, 1.0f, 3.0f, 0.6f * g, pan, 0.0f); + play(WAVE_NOISE, 0.0f, 0.0f, 500.0f * dull, 60.0f, 0.4f, 1.4f, 3.5f, 0.5f * g, pan, 0.3f); + play(WAVE_CRACKLE, 60.0f, 0.0f, 1200.0f * dull, 100.0f, 0.5f, 1.2f, 3.0f, 0.3f * g, pan, 0.6f); + break; case NOISE_SNAP: /* a dry branch underfoot */ play(WAVE_CRACKLE, 1500.0f, 0.0f, 7000.0f * dull, 900.0f, 0.001f, 0.03f, 0.08f, 0.8f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 2500.0f * dull, 400.0f, 0.001f, 0.02f, 0.06f, 0.5f * g, pan, 0.0f); diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c index abd9ad2..623edb0 100644 --- a/entropie/src/people/ai.c +++ b/entropie/src/people/ai.c @@ -1266,6 +1266,20 @@ void Ai_say(Object *self, const char *text) { Ai *ai = &self->ai; snprintf(ai->speech, sizeof(ai->speech), "%s", text); ai->speech_time = 2.0f + 0.06f * (float)strlen(ai->speech); + if (!g_world) return; + Vec3 head = vec3_add(self->position, vec3(0.0f, self->look.height * (self->type == OBJ_DOG ? 0.25f : 0.9f), 0.0f)); + if (self->type == OBJ_DOG) { /* (her woofs, and a crunch) */ + int woofs = 0; + for (const char *s = text; (s = strstr(s, "Woof")) != NULL; s += 4) woofs++; + if (woofs) World_noiseOn(g_world, NOISE_BARK, head, 0.9f, woofs); + else if (strstr(text, "crunch")) World_noiseOn(g_world, NOISE_GULP, head, 0.5f, 1); + return; + } + if (text[0] == '*') return; /* (a sound written out, not words) */ + int syllables = (int)strlen(text) / 5; + syllables = syllables < 2 ? 2 : syllables > 10 ? 10 : syllables; + int voice = self->female ? VOICE_WOMAN : self->look.height > 1.82f || self->weight > 85.0f ? VOICE_DEEP : VOICE_MAN; + World_noiseOn(g_world, NOISE_VOICE, head, strchr(text, '!') ? 0.9f : 0.6f, voice | (syllables << 2)); } void Ai_order(Object *self, Order order, Vec3 area) { diff --git a/entropie/src/render/renderer.c b/entropie/src/render/renderer.c index cdae0af..e8a86e4 100644 --- a/entropie/src/render/renderer.c +++ b/entropie/src/render/renderer.c @@ -36,6 +36,7 @@ static bool loadShaders(Renderer *r) { s->shadow_strength = glGetUniformLocation(s->program, "shadowStrength"); s->fire_light = glGetUniformLocation(s->program, "fireLight"); s->tint = glGetUniformLocation(s->program, "tint"); + s->puff = glGetUniformLocation(s->program, "puff"); s->grass = glGetUniformLocation(s->program, "grass"); s->rain_wet = glGetUniformLocation(s->program, "rainWet"); s->shadow_map = glGetUniformLocation(s->program, "shadowMap"); @@ -350,6 +351,7 @@ void Renderer_beginScenePass(Renderer *r, const Camera *camera, const Lighting * glUniform1f(s->decal_size, 0.0f); glUniform4f(s->tint, 1.0f, 1.0f, 1.0f, 0.0f); glUniform1f(s->grass, 0.0f); + glUniform1f(s->puff, 0.0f); glUniform1i(s->wear_map, UNIT_WEAR); glUniform1i(s->water_map, UNIT_WATER); glUniform1i(s->surface_map, UNIT_SURFACE); @@ -409,6 +411,20 @@ void Renderer_drawSmoke(Renderer *r, const Mesh *mesh, Mat4 model, Vec3 rgb) { glColorMaski(1, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } +void Renderer_drawPuff(Renderer *r, const Mesh *mesh, Mat4 model, Vec3 rgb, float alpha) { + if (!meshInView(r, mesh, model) || alpha <= 0.01f) return; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMaski(1, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); /* (no outline) */ + glDepthMask(GL_FALSE); + glUniform1f(r->scene.puff, alpha); + Renderer_drawMeshTinted(r, mesh, model, rgb, 0.0f); + glUniform1f(r->scene.puff, 0.0f); + glDepthMask(GL_TRUE); + glColorMaski(1, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDisable(GL_BLEND); +} + void Renderer_drawGrass(Renderer *r, const Mesh *mesh, float terrain_size, float grass_depth) { if (!meshInView(r, mesh, mat4_identity())) return; glUniform1f(r->scene.grass, terrain_size); diff --git a/entropie/src/render/renderer.h b/entropie/src/render/renderer.h index ac9b9eb..5f62c53 100644 --- a/entropie/src/render/renderer.h +++ b/entropie/src/render/renderer.h @@ -49,6 +49,7 @@ typedef struct { GLint terrain_size, grass_depth, wear_map, water_map, surface_map; /* terrain only */ GLint decal_size; /* things lying on the ground: sink with it */ GLint tint; /* color multiplier and ember glow */ + GLint puff; /* > 0: soft and see-through (smoke), this thick */ GLint grass; /* grass blades: terrain size while drawing them */ GLint rain_wet; /* everything wet from rain (0..1) */ GLint holes, hole_size, hole_count; /* holes in the ground (basements): not drawn there */ @@ -138,6 +139,8 @@ void Renderer_beginAirPass(Renderer *r); /* Like Renderer_drawMesh, darkened by rgb and glowing like embers by glow (0..1). */ void Renderer_drawMeshTinted(Renderer *r, const Mesh *mesh, Mat4 model, Vec3 rgb, float glow); /* Like Renderer_drawMeshTinted, but no ink outline round it and nothing hides behind it (smoke). */ +/* Smoke: a soft, see-through ball (edges fading out), this thick (0..1). */ +void Renderer_drawPuff(Renderer *r, const Mesh *mesh, Mat4 model, Vec3 rgb, float alpha); void Renderer_drawSmoke(Renderer *r, const Mesh *mesh, Mat4 model, Vec3 rgb); /* Grass blades (see world/meadow.h): trampled with the terrain, swaying. */ void Renderer_drawGrass(Renderer *r, const Mesh *mesh, float terrain_size, float grass_depth); diff --git a/entropie/src/render/shapes.h b/entropie/src/render/shapes.h index e355756..f2cb342 100644 --- a/entropie/src/render/shapes.h +++ b/entropie/src/render/shapes.h @@ -23,6 +23,8 @@ typedef enum { Mesh Shapes_box(Vec3 size, Palette palette); /* A white unit block (tinted when drawn): wood chips, sawdust. */ Mesh Shapes_chip(void); +/* A ball of smoke (1 across, white: tinted), drawn soft and see-through (Renderer_drawPuff). */ +Mesh Shapes_puff(void); /* Upright cylinder centered on the origin (single palette color). */ Mesh Shapes_cylinder(float diameter, float height, int segments, Palette palette); diff --git a/entropie/src/render/shapes_props.c b/entropie/src/render/shapes_props.c index 03bea0a..6ff81e7 100644 --- a/entropie/src/render/shapes_props.c +++ b/entropie/src/render/shapes_props.c @@ -661,6 +661,24 @@ Mesh Shapes_chip(void) { return Mesh_fromBuilder(&b); } +Mesh Shapes_puff(void) { + MeshBuilder b; + MeshBuilder_init(&b); + const int around = 10, up = 7; + GLuint ring[8][11]; + for (int j = 0; j <= up; j++) { + float v = PI_F * (float)j / (float)up; + for (int i = 0; i <= around; i++) { + float u = 2.0f * PI_F * (float)i / (float)around; + Vec3 n = vec3(sinf(v) * cosf(u), cosf(v), sinf(v) * sinf(u)); + ring[j][i] = MeshBuilder_vertex(&b, vec3_scale(n, 0.5f), n, vec3(1.0f, 1.0f, 1.0f)); + } + } + for (int j = 0; j < up; j++) + for (int i = 0; i < around; i++) MeshBuilder_quad(&b, ring[j][i], ring[j][i + 1], ring[j + 1][i + 1], ring[j + 1][i]); + return Mesh_fromBuilder(&b); +} + Mesh Shapes_bag(float fullness) { const Vec3 size = Shapes_bagSize(fullness); const float r = size.x * 0.5f, h = size.y; diff --git a/entropie/src/world/chips.c b/entropie/src/world/chips.c index 19b87f4..82ad2c6 100644 --- a/entropie/src/world/chips.c +++ b/entropie/src/world/chips.c @@ -21,10 +21,12 @@ void Chips_init(Chips *c) { memset(c, 0, sizeof(*c)); c->rng = 0x51f15eedu; c->mesh = Shapes_chip(); + c->puff = Shapes_puff(); } void Chips_destroy(Chips *c) { Mesh_destroy(&c->mesh); + Mesh_destroy(&c->puff); memset(c, 0, sizeof(*c)); } @@ -116,6 +118,8 @@ void Chips_update(Chips *c, const Terrain *terrain, Vec3 wind, float dt) { } } +#define SMOKE_ALPHA 0.5f /* how thick a puff of smoke is (see-through) */ + void Chips_draw(const Chips *c, Renderer *renderer) { for (int i = 0; i < CHIP_MAX; i++) { const Chip *ch = &c->chips[i]; @@ -124,7 +128,7 @@ void Chips_draw(const Chips *c, Renderer *renderer) { float s = ch->size * (ch->smoke && ch->size > 0.05f ? 1.0f : shrink); /* (a fire's smoke: thinning out instead) */ Mat4 m = mat4_mul(mat4_translate(ch->p), mat4_mul(mat4_rotate_yxz(vec3(ch->down ? 0.0f : ch->angle, ch->angle * 0.7f, 0.0f)), mat4_scale(vec3(s, s * (ch->dust || ch->smoke ? 1.0f : 0.35f), s * (ch->dust || ch->smoke ? 1.0f : 0.7f))))); - if (ch->smoke) Renderer_drawSmoke(renderer, &c->mesh, m, ch->color); + if (ch->smoke) Renderer_drawPuff(renderer, &c->puff, m, ch->color, SMOKE_ALPHA * fminf(1.0f, ch->life / 0.8f)); /* (thinning out at the end) */ else Renderer_drawMeshTinted(renderer, &c->mesh, m, ch->color, 0.0f); } } diff --git a/entropie/src/world/chips.h b/entropie/src/world/chips.h index 1800e7c..a4c2a46 100644 --- a/entropie/src/world/chips.h +++ b/entropie/src/world/chips.h @@ -31,7 +31,8 @@ typedef struct { Chip chips[CHIP_MAX]; int next; unsigned rng; - Mesh mesh; /* a small white block (tinted per chip) */ + Mesh mesh; /* a small white block (tinted per chip) ... */ + Mesh puff; /* ... and a ball for smoke */ } Chips; void Chips_init(Chips *c); diff --git a/entropie/src/world/world.c b/entropie/src/world/world.c index 437e0e6..0e7ef2c 100644 --- a/entropie/src/world/world.c +++ b/entropie/src/world/world.c @@ -298,6 +298,7 @@ static void spawnPee(World *world, Object *o) { o->rotation.y = atan2f(front.z, front.x) + PI_F / 2.0f; Object_syncColliders(o); o->toilet->dog.mode = 0; + World_noise(world, NOISE_FLUSH, o->toilet->position, 0.8f); o->toilet = NULL; return; } @@ -949,7 +950,11 @@ static void animateCharacter(World *world, Object *o, float dt) { if (o->climbing || o->on_ladder || o->is_held) o->air_fall = 0.0f; else if (!o->is_grounded) o->air_fall = fmaxf(o->air_fall, -o->velocity.y); else if (o->air_fall > 0.0f) landFromHigh(world, o); + bool sipped = o->hands.drink_hand >= 0 && o->hands.drink_sipped; /* (a sip or a bite just now: heard) */ + const Object *drinking = o->hands.drink_hand >= 0 ? o->hands.item[o->hands.drink_hand] : NULL; Hands_update(o, dt); + if (drinking && !sipped && o->hands.drink_sipped) + World_noiseOn(world, NOISE_GULP, vec3_add(o->position, vec3(0.0f, o->look.height * 0.9f, 0.0f)), 0.5f, Object_isFood(drinking) ? 1 : 0); Tools_update(o, dt); Ladder_poseHands(o, dt); /* on a ladder: hands on the rungs */ bool settling = Object_settleOnSeat(o, dt); /* over to the seat first */ diff --git a/entropie/src/world/world.h b/entropie/src/world/world.h index 5f0cbef..3efb320 100644 --- a/entropie/src/world/world.h +++ b/entropie/src/world/world.h @@ -23,9 +23,14 @@ typedef enum { NOISE_THUD, /* somebody (or something heavy) hitting the ground */ NOISE_CRASH, /* a tree coming down */ NOISE_SNAP, /* a dry branch breaking underfoot */ + NOISE_VOICE, /* somebody saying something (surface: VOICE_* | syllables << 2) */ + NOISE_BARK, /* Alice (surface: how many woofs) */ + NOISE_GULP, /* a sip (surface 0), a bite (1) */ + NOISE_FLUSH, /* the toilet */ NOISE_COUNT } NoiseKind; enum { STEP_GRASS, STEP_GRAVEL, STEP_HARD, STEP_EARTH, STEP_MUD, STEP_WOOD, STEP_WATER }; +enum { VOICE_DEEP, VOICE_MAN, VOICE_WOMAN }; typedef struct { NoiseKind kind; Vec3 at; float loud; int surface; } Noise; #define WORLD_NOISES 96