Add survival pressure death QA gate
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:",
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user