#!/usr/bin/env python3 """Verify the MVP full day/night survival 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" CORE_DESIGN = ROOT / "Docs" / "CoreDesignDocument.md" MOVEMENT_TIME = ROOT / "Docs" / "MovementAndTimeScaleBaseline.md" GAME_STATE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianGameState.h" GAME_STATE_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianGameState.cpp" SURVIVAL_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianSurvivalComponent.cpp" DEBUG_HUD_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianDebugHUD.cpp" WORLD_TIME_VERIFY = ROOT / "Scripts" / "verify_world_time_persistence.py" STAT_SAVE_VERIFY = ROOT / "Scripts" / "verify_stat_save_load_support.py" SHELTER_VERIFY = ROOT / "Scripts" / "verify_shelter_weather_protection.py" FIRE_VERIFY = ROOT / "Scripts" / "verify_craft_fire_qa_gate.py" REQUIRED = { QA_DOC: [ "## Full Day/Night Survival", "4 real hours = 1 in-game day", "WorldHours", "Day/night state changes at least once", "Hunger, thirst, stamina, body temperature, and health", ], CORE_DESIGN: [ "gameplay calendar target: `4 real hours = 1 in-game day`", "Solve thirst, hunger, warmth, and shelter.", ], MOVEMENT_TIME: [ "Calendar compression is for crop growth, weather passage, day/night rhythm", "hunger, thirst, weather exposure, and stamina", ], GAME_STATE_H: [ "float GameHoursPerRealMinute = 0.1f;", "float WorldHours = 8.0f;", "bool IsNight() const;", ], GAME_STATE_CPP: [ "WorldHours += (DeltaSeconds / 60.0f) * GameHoursPerRealMinute;", "while (WorldHours >= 24.0f)", "DOREPLIFETIME(AAgrarianGameState, WorldHours);", "bool AAgrarianGameState::IsNight() const", ], SURVIVAL_CPP: [ "Survival.Hunger -= HungerDecayPerMinute * Minutes;", "Survival.Thirst -= ThirstDecayPerMinute * Minutes;", "Survival.Stamina += StaminaRecoveryPerSecond * DeltaTime;", "Survival.BodyTemperature += FMath::Clamp(ExposureDelta", "Survival.Health -= StarvationDamagePerMinute * Minutes;", "Survival.Health -= DehydrationDamagePerMinute * Minutes;", "Survival.Health -= ColdDamagePerMinute * Minutes", ], DEBUG_HUD_CPP: [ "DrawCriticalStats", "State ALIVE", "State DEAD", ], WORLD_TIME_VERIFY: ["WorldHours", "SaveGame->WorldHours"], STAT_SAVE_VERIFY: ["SurvivalComponent->ApplySavedState", "SavedPlayer.Survival"], SHELTER_VERIFY: ["weather protection"], FIRE_VERIFY: ["## Craft Fire", "AAgrarianCampfire"], ROADMAP: [ "[x] Can survive one full day/night cycle.", ], } 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: full day/night survival QA gate is tied to time, survival, HUD, and persistence coverage.") if __name__ == "__main__": main()