M1b: survival stats, day/night, fire fuel, cooking, foraging, torches, death + grave

- Health, stamina, hunger, warmth and wet with HUD bars, status tags and clock
- 20-minute day/night cycle with moon, stars and lit clouds
- Campfire fuel (2 min start, 15 min max), heat radius, cooking needs a lit fire
- Berry bushes (regrow 10 min), mushrooms, roasted mushroom, torch
- Stamina costs for sprint, jump and harvest hits
- Death respawns at island start and leaves a grave bag
- Saves include stats, time of day, fire fuel, regrowth timers and graves
- New test_survival_stats suite; tests fail if a script error aborts them
This commit is contained in:
Paul Bailey
2026-11-04 01:19:41 +00:00
parent 2b1afc86ba
commit fc7eb3cfed
28 changed files with 1326 additions and 97 deletions

View File

@@ -1,16 +1,25 @@
shader_type sky;
// Gradient sky + sun disc/halo + a static cloud layer projected onto a dome.
// Deliberately no TIME: a time-varying sky forces Godot to rebuild the ambient
// radiance map every frame.
// Gradient sky + sun disc/halo + a static cloud layer projected onto a dome,
// plus stars at night. Colours and energy are driven by day_night.gd, a few
// times a second rather than every frame. Deliberately no TIME: a time-varying
// sky forces Godot to rebuild the ambient radiance map every frame.
uniform vec3 zenith_col : source_color = vec3(0.24, 0.44, 0.72);
uniform vec3 horizon_col : source_color = vec3(0.72, 0.79, 0.85);
uniform vec3 ground_col : source_color = vec3(0.30, 0.33, 0.32);
uniform vec3 sun_tint : source_color = vec3(1.0, 0.86, 0.62);
uniform vec3 cloud_lit : source_color = vec3(1.0, 1.0, 1.0);
uniform sampler2D cloud_tex : filter_linear_mipmap, repeat_enable;
uniform float cloud_cover = 0.5;
uniform float energy = 1.0;
uniform float star_strength = 0.0;
float hash(vec3 p) {
p = fract(p * 0.3183099 + 0.1);
p *= 17.0;
return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
}
void sky() {
vec3 d = EYEDIR;
@@ -23,19 +32,28 @@ void sky() {
sd = max(dot(d, LIGHT0_DIRECTION), 0.0);
col += sun_tint * pow(sd, 6.0) * 0.35 * (1.0 - smoothstep(0.0, 0.5, h));
col += sun_tint * pow(sd, 64.0) * 0.6;
col += LIGHT0_COLOR * smoothstep(0.9994, 0.9997, sd) * 20.0;
col += LIGHT0_COLOR * smoothstep(0.9994, 0.9997, sd) * 20.0 * LIGHT0_ENERGY;
}
float cov = 0.0;
if (h > 0.0) {
vec2 uv = d.xz / (h + 0.12);
float c = texture(cloud_tex, uv * 0.08).r * 0.65 + texture(cloud_tex, uv * 0.23 + 0.31).r * 0.35;
float cov = smoothstep(1.0 - cloud_cover, 1.0 - cloud_cover + 0.22, c);
cov = smoothstep(1.0 - cloud_cover, 1.0 - cloud_cover + 0.22, c);
cov *= smoothstep(0.0, 0.18, h);
float shade = smoothstep(0.35, 1.0, c);
vec3 cloud = mix(vec3(1.0), vec3(0.62, 0.66, 0.72), shade * 0.8);
vec3 cloud = mix(cloud_lit, cloud_lit * vec3(0.62, 0.66, 0.72), shade * 0.8);
cloud += sun_tint * pow(sd, 4.0) * 0.5;
col = mix(col, cloud, cov * 0.9);
}
col *= energy;
COLOR = col * energy;
// Stars: a sparse hash over direction cells, hidden by clouds and haze.
if (star_strength > 0.0 && h > 0.0) {
float s = hash(floor(d * 260.0));
float star = step(0.9972, s) * (0.5 + 0.5 * fract(s * 91.7));
col += vec3(0.9, 0.93, 1.0) * star * star_strength * smoothstep(0.02, 0.25, h) * (1.0 - cov) * 2.0;
}
COLOR = col;
}