Add survival pressure death QA gate

This commit is contained in:
2026-05-19 14:02:55 -07:00
parent 4370924386
commit 428918bc75
5 changed files with 111 additions and 3 deletions
+1 -1
View File
@@ -861,7 +861,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
- [x] Can craft a fire. Added a craft-fire QA gate tied to `DA_Recipe_Campfire`, the player recipe setup, `BP_Campfire`, replicated campfire lit/fuel state, fire interaction prompts, campfire persistence, and fire-risk QA coverage.
- [x] Can craft a shelter. Added a craft-shelter QA gate tied to primitive frame/wall/roof/shelter recipes, native build placement, `BP_PrimitiveShelter`, shelter persistence/protection hooks, and the natural shelter playable-loop smoke test.
- [x] Can survive one full day/night cycle. Added a full day/night survival QA gate tied to the `4 real hours = 1 in-game day` calendar, replicated world time and solar phase, authoritative hunger/thirst/stamina/body-temperature/health pressure, fire and shelter mitigation, critical survival HUD visibility, and save/load persistence coverage before investor demos treat the gate as play-proven.
- [ ] Can die from survival pressure.
- [x] Can die from survival pressure. Added a survival-pressure death QA gate requiring starvation, dehydration, cold exposure, sickness, and bleeding to reduce health on server authority, trigger `UpdateDeathState`, replicate `bIsDead` and `LastDeathReason`, show death/respawn UI feedback, support server respawn, and remain covered by player stat persistence.
- [ ] Can reconnect and retain state.
- [ ] Can restart server and retain placed shelter.
- [ ] No critical log spam during 30-minute test.
+25
View File
@@ -154,3 +154,28 @@ Required evidence:
This gate is gameplay/server-relevant and should be covered by the next server
package deployment.
## Survival Pressure Death
The survival-pressure death gate proves the MVP does not merely display hunger,
thirst, exposure, illness, or injury as cosmetic stats. Sustained pressure must
be able to reduce health, mark the character dead, replicate the death state,
and expose a clear respawn path.
Required evidence:
- Starvation, dehydration, cold exposure, sickness, and bleeding can reduce
`Survival.Health` on server authority.
- `ClampSurvival` calls `UpdateDeathState`, clamps health to zero, clears
stamina, sets `bIsDead`, and records `LastDeathReason`.
- The replicated survival snapshot includes the death state and last death
reason.
- The critical survival HUD and death/respawn panel display the dead state and
cause.
- The player controller exposes the MVP respawn path and revives the survival
component on the server.
- Death/respawn behavior remains persistence-safe through the player stat
save/load path.
This gate is gameplay/server-relevant and should be covered by the next server
package deployment.
+1 -1
View File
@@ -33,7 +33,7 @@ EXPECTED = {
"AgrarianGameCharacter.cpp": [
"SurvivalComponent->Survival.Exhaustion < 85.0f",
"const float ExhaustionMultiplier = FMath::GetMappedRangeValueClamped",
"HungerMultiplier * ThirstMultiplier * InjuryMultiplier * ExhaustionMultiplier",
"HungerMultiplier * ThirstMultiplier * InjuryMultiplier * SprainMultiplier * SicknessMultiplier * ExhaustionMultiplier",
],
"AgrarianDebugHUD.cpp": [
"Exhaust:",
+1 -1
View File
@@ -31,7 +31,7 @@ EXPECTED = {
"AgrarianGameCharacter.cpp": [
"const float SicknessMultiplier = FMath::GetMappedRangeValueClamped",
"Survival.SicknessSeverity",
"InjuryMultiplier * SicknessMultiplier * ExhaustionMultiplier",
"InjuryMultiplier * SprainMultiplier * SicknessMultiplier * ExhaustionMultiplier",
],
"AgrarianDebugHUD.cpp": ["Sick:", "Survival.SicknessSeverity"],
"AgrarianGamePlayerController.cpp": [
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Verify the MVP survival-pressure death QA gate is covered."""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
QA_DOC = ROOT / "Docs" / "QA" / "MvpQaGates.md"
TYPES_H = ROOT / "Source" / "AgrarianGame" / "AgrarianTypes.h"
SURVIVAL_H = ROOT / "Source" / "AgrarianGame" / "AgrarianSurvivalComponent.h"
SURVIVAL_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianSurvivalComponent.cpp"
HUD_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianDebugHUD.cpp"
CONTROLLER_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.cpp"
DEATH_VERIFY = ROOT / "Scripts" / "verify_death_state.py"
REPLICATED_DEATH_VERIFY = ROOT / "Scripts" / "verify_replicated_death_feedback.py"
RESPAWN_VERIFY = ROOT / "Scripts" / "verify_mvp_death_respawn_ui.py"
STAT_SAVE_VERIFY = ROOT / "Scripts" / "verify_stat_save_load_support.py"
REQUIRED = {
QA_DOC: [
"## Survival Pressure Death",
"Starvation, dehydration, cold exposure, sickness, and bleeding",
"UpdateDeathState",
"LastDeathReason",
"death/respawn panel",
],
TYPES_H: [
"bool bIsDead = false;",
"FName LastDeathReason = NAME_None;",
],
SURVIVAL_H: [
"float StarvationDamagePerMinute = 3.0f;",
"float DehydrationDamagePerMinute = 5.0f;",
"float ColdDamagePerMinute = 4.0f;",
"float SicknessDamagePerMinute = 1.5f;",
"float BleedingDamagePerMinute = 2.0f;",
"void UpdateDeathState();",
],
SURVIVAL_CPP: [
"Survival.Health -= StarvationDamagePerMinute * Minutes;",
"Survival.Health -= DehydrationDamagePerMinute * Minutes;",
"Survival.Health -= ColdDamagePerMinute * Minutes",
"Survival.Health -= SicknessDamagePerMinute * (Survival.SicknessSeverity / 100.0f) * Minutes;",
"Survival.Health -= BleedingDamagePerMinute * BleedingRatio * Minutes;",
"UpdateDeathState();",
"Survival.bIsDead = true;",
"Survival.LastDeathReason = FName(TEXT(\"health_depleted\"));",
"OnDeathStateChanged.Broadcast(Survival.bIsDead, Survival.LastDeathReason);",
],
HUD_CPP: [
"YOU DID NOT SURVIVE",
"Cause: %s",
"State DEAD",
],
CONTROLLER_CPP: [
"ServerAgrarianRespawn_Implementation",
"SurvivalComponent->Revive",
],
DEATH_VERIFY: ["PASS: death state is present."],
REPLICATED_DEATH_VERIFY: ["replicated death feedback"],
RESPAWN_VERIFY: ["MVP death/respawn UI verification"],
STAT_SAVE_VERIFY: ["SavedPlayer.Survival", "ApplySavedState"],
ROADMAP: [
"[x] Can die from survival pressure.",
],
}
def main() -> None:
missing: list[str] = []
for path, snippets in REQUIRED.items():
text = path.read_text(encoding="utf-8")
for snippet in snippets:
if snippet not in text:
missing.append(f"{path.relative_to(ROOT)} missing {snippet!r}")
if missing:
raise SystemExit("FAILED: " + "; ".join(missing))
print("OK: survival-pressure death QA gate is tied to damage, death, HUD, respawn, and persistence coverage.")
if __name__ == "__main__":
main()