Compare commits
10 Commits
8e33068d01
...
4e17cede2d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e17cede2d | |||
| d2b8185333 | |||
| d59f613e2b | |||
| c742a172da | |||
| 64d0603680 | |||
| 0aa1802949 | |||
| 66c6052e91 | |||
| 766ceac5d7 | |||
| b5416e0453 | |||
| 7b1f9b81c0 |
@@ -29,7 +29,6 @@ Core commitments:
|
||||
- [ ] Build toward an Earth-scale world made from real-world terrain tiles.
|
||||
- [ ] Keep travel paced by believable real-world movement, vehicles, terrain, and character condition.
|
||||
- [ ] Treat terrain, bathymetry, biomes, resources, rivers, and mountains as data-driven long-term infrastructure, not one-off maps.
|
||||
- [ ] Represent each real-world tile with layered climate, biome, ecology, and human-use metadata so the in-game environment feels geographically recognizable rather than using generic biome labels.
|
||||
- [ ] Keep base world time grounded; the world should progress without feeling artificially sped up.
|
||||
- [ ] Make player skill, tools, infrastructure, cooperation, and knowledge improve efficiency, yield, reliability, quality, and capacity rather than breaking natural biological time.
|
||||
- [ ] Treat learning as a core play loop: knowledge unlocks understanding, practice builds competence, and infrastructure makes advanced work possible.
|
||||
@@ -826,65 +825,60 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
- [x] Replace the placeholder Ground Zero environment presentation with investor-facing biome dressing: believable terrain material, grass, brush, shrubs, bushes, trees, rocks, water visuals, and local coastal-scrub color variation. Upgraded the repeatable Ground Zero setup to require denser investor-facing foliage counts and twenty-three labeled variation actors covering trees, brush, shrubs, dry grass mats, rock slabs, water-bank pieces, reeds, and freshwater surface material variation, then extended the verifier/docs so the map no longer qualifies if the visual dressing falls back to sparse placeholder presentation.
|
||||
- [x] Add a real water-source visual pass with surface material, edge treatment, scale, and placement that reads as collectable freshwater instead of a placeholder plane. Formalized the MVP freshwater presentation around the native `AAgrarianWaterSource` water-surface, stone-bank, and collect-marker proxies, documented the Ground Zero drainage-candidate placement and nearby water-bank/reed dressing, and added an Unreal verifier that checks surface material, edge treatment, scale, placement, and nearby dressing actors in the actual map.
|
||||
- [x] Add density and sightline tuning so grasses, shrubs, trees, and resource clusters are visible enough to sell the world without hiding gameplay-critical objects. Added protected foliage clearances around early survival targets and biome resource nodes, sampled first-look sightline corridors from the player start to wood, fiber, campfire, shelter, wildlife, and freshwater, plus a dedicated Unreal verifier/documentation gate so investor-facing density cannot regress into object-hiding clutter.
|
||||
- [x] Preserve realism as the target: use assets, materials, lighting, and environmental dressing that can survive toward MVP production rather than cosmetic throwaways where practical. Added MVP realism target rules to the shared art/UX/code/asset standards, tied current 0.1.O visual passes to production-directed proxy expectations, and added a verifier that requires Ground Zero materials, water, density/sightline, character, and survival-object docs to point toward production realism instead of cosmetic throwaways.
|
||||
- [x] Define default, recommended, and cinematic investor rendering presets, with ray tracing available only as an optional high-end/cinematic mode and never required for baseline visual credibility. Added `Config/AgrarianRenderingPresets.ini`, `Docs/Rendering/InvestorRenderingPresets.md`, and a verifier that requires Default and Recommended to remain non-ray-traced while Cinematic is the only optional high-end ray-tracing profile.
|
||||
- [x] Verify the non-ray-traced compatibility/default path still looks credible on common investor, tester, and remote-session hardware. Added a dedicated non-ray-traced default verifier that checks Default and Recommended disable `r.RayTracing`, `r.Lumen.HardwareRayTracing`, and `r.PathTracing`, keeps Cinematic as the only optional ray-tracing profile, and documents that packaged investor demos should launch on Default unless another profile is explicitly selected.
|
||||
- [x] Add packaged-demo visual QA screenshots or short clips for startup credits, character selection, first spawn, terrain, vegetation, water, campfire, shelter, pause menu, and save/quit before each investor build is called ready. Added a full investor-demo visual QA evidence runbook, Windows helper, startup capture expansion, and verifier requiring Sunshine/Moonlight or equivalent real-GPU captures for startup credits, character selection, first spawn, terrain, vegetation, water, campfire, shelter, pause, and save/quit before a packaged build is called investor-ready.
|
||||
- [x] Add an investor-demo acceptance gate: no current build should be described as investor visual MVP if menus are confusing, character art is mannequin-only, terrain is flat/tan, foliage is absent or unreadable, or core objects still read as primitive debug shapes. Added an investor-demo acceptance gate document, updated demo status wording, and added verification that hard-fail conditions cover confusing menus, mannequin-only characters, flat/tan terrain, absent/unreadable foliage, unreadable water, primitive debug objects, non-ray-traced default credibility, and missing visual QA evidence.
|
||||
- [ ] Preserve realism as the target: use assets, materials, lighting, and environmental dressing that can survive toward MVP production rather than cosmetic throwaways where practical.
|
||||
- [ ] Define default, recommended, and cinematic investor rendering presets, with ray tracing available only as an optional high-end/cinematic mode and never required for baseline visual credibility.
|
||||
- [ ] Verify the non-ray-traced compatibility/default path still looks credible on common investor, tester, and remote-session hardware.
|
||||
- [ ] Add packaged-demo visual QA screenshots or short clips for startup credits, character selection, first spawn, terrain, vegetation, water, campfire, shelter, pause menu, and save/quit before each investor build is called ready.
|
||||
- [x] Add an investor-demo acceptance gate: no current build should be described as investor visual MVP if menus are confusing, character art is mannequin-only, terrain is flat/tan, foliage is absent or unreadable, or core objects still read as primitive debug shapes.
|
||||
|
||||
## 0.1.P MVP Audio And Atmosphere
|
||||
|
||||
- [x] Add ambient biome audio. Extended the placed `AAgrarianWeatherAudioController` so its ambient component explicitly owns a Ground Zero coastal-scrub biome loop slot with separate day/night volume targets, keeping the current MVP silent until placeholder or final audio assets are assigned while giving the map a real ambient audio attachment point.
|
||||
- [x] Add footstep placeholders. Added native player-character footstep hooks with assignable walk, sprint, crouch, and prone sound slots plus movement-state cadence, keeping the MVP silent until placeholder or final surface-aware audio assets are assigned.
|
||||
- [x] Add gathering sounds. Added spatialized resource-node gathering audio hooks with assignable normal/depleted gathering cues and a server-authoritative multicast trigger after successful harvests, keeping multiplayer clients aligned while remaining silent until audio assets are assigned.
|
||||
- [x] Add fire sounds. Added campfire loop, ignition, and extinguish audio hooks with spatialized components, replicated lit-state loop control, and server-triggered multicast event cues so fire audio follows the authoritative campfire state once assets are assigned.
|
||||
- [x] Add unattended and poorly maintained fire risk for campfires and other open-flame sources. Added server-side campfire risk state for lit duration, seconds since maintenance, cleared area, containment, high fuel, wet weather mitigation, and a replicated `FireRiskScore` that later ignition/spread systems can consume.
|
||||
- [x] Add grass and forest ignition checks from irresponsible fire placement, wind/weather, dry fuel, nearby vegetation, and burn duration. Added foliage fuel counting, campfire vegetation ignition risk scores, grass/brush and forest ignition flags, and weather/wind/burn-duration modifiers so unsafe fire placement near dry fuel can now become a server-authoritative ignition risk.
|
||||
- [x] Add shelter/structure ignition risk when fires are placed too close to primitive shelters, wood piles, flammable crafting stations, or settlement objects. Added campfire structure ignition risk for nearby primitive shelters and flammable wood/fiber resource nodes, with containment, burn-duration, weather/wind, and fire-risk modifiers before a replicated structure ignition flag is set.
|
||||
- [x] Add server-authoritative fire spread rules for grass, brush, trees, shelters, and other burnable actors, including fuel, distance, wind, weather, and suppression hooks. Added replicated grass, forest, and structure fire intensities plus active spread radius that grow only on the server from nearby fuel, ignition distance, wind/weather, and a suppression-pressure hook for later rain, carried water, dirt/sand, firebreaks, and tools.
|
||||
- [x] Add fire maintenance gameplay so watched, cleared, contained, or extinguished fires are safe, while neglected fires can become dangerous. Updated lit campfire interaction to maintain the fire, added watch, clear-area, and contain-fire hooks, and made maintenance reduce campfire, vegetation, forest, and structure ignition risks while extinguishing resets active risk state.
|
||||
- [x] Add fire suppression hooks for rain, water carrying, dirt/sand, cleared firebreaks, and future firefighting tools. Added shared server-side suppression hooks plus water, dirt/sand, firebreak, and tool wrappers that raise suppression pressure, reduce ignition risks, reduce active fire intensity, shrink spread radius, and let rain/water drain fuel.
|
||||
- [x] Persist active grass, forest, and structure fires across save/load without corrupting world state. Extended campfire persistence coverage for ignition flags, ignition risk scores, active grass/forest/structure fire intensities, spread radius, and suppression pressure so save/load recovery preserves active and partially suppressed fire state.
|
||||
- [x] Add QA coverage for safe campfires, unsafe campfires, vegetation spread, shelter ignition, suppression, and save/load recovery. Added a fire-risk QA coverage document and verifier requiring safe/unsafe campfire, vegetation spread, shelter ignition, suppression, and save/load recovery scenarios plus the supporting fire-risk verification scripts.
|
||||
- [x] Add weather sounds. Formalized the existing placed weather audio controller as the MVP weather-sound path, documenting rain, wind, storm, clear ambient, and biome loop slots plus verification that weather playback follows replicated weather state, provider wind speed, and day/night state while remaining silent until assets are assigned.
|
||||
- [x] Add wildlife sounds. Added spatialized wildlife audio hooks with assignable idle, flee/chase, death, and harvest sound slots plus server-triggered multicast playback from authoritative wildlife state changes and harvest events.
|
||||
- [x] Add UI sounds. Added optional 2D confirm, back, selection, and save/quit sound hooks to the MVP frontend widget, with keyboard and mouse actions sharing the same feedback path while remaining silent until UI audio assets are assigned.
|
||||
- [x] Add mix settings. Added MVP audio mix settings for master, ambient, weather, foley, fire, wildlife, and UI buses with conservative investor-build defaults and documentation for future SoundClass/MetaSound replacement.
|
||||
- [x] Add volume sliders. Added MVP frontend volume sliders for master, ambient, weather, effects, wildlife, and UI levels, with runtime value storage and immediate UI-volume application to frontend feedback sounds while leaving final SoundClass binding for authored audio assets.
|
||||
- [ ] Add ambient biome audio.
|
||||
- [ ] Add footstep placeholders.
|
||||
- [ ] Add gathering sounds.
|
||||
- [ ] Add fire sounds.
|
||||
- [ ] Add unattended and poorly maintained fire risk for campfires and other open-flame sources.
|
||||
- [ ] Add grass and forest ignition checks from irresponsible fire placement, wind/weather, dry fuel, nearby vegetation, and burn duration.
|
||||
- [ ] Add shelter/structure ignition risk when fires are placed too close to primitive shelters, wood piles, flammable crafting stations, or settlement objects.
|
||||
- [ ] Add server-authoritative fire spread rules for grass, brush, trees, shelters, and other burnable actors, including fuel, distance, wind, weather, and suppression hooks.
|
||||
- [ ] Add fire maintenance gameplay so watched, cleared, contained, or extinguished fires are safe, while neglected fires can become dangerous.
|
||||
- [ ] Add fire suppression hooks for rain, water carrying, dirt/sand, cleared firebreaks, and future firefighting tools.
|
||||
- [ ] Persist active grass, forest, and structure fires across save/load without corrupting world state.
|
||||
- [ ] Add QA coverage for safe campfires, unsafe campfires, vegetation spread, shelter ignition, suppression, and save/load recovery.
|
||||
- [ ] Add weather sounds.
|
||||
- [ ] Add wildlife sounds.
|
||||
- [ ] Add UI sounds.
|
||||
- [ ] Add mix settings.
|
||||
- [ ] Add volume sliders.
|
||||
|
||||
## 0.1.Q MVP QA Gates
|
||||
|
||||
- [x] Can launch packaged client. Added an MVP QA gate that requires the Windows package script, packaged executable, installed investor launchers, and the real-GPU visual QA readiness check before the client launch gate qualifies.
|
||||
- [x] Can launch server. Added an MVP QA gate for the Linux gameplay host requiring the true dedicated build path or current binary-engine fallback, deployment to `/opt/agrarian/server`, `agrarian-game-server.service` active state, UDP `7777` listener evidence, and Ground Zero map browse evidence.
|
||||
- [x] Can connect two clients. Added a two-client connection QA gate and Windows helper that checks the packaged client, launches two client instances against the same `play.agrariangame.com:7777` or LAN endpoint, and ties the manual observation steps to the multiplayer latency smoke plan.
|
||||
- [x] Can gather resources. Added a resource gathering QA gate tied to Ground Zero wood/fiber nodes, server-authoritative resource interaction, replicated harvest depletion, inventory grants, resource persistence coverage, and the natural shelter playable-loop smoke test.
|
||||
- [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.
|
||||
- [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.
|
||||
- [x] Can reconnect and retain state. Added a reconnect state-retention QA gate tied to logout/restart player snapshots, safe player identity, transform, survival, care history, inventory restore, normal-spawn fallback behavior, and the two-client manual reconnect evidence path.
|
||||
- [x] Can restart server and retain placed shelter. Added a server-restart shelter persistence QA gate tied to `primitive_shelter` persistent actor state, world actor save/load, game-mode class registration, load-on-server-start behavior, shelter weather protection, and a release smoke requirement to place, save, restart, and confirm the shelter transform remains.
|
||||
- [x] No critical log spam during 30-minute test. Added a 30-minute critical log soak QA gate plus `scan_critical_log_spam.py` so client/server/release logs can be checked for fatal, crash, assertion, ensure, access-violation, callstack, and critical-error spam before a milestone package is treated as investor-stable.
|
||||
- [x] Clean up Unreal API deprecation warnings from packaged builds, starting
|
||||
- [ ] Can launch packaged client.
|
||||
- [ ] Can launch server.
|
||||
- [ ] Can connect two clients.
|
||||
- [ ] Can gather resources.
|
||||
- [ ] Can craft a fire.
|
||||
- [ ] Can craft a shelter.
|
||||
- [ ] Can survive one full day/night cycle.
|
||||
- [ ] Can die from survival pressure.
|
||||
- [ ] Can reconnect and retain state.
|
||||
- [ ] Can restart server and retain placed shelter.
|
||||
- [ ] No critical log spam during 30-minute test.
|
||||
- [ ] Clean up Unreal API deprecation warnings from packaged builds, starting
|
||||
with direct `NetCullDistanceSquared` access on replicated world actors before
|
||||
future Unreal upgrades turn the warning into a compile blocker. Replaced direct
|
||||
`NetCullDistanceSquared = FMath::Square(...)` assignments with
|
||||
`SetNetCullDistanceSquared(FMath::Square(...))` on item pickups, resource
|
||||
nodes, campfires, shelters, wildlife, water sources, weather exposure zones,
|
||||
and wildlife spawn managers, then added verifier coverage to prevent the
|
||||
deprecated assignment style from returning.
|
||||
- [x] Server remains stable with target test player count. Added a target player count server-stability QA gate using the MVP audience definition: 2-player minimum proof, 4-player closed-test smoke target, and 8-player stretch test only after the server path is stable, with evidence tied to server launch, two-client connection, reconnect retention, critical log scanning, active service state, and UDP `7777` listener checks.
|
||||
future Unreal upgrades turn the warning into a compile blocker.
|
||||
- [ ] Server remains stable with target test player count.
|
||||
|
||||
## 0.1.R Knowledge And Skill Foundation
|
||||
|
||||
- [x] Define the MVP separation between knowledge, practical experience, physical stats, tools, and infrastructure. Added `Docs/KnowledgeAndSkillFoundation.md` with a five-part MVP model separating knowledge, practical experience, physical stats, tools, and infrastructure so basic survival remains possible but outcomes improve through understanding, practice, equipment, and durable world improvements.
|
||||
- [x] Add a first-pass skill taxonomy for survival, gathering, tool use, crafting, fire, shelter, navigation, first aid, food safety, and weather awareness. Added the first MVP taxonomy to `Docs/KnowledgeAndSkillFoundation.md`, covering survival, gathering, tool use, crafting, fire, shelter, navigation, first aid, food safety, and weather awareness as non-lockout skill domains that modify risk, quality, speed, yield, readability, and confidence.
|
||||
- [x] Define how knowledge affects survival actions: fewer mistakes, safer attempts, better yields, lower injury risk, and more reliable outcomes. Added the knowledge action-effects model to `Docs/KnowledgeAndSkillFoundation.md`, defining how knowledge changes warnings, failed-action reasons, safety, yield/waste, injury risk, and outcome reliability without silently guaranteeing success or replacing practical experience.
|
||||
- [x] Define how practical experience grows through use, repetition, mistakes, and recovery from failure. Added practical experience growth rules to `Docs/KnowledgeAndSkillFoundation.md`, defining gain from meaningful use, diminishing returns for rote repetition, learning from readable mistakes, and extra credit for recovering well from failure.
|
||||
- [x] Add first contextual learning prompts for fire safety, potable water, exposure, shelter placement, injury care, and resource identification. Added first contextual prompt specs to `Docs/KnowledgeAndSkillFoundation.md` for fire safety, potable water, exposure, shelter placement, injury care, and resource identification, with trigger examples, prompt intent, sample wording, and the rule that prompts explain immediate risk without pausing the game or forcing a quiz.
|
||||
- [x] Design optional knowledge checks that appear when relevant to the action instead of interrupting basic play. Added optional knowledge-check rules to `Docs/KnowledgeAndSkillFoundation.md`, defining inline/skippable presentation, action-relevant timing, calm review moments, non-punitive wrong answers, and the rule that checks deepen understanding without gating the first survival loop.
|
||||
- [x] Add player-facing feedback that explains why an action failed or produced poor results. Added failed-action and poor-result feedback rules to `Docs/KnowledgeAndSkillFoundation.md`, requiring short messages that say what happened, name one likely cause, offer one useful next step, avoid blame, and avoid revealing hidden formulas.
|
||||
- [x] Define accessibility rules for the learning system: hints, retries, readable wording, no hard lockout from basic survival, and non-punitive practice paths. Added learning accessibility rules to `Docs/KnowledgeAndSkillFoundation.md`, covering reusable hints, retries, readable wording, non-color-only warnings, no lockout from basic survival, safer practice paths, and diminishing returns to prevent exploit loops.
|
||||
- [ ] Define the MVP separation between knowledge, practical experience, physical stats, tools, and infrastructure.
|
||||
- [ ] Add a first-pass skill taxonomy for survival, gathering, tool use, crafting, fire, shelter, navigation, first aid, food safety, and weather awareness.
|
||||
- [ ] Define how knowledge affects survival actions: fewer mistakes, safer attempts, better yields, lower injury risk, and more reliable outcomes.
|
||||
- [ ] Define how practical experience grows through use, repetition, mistakes, and recovery from failure.
|
||||
- [ ] Add first contextual learning prompts for fire safety, potable water, exposure, shelter placement, injury care, and resource identification.
|
||||
- [ ] Design optional knowledge checks that appear when relevant to the action instead of interrupting basic play.
|
||||
- [ ] Add player-facing feedback that explains why an action failed or produced poor results.
|
||||
- [ ] Define accessibility rules for the learning system: hints, retries, readable wording, no hard lockout from basic survival, and non-punitive practice paths.
|
||||
- [ ] Define the first subject content format: topic, concepts, difficulty tier, prerequisite concepts, in-game effect, practice action, and source note.
|
||||
- [ ] Add a small MVP question bank for elementary survival knowledge.
|
||||
- [ ] Define when deeper questions should matter: quality improvements, safer work, complex crafting, teaching others, and advanced branches.
|
||||
@@ -892,6 +886,14 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
- [ ] Add persistence requirements for knowledge, skill experience, learned concepts, failed attempts, and tutorial state.
|
||||
- [ ] Add multiplayer rules for teaching, observation, shared work, and group skill benefits.
|
||||
|
||||
## 0.1.S Investor Visual Recovery
|
||||
|
||||
- [x] Prevent broken first-view character/menu presentation before 0.2. Added a dedicated frontend presentation state that hides and freezes the pawn, suppresses gameplay/death HUD overlays, uses a clean temporary menu camera, and restores gameplay camera/input only after the player enters Ground Zero.
|
||||
- [x] Improve MVP character presentation enough for investor builds before final character art. Added practical workwear, backpack, bedroll, and boot proxy layers on top of the selected Manny/Quinn meshes so the current characters read as near-future frontier survivors instead of untouched template dummies.
|
||||
- [x] Raise the Ground Zero first-look vegetation density before 0.2. Expanded the repeatable Ground Zero setup from 64 trees, 148 shrubs, and 260 grass clumps to 96 trees, 220 shrubs, and 420 grass clumps while preserving spawn and sightline reservations.
|
||||
- [x] Add first-look environment dressing near spawn. Added a focused ring of coastal oak, brush, grass mat, and rock variation actors around the safe spawn approach so the first investor camera view reads as a dressed coastal-scrub environment rather than empty tan terrain.
|
||||
- [x] Tighten visual recovery verification. Updated character and Ground Zero environment verifiers so missing workwear proxy layers, lower foliage counts, or missing first-look variation actors fail before an investor build is called ready.
|
||||
|
||||
---
|
||||
|
||||
# Version 0.2 - Persistent Homesteading
|
||||
@@ -1355,29 +1357,16 @@ Goal: Expand from a small test world toward a huge, regionally diverse, persiste
|
||||
|
||||
## 0.7.B Biome Diversity
|
||||
|
||||
- [ ] Define a layered real-world biome architecture instead of a simplified school-model biome list.
|
||||
- [ ] Define the biome data contract for each 1 km tile: macro biome weights, regional ecological region, local sub-biome blend, confidence score, source datasets, generation version, and manual override fields.
|
||||
- [ ] Define core climate bands that drive temperature, daylight, seasonality, and weather behavior: polar, subpolar, boreal, cool temperate, warm temperate, subtropical, tropical, and highland/alpine.
|
||||
- [ ] Define roughly 15-25 macro biome families as the maintainable top-level simulation vocabulary.
|
||||
- [ ] Define roughly 60-120 regional biome variants so recognizable places such as the Pacific Northwest, Siberia, Patagonia, the Great Plains, Mongolian Steppe, Scottish Highlands, Amazon Basin, and Mediterranean coasts can emerge without one-off labels.
|
||||
- [ ] Define procedural local sub-biome blending so tiles can carry weighted mixtures such as prairie, riparian woodland, marsh, rocky slope, or scrub edge instead of hard biome borders.
|
||||
- [ ] Derive biome weights from latitude, elevation, rainfall, prevailing wind, ocean proximity, ocean currents, soil type, drainage, seasonal temperature swing, rain shadow effects, and river/wetland systems.
|
||||
- [ ] Add biome inference rules for real Earth patterns: subtropical west-coast Mediterranean climates, humid east coasts, interior continental steppe/prairie, leeward mountain deserts, equatorial alpine tundra, river-valley fertility, and coastal/marine moderation.
|
||||
- [ ] Add forest biome variants: tropical rainforest, tropical seasonal forest, temperate deciduous forest, temperate rainforest, boreal conifer forest, cloud forest, and mangrove forest.
|
||||
- [ ] Add grassland biome variants: savanna, prairie, steppe, pampas, and alpine meadow.
|
||||
- [ ] Add dryland biome variants: hot desert, cold desert, semi-arid scrubland, Mediterranean shrubland, and badlands.
|
||||
- [ ] Add cold-region biome variants: arctic tundra, alpine tundra, ice sheet, and glacier.
|
||||
- [ ] Add wet-system biome variants: swamp, marsh, fen/bog, river delta, and floodplain.
|
||||
- [ ] Add aquatic and coastal biome variants: freshwater river, freshwater lake, estuary, rocky coast, sandy coast, coral reef, and kelp forest.
|
||||
- [ ] Add a climate-engine layer that produces the environmental variables biome generation consumes.
|
||||
- [ ] Add a biome-generator layer that assigns weighted biome and regional-variant outputs to each tile.
|
||||
- [ ] Add an ecology layer that converts biome weights into plausible plants, animals, diseases, soil fertility, water access, fuel availability, and building-material availability.
|
||||
- [ ] Add a human-use layer that converts biome/ecology outputs into crop viability, settlement density, trade value, culture pressure, transportation difficulty, warfare strategy, and long-term economic specialization.
|
||||
- [ ] Make crop viability, animal species, disease pressure, water access, building materials, fuel availability, trade economics, settlement density, cultural evolution, transportation difficulty, and warfare strategy consume the same biome/ecology tile metadata instead of separate hand-authored rules.
|
||||
- [ ] Map natural resources to likely real-world geology, flora, water, climate, soil, and land-cover data.
|
||||
- [ ] Add biome-specific survival pressure for exposure, thirst, food reliability, fire risk, disease risk, travel friction, shelter material scarcity, and visibility.
|
||||
- [ ] Add biome plausibility QA checks that reject tiles whose climate, vegetation, water, soil, and resource placement contradict the represented real-world location.
|
||||
- [ ] Keep Ground Zero as an early coastal-scrub proof tile, but require future generated tiles to move toward this layered biome contract as the Earth-scale pipeline matures.
|
||||
- [ ] Derive biome candidates from real-world land-cover, climate, elevation, and water data.
|
||||
- [ ] Add forest biome.
|
||||
- [ ] Add plains biome.
|
||||
- [ ] Add mountain biome.
|
||||
- [ ] Add wetland biome.
|
||||
- [ ] Add desert/dryland biome.
|
||||
- [ ] Add cold biome.
|
||||
- [ ] Add biome-specific resources.
|
||||
- [ ] Map natural resources to likely real-world geology, flora, water, and climate.
|
||||
- [ ] Add biome-specific survival pressure.
|
||||
|
||||
## 0.7.C Logistics And Transportation
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,39 +1,25 @@
|
||||
# MVP Character Proxies
|
||||
|
||||
The 0.1.O investor visual pass introduces first playable character proxies for
|
||||
the startup character selection flow. These are not final production humans;
|
||||
they are practical, human-scale stand-ins that remove the single default dummy
|
||||
presentation while the final realistic character art pipeline is still pending.
|
||||
The current investor build uses MVP character proxies, not final production humans.
|
||||
|
||||
## Current Proxies
|
||||
Selection currently supports:
|
||||
|
||||
- Young adult male:
|
||||
`/Game/Characters/Mannequins/Meshes/SKM_Manny_Simple`
|
||||
- Young adult female:
|
||||
`/Game/Characters/Mannequins/Meshes/SKM_Quinn_Simple`
|
||||
- Male workwear material:
|
||||
`/Game/Agrarian/Characters/Materials/M_AGR_CharacterProxy_Workwear_Male`
|
||||
- Female workwear material:
|
||||
`/Game/Agrarian/Characters/Materials/M_AGR_CharacterProxy_Workwear_Female`
|
||||
- `SKM_Manny_Simple` as the young adult male base.
|
||||
- `SKM_Quinn_Simple` as the young adult female base.
|
||||
|
||||
The materials use muted earth-tone workwear colors so the character reads as an
|
||||
Agrarian survivor/pioneer proxy instead of an untouched template dummy.
|
||||
Agrarian applies project-owned workwear materials and runtime presentation
|
||||
layers for torso clothing, backpack, bedroll, and boots. These layers are meant
|
||||
to move the build away from untouched Unreal template dummies while still
|
||||
remaining honest prototype art.
|
||||
|
||||
## Runtime Flow
|
||||
Visual direction:
|
||||
|
||||
The MVP frontend stores the selected young-adult archetype. When the loading
|
||||
segment closes, it issues `AgrarianSelectCharacter male` or
|
||||
`AgrarianSelectCharacter female`. The player controller records the selected
|
||||
proxy and applies the matching mesh/material to the possessed Agrarian player
|
||||
character.
|
||||
- realistic near-future frontier survival;
|
||||
- practical modern workwear;
|
||||
- post-collapse scarcity without apocalypse-warrior exaggeration;
|
||||
- settlement-builder tone;
|
||||
- a few archetypes that can later respond to age, health, injury, nutrition,
|
||||
and long-term physical condition.
|
||||
|
||||
The character asset folder is always cooked for investor builds so both proxy
|
||||
materials remain available in packaged clients.
|
||||
|
||||
## Replacement Path
|
||||
|
||||
Final character work should replace these proxies with grounded realistic human
|
||||
assets, production clothing, age/condition variation, and replication/persistence
|
||||
of visual state. The current C++ selection path is intentionally simple so final
|
||||
assets can slot into the same male/female archetype switch without reworking the
|
||||
menu flow.
|
||||
Final character work should replace these proxies with original or properly
|
||||
licensed free character bases before public testing.
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# Codebase Readiness Review
|
||||
|
||||
Date: 2026-05-19
|
||||
|
||||
Scope: source code, build scripts, verification scripts, config files, and the
|
||||
roadmap after completion of `0.1.R`.
|
||||
|
||||
## 0.1 Completion Check
|
||||
|
||||
All `0.1.A` through `0.1.R` roadmap checkboxes are complete. The remaining
|
||||
unchecked roadmap items before `Version 0.2` are North Star and philosophy
|
||||
statements, not incomplete `0.1` implementation tasks.
|
||||
|
||||
## Current Strengths
|
||||
|
||||
- Core gameplay systems are separated into recognizable Unreal classes:
|
||||
character, controller, game state, survival, inventory, crafting, building,
|
||||
persistence, weather, resource nodes, wildlife, shelter, campfire, water, UI,
|
||||
and automation.
|
||||
- Server-authoritative paths are already present for core multiplayer actions
|
||||
such as crafting, item use/drop/splitting, travel, save/load, respawn, fire,
|
||||
resource depletion, wildlife state, world actors, and weather state.
|
||||
- Persistence has a central subsystem and explicit world actor/resource/player
|
||||
capture paths, which is the right base for 0.2 homesteading.
|
||||
- The project now has broad verifier coverage for roadmap promises and MVP
|
||||
guardrails. This is useful for preventing accidental regression while the team
|
||||
moves into larger systems.
|
||||
- The Windows build pipeline, package script, visual QA gate, and Linux server
|
||||
target are established enough to keep milestone work shippable.
|
||||
|
||||
## Cleanup Findings
|
||||
|
||||
- `AgrarianEditorAutomationLibrary.cpp` is doing too much. It contains map
|
||||
import, gameplay smoke tests, persistence tests, and setup helpers in one
|
||||
large file. It should be split by responsibility before 0.2 adds claiming,
|
||||
farming, storage, and household tests.
|
||||
- The MVP frontend is still implemented as runtime-created widget trees. This
|
||||
has been effective for fast iteration, but 0.2 should move toward reusable
|
||||
UMG widgets, shared styling, and a cleaner input/navigation model.
|
||||
- Placeholder/proxy meshes are intentionally still present for resources,
|
||||
shelters, campfires, water, wildlife, and characters. 0.2 should replace the
|
||||
highest-impact placeholders with durable realistic assets while preserving
|
||||
data-driven class behavior.
|
||||
- Several systems use hard-coded Ground Zero, MVP, and automation constants.
|
||||
These are acceptable for 0.1 but should move toward data assets, config, or
|
||||
tile metadata as 0.2 introduces claims, crops, animals, storage, and
|
||||
homestead state.
|
||||
- Admin/dev console commands currently share runtime controller paths. Before
|
||||
broader testing, privileged commands need a clearer authority, role, and
|
||||
audit boundary so tester tools do not become gameplay exploits.
|
||||
- Verifier scripts are useful but numerous. 0.2 should add a grouped verifier
|
||||
runner so milestone verification is one command with explicit categories.
|
||||
|
||||
## Low-Risk Cleanup Applied
|
||||
|
||||
- Removed duplicate `SavingAndQuit` branch logic from the MVP frontend continue
|
||||
flow. Behavior is unchanged, but the control path is now easier to read.
|
||||
|
||||
## 0.2 Engineering Priorities
|
||||
|
||||
- Keep land claims, farming, storage, household tasks, and future economy state
|
||||
server-authoritative from the first implementation pass.
|
||||
- Add schema/version fields when introducing new persistent records.
|
||||
- Use data assets for claim rules, crop definitions, animal definitions,
|
||||
storage container definitions, and early profession/knowledge topics.
|
||||
- Create focused automation helpers by domain instead of expanding the current
|
||||
monolithic editor automation file.
|
||||
- Replace placeholder visuals in the order investors and testers notice them:
|
||||
terrain material, grasses/shrubs/trees, water, character bodies/clothing,
|
||||
resource objects, fire/smoke, shelter pieces, and wildlife.
|
||||
- Keep ray tracing optional. The default non-ray-traced path must remain the
|
||||
baseline for demo readability and remote testing.
|
||||
@@ -379,3 +379,252 @@ Non-punitive practice paths:
|
||||
|
||||
Accessibility rule: learning should make players more capable without making
|
||||
them feel trapped, shamed, or forced into a classroom flow.
|
||||
|
||||
## Subject Content Format
|
||||
|
||||
Learning content should use a small structured format so topics can become data
|
||||
assets later without rewriting design intent.
|
||||
|
||||
Required fields:
|
||||
|
||||
- `topic`: stable identifier and display name, such as `fire.clearance`.
|
||||
- `concepts`: one or more concept tags the player may learn or demonstrate.
|
||||
- `difficulty_tier`: `elementary`, `practical`, `advanced`, or `expert`.
|
||||
- `prerequisite_concepts`: concept tags that should be known first, if any.
|
||||
- `in_game_effect`: the warning, modifier, feedback, unlock, or quality effect
|
||||
this topic can influence.
|
||||
- `practice_action`: the action that can build practical experience for the
|
||||
topic.
|
||||
- `source_note`: a short design/source note explaining why the topic matters.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
topic: fire.clearance
|
||||
concepts: fire_safety, dry_fuel, wind_spread
|
||||
difficulty_tier: elementary
|
||||
prerequisite_concepts: none
|
||||
in_game_effect: clearer fire-risk warning and lower unsafe-placement mistakes
|
||||
practice_action: clear area, contain fire, maintain fire, extinguish fire
|
||||
source_note: Open flame near dry fuel and wind can spread beyond the campfire.
|
||||
```
|
||||
|
||||
Format rule: content records should be small enough to review in source control
|
||||
and explicit enough to become data assets later.
|
||||
|
||||
## MVP Elementary Survival Question Bank
|
||||
|
||||
The first question bank is intentionally small and elementary. These questions
|
||||
support optional checks, camp review, and future teaching objects.
|
||||
|
||||
```text
|
||||
id: fire.clearance.001
|
||||
topic: fire.clearance
|
||||
question: Why clear dry brush away from a campfire?
|
||||
answers: A) It lowers fire-spread risk. B) It makes the fire colder. C) It makes rain stronger.
|
||||
correct: A
|
||||
feedback: Open flame, dry fuel, and wind can spread fire beyond the campfire.
|
||||
```
|
||||
|
||||
```text
|
||||
id: water.potable.001
|
||||
topic: water.potable
|
||||
question: What is the safest first assumption about unknown water?
|
||||
answers: A) It may need treatment. B) It is always safe. C) It restores warmth.
|
||||
correct: A
|
||||
feedback: Water access and safe drinking water are not always the same thing.
|
||||
```
|
||||
|
||||
```text
|
||||
id: exposure.cold.001
|
||||
topic: exposure.cold
|
||||
question: What helps most when cold wind and rain are lowering body temperature?
|
||||
answers: A) Shelter, warmth, dry conditions, and rest. B) Sprinting forever. C) Dropping all food.
|
||||
correct: A
|
||||
feedback: Wind, rain, fatigue, and nightfall can make exposure dangerous.
|
||||
```
|
||||
|
||||
```text
|
||||
id: shelter.drainage.001
|
||||
topic: shelter.drainage
|
||||
question: Why avoid placing shelter in a drainage path?
|
||||
answers: A) Water can pool or flow through it. B) It makes tools sharper. C) It prevents all wind.
|
||||
correct: A
|
||||
feedback: Stable, drained ground improves shelter reliability.
|
||||
```
|
||||
|
||||
```text
|
||||
id: injury.bleeding.001
|
||||
topic: injury.bleeding
|
||||
question: What should you do before heavy work while bleeding?
|
||||
answers: A) Treat bleeding and rest if possible. B) Ignore it and sprint. C) Stand in smoke.
|
||||
correct: A
|
||||
feedback: Bleeding and exhaustion make further mistakes and injury more likely.
|
||||
```
|
||||
|
||||
```text
|
||||
id: resource.fiber.001
|
||||
topic: resource.fiber
|
||||
question: Why is fiber useful early?
|
||||
answers: A) Binding, panels, and shelter parts. B) It replaces all water. C) It stops night.
|
||||
correct: A
|
||||
feedback: Fiber is a basic binding material for primitive crafting and shelter.
|
||||
```
|
||||
|
||||
Question-bank rule: elementary questions should be short, practical, and tied to
|
||||
actions the player can immediately recognize in the MVP.
|
||||
|
||||
## When Deeper Questions Matter
|
||||
|
||||
Deeper questions should not decide whether a new player can survive the first
|
||||
night. They should matter when the player is trying to do better, teach others,
|
||||
or enter more complex branches.
|
||||
|
||||
Quality improvements:
|
||||
|
||||
- Use deeper checks when the player wants better yield, stronger construction,
|
||||
more durable tools, safer food, better medicine, or more efficient work.
|
||||
|
||||
Safer work:
|
||||
|
||||
- Use deeper checks when incorrect assumptions could create fire spread, injury,
|
||||
sickness, structural failure, bad weather exposure, or wasted scarce supplies.
|
||||
|
||||
Complex crafting:
|
||||
|
||||
- Use deeper checks for multi-stage recipes, material substitutions, tool
|
||||
maintenance, preserved food, medicines, buildings, and later machinery.
|
||||
|
||||
Teaching others:
|
||||
|
||||
- Use deeper checks when a player or NPC attempts to teach a concept, create a
|
||||
lesson note, train family/community members, or evaluate whether someone
|
||||
understood a dangerous task.
|
||||
|
||||
Advanced branches:
|
||||
|
||||
- Use deeper checks for farming, animal care, medicine, engineering, trade,
|
||||
navigation, governance, science, and future advanced technology.
|
||||
|
||||
Depth rule: deeper questions should improve mastery and responsibility. They
|
||||
should not be busywork for actions the character has already demonstrated
|
||||
through repeated safe practice.
|
||||
|
||||
## Exploit And Rote-Memorization Guardrails
|
||||
|
||||
The learning system should reward understanding and practice, not repetitive
|
||||
input farming or memorizing answer order.
|
||||
|
||||
Exploit farming risks:
|
||||
|
||||
- Repeating the same safe action for unlimited experience.
|
||||
- Creating harmless failures just to farm mistake feedback.
|
||||
- Spamming prompts or questions for repeated rewards.
|
||||
- Using alternate accounts or group work to duplicate teaching credit.
|
||||
- Performing actions with no meaningful cost, context, or outcome.
|
||||
|
||||
Guardrails:
|
||||
|
||||
- Apply diminishing returns to identical repeated actions.
|
||||
- Require meaningful context for experience: time, resource cost, risk,
|
||||
environmental variation, or consequence.
|
||||
- Track recent prompt/question exposure and suppress repeated rewards.
|
||||
- Give recovery credit only when the player takes a useful corrective action.
|
||||
- Separate "learned concept" from "perfect mastery" so one answer does not solve
|
||||
every future situation.
|
||||
- Prefer concept families and varied wording over fixed answer-order memorizing.
|
||||
|
||||
Anti-exploit rule: rewards should come from meaningful decisions, varied
|
||||
practice, and good recovery, not from low-cost repetition.
|
||||
|
||||
## Knowledge Persistence Requirements
|
||||
|
||||
Knowledge and skill state must survive save/load, server restarts, and future
|
||||
character handoff systems without resetting learning progress or tutorial
|
||||
fatigue.
|
||||
|
||||
Persisted state:
|
||||
|
||||
- Stable knowledge profile ID linked to the player character or NPC.
|
||||
- Learned concepts by stable concept ID.
|
||||
- Practical skill experience by taxonomy domain.
|
||||
- Mastery/confidence tier per topic, separate from raw experience.
|
||||
- Failed attempts that produced useful feedback, including cause category,
|
||||
recovery action, and recent cooldown state.
|
||||
- Tutorial and contextual prompt state: seen, dismissed, repeated, snoozed, and
|
||||
next eligible display time.
|
||||
- Optional knowledge checks answered, skipped, failed, or recently displayed.
|
||||
- Teaching, observation, and shared-work learning events that granted credit.
|
||||
- Schema version, migration marker, and source build/version for future cleanup.
|
||||
|
||||
Save/load rules:
|
||||
|
||||
- The server is authoritative for writes in multiplayer.
|
||||
- Client UI may cache prompt visibility, but save data comes from validated
|
||||
server state.
|
||||
- Save enough recent action and prompt history to prevent reset-based farming.
|
||||
- Keep learned concepts stable across wording changes in the question bank.
|
||||
- Preserve failed attempts long enough to support better feedback and avoid
|
||||
repeating the same hint every few seconds.
|
||||
- Store tutorial state per character/profile, not globally across all worlds.
|
||||
- Never store private credentials, personal account data, or real-world source
|
||||
notes in player save records.
|
||||
|
||||
Persistence rule: learning state should make a returning character feel
|
||||
continuous while giving designers enough history to tune teaching, feedback, and
|
||||
anti-exploit behavior.
|
||||
|
||||
## Multiplayer Learning Rules
|
||||
|
||||
Learning in multiplayer should make cooperation valuable without turning nearby
|
||||
players into passive experience sources.
|
||||
|
||||
Teaching:
|
||||
|
||||
- The server validates teaching credit.
|
||||
- The teacher must know the concept, have enough practical experience, or
|
||||
demonstrate the action in the current context.
|
||||
- The learner must be nearby, attentive, and eligible for the concept.
|
||||
- Teaching grants bounded awareness or practice credit, not instant mastery.
|
||||
- Dangerous concepts require supervised practice before full confidence is
|
||||
awarded.
|
||||
|
||||
Observation:
|
||||
|
||||
- Nearby observation can unlock awareness of a concept when the action is
|
||||
visible and relevant.
|
||||
- Passive observation grants less credit than direct practice.
|
||||
- Repeated passive observation has diminishing returns.
|
||||
- Observation does not grant credit while idle, disconnected, hidden from the
|
||||
action, or outside the relevant range.
|
||||
|
||||
Shared work:
|
||||
|
||||
- Group tasks can grant role-specific experience to active contributors.
|
||||
- Roles should be explicit enough to audit later: gatherer, builder, fire
|
||||
watcher, first aid helper, scout, teacher, cook, hauler, or defender.
|
||||
- Failed group work can teach useful feedback when a player helps diagnose or
|
||||
correct the problem.
|
||||
- Idle proximity, item handoff spam, and repeated low-risk loops should not
|
||||
grant shared-work credit.
|
||||
|
||||
Group skill benefits:
|
||||
|
||||
- Skilled contributors can reduce risk, improve quality, increase speed, or
|
||||
make warnings more visible for the group task.
|
||||
- Benefits should be capped so one expert does not remove all challenge.
|
||||
- Benefits apply only while the contributor is present, equipped, and actively
|
||||
helping.
|
||||
- No global aura: skill benefits do not apply across the map or while logged
|
||||
off unless a future offline-simulation rule explicitly grants protection.
|
||||
|
||||
Network rules:
|
||||
|
||||
- Client learning requests are hints only.
|
||||
- Server authority validates distance, visibility, participation, role,
|
||||
cooldown, tools, environmental context, and outcome before granting credit.
|
||||
- Teaching, observation, and shared-work events must be persisted for tuning and
|
||||
anti-exploit review.
|
||||
|
||||
Multiplayer rule: cooperation should transfer awareness, improve group outcomes,
|
||||
and reward active contribution without bypassing practice, risk, or context.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Investor Readiness Audit - 2026-05-19
|
||||
|
||||
## Current Verdict
|
||||
|
||||
The current build should not be presented as investor visual MVP until it passes
|
||||
a real-GPU packaged-client visual and stability check. The systems are further
|
||||
along than the presentation, but the player-facing first impression is still at
|
||||
risk.
|
||||
|
||||
## Findings
|
||||
|
||||
- Packaged crash logs show a Slate/UI paint crash while the character-selection
|
||||
frontend is active. The crash callstack centers on `SButton::OnPaint` and
|
||||
`FSlateDrawElement::MakeBox`, so the immediate fix removes custom button brush
|
||||
styling, defers frontend actions to the next tick, and shortens the investor
|
||||
path from character selection directly into Ground Zero.
|
||||
- The project content library still contains only placeholder meshes plus the
|
||||
Manny/Quinn mannequin assets for characters. The current character and object
|
||||
visuals are proxy-quality, not final investor-quality realism.
|
||||
- Ground Zero editor verification confirms landscape, water/resource actors,
|
||||
foliage variation actors, and environment materials exist, but this does not
|
||||
prove the packaged executable looks good through the real GPU path.
|
||||
- Cooked runtime logs warned that tree, shrub, and grass materials were missing
|
||||
instanced-static-mesh usage. Those flags are now set on the assets and in the
|
||||
repeatable map setup script.
|
||||
- The generated investor roadmap/report can show old milestone completion state
|
||||
if it is not regenerated from the current roadmap after fixes. The source
|
||||
roadmap still contains explicit visual QA acceptance items that remain open
|
||||
until real packaged screenshots or clips are captured.
|
||||
|
||||
## Required Before Investor-Ready Label
|
||||
|
||||
- Launch the packaged Windows demo through the real GPU desktop path.
|
||||
- Capture startup credits, character selection, first spawn, terrain, vegetation,
|
||||
water, campfire, shelter, pause menu, and save/quit.
|
||||
- Confirm selecting a character and entering Ground Zero does not crash.
|
||||
- Confirm the first player view is not ground/legs, the menu does not appear
|
||||
after gameplay starts, water is visible, foliage is visible, and debug-looking
|
||||
primitives are not the dominant read.
|
||||
- Replace proxy/mannequin art with real or production-directed free/internal art
|
||||
assets before calling the build visually investor-ready.
|
||||
|
||||
## Verification Completed In This Pass
|
||||
|
||||
- `verify_mvp_menu_input_and_quit_flow.py`
|
||||
- `verify_mvp_character_archetype_choice.py`
|
||||
- `verify_mvp_character_proxies.py`
|
||||
- `verify_mvp_frontend_umg_flow.py`
|
||||
- `verify_ground_zero_natural_environment_pass.py`
|
||||
- Windows package BuildCookRun completed successfully after the fixes.
|
||||
|
||||
## Remaining Risk
|
||||
|
||||
The packaged build still needs a real interactive visual pass through
|
||||
Sunshine/Moonlight or direct Windows display access. Static checks and editor
|
||||
commandlets are not enough to clear the user-reported crash and visual quality
|
||||
concerns.
|
||||
@@ -0,0 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set "PACKAGE_DIR=%~dp0..\Builds\WindowsDevelopment"
|
||||
set "GAME_EXE=%PACKAGE_DIR%\AgrarianGame\Binaries\Win64\AgrarianGame.exe"
|
||||
|
||||
cd /d "%PACKAGE_DIR%"
|
||||
"%GAME_EXE%" -windowed -ResX=1280 -ResY=720 -ExecCmds=AgrarianInvestorSmokeTest -nosound
|
||||
@@ -0,0 +1,43 @@
|
||||
import unreal
|
||||
|
||||
MATERIAL_PATHS = [
|
||||
"/Game/Agrarian/Materials/M_AGR_GZ_Tree_CoastalOak",
|
||||
"/Game/Agrarian/Materials/M_AGR_GZ_Shrub_CoyoteBrush",
|
||||
"/Game/Agrarian/Materials/M_AGR_GZ_Grass_DryCoastal",
|
||||
]
|
||||
|
||||
|
||||
def set_instanced_usage(material):
|
||||
applied = False
|
||||
for property_name in (
|
||||
"used_with_instanced_static_meshes",
|
||||
"bUsedWithInstancedStaticMeshes",
|
||||
):
|
||||
try:
|
||||
material.set_editor_property(property_name, True)
|
||||
applied = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return applied
|
||||
|
||||
|
||||
dirty_assets = []
|
||||
for material_path in MATERIAL_PATHS:
|
||||
material = unreal.EditorAssetLibrary.load_asset(material_path)
|
||||
if not material:
|
||||
raise RuntimeError(f"Missing material: {material_path}")
|
||||
|
||||
if not set_instanced_usage(material):
|
||||
raise RuntimeError(f"Could not set instanced static mesh usage on {material_path}")
|
||||
|
||||
unreal.MaterialEditingLibrary.recompile_material(material)
|
||||
dirty_assets.append(material_path)
|
||||
|
||||
for material_path in dirty_assets:
|
||||
if not unreal.EditorAssetLibrary.save_asset(material_path, only_if_is_dirty=False):
|
||||
raise RuntimeError(f"Failed to save updated material: {material_path}")
|
||||
|
||||
print("Updated instanced static mesh usage for Ground Zero foliage materials:")
|
||||
for material_path in dirty_assets:
|
||||
print(f" - {material_path}")
|
||||
@@ -30,9 +30,9 @@ PLACEHOLDER_MESHES = {
|
||||
for name in PLACEHOLDER_MESH_SOURCES
|
||||
}
|
||||
FOLIAGE_MESHES = {
|
||||
"tree": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"],
|
||||
"shrub": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cube"],
|
||||
"grass": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"],
|
||||
"tree": "/Engine/BasicShapes/Cone",
|
||||
"shrub": "/Engine/BasicShapes/Sphere",
|
||||
"grass": "/Engine/BasicShapes/Plane",
|
||||
}
|
||||
VARIATION_MESHES = {
|
||||
"cube": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cube"],
|
||||
@@ -40,28 +40,33 @@ VARIATION_MESHES = {
|
||||
"cylinder": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"],
|
||||
"quarter_cylinder": PLACEHOLDER_MESHES["SM_AGR_Placeholder_QuarterCylinder"],
|
||||
"plane": PLACEHOLDER_MESHES["SM_AGR_Placeholder_Plane"],
|
||||
"sphere": "/Engine/BasicShapes/Sphere",
|
||||
"cone": "/Engine/BasicShapes/Cone",
|
||||
}
|
||||
MATERIAL_FOLDER = "/Game/Agrarian/Materials"
|
||||
ENVIRONMENT_MATERIALS = {
|
||||
"terrain": {
|
||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Terrain_CoastalScrub",
|
||||
"color": unreal.LinearColor(0.28, 0.24, 0.16, 1.0),
|
||||
"color": unreal.LinearColor(0.16, 0.23, 0.12, 1.0),
|
||||
"roughness": 0.92,
|
||||
},
|
||||
"tree": {
|
||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Tree_CoastalOak",
|
||||
"color": unreal.LinearColor(0.18, 0.31, 0.16, 1.0),
|
||||
"color": unreal.LinearColor(0.07, 0.18, 0.06, 1.0),
|
||||
"roughness": 0.88,
|
||||
"used_with_instanced_static_meshes": True,
|
||||
},
|
||||
"shrub": {
|
||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Shrub_CoyoteBrush",
|
||||
"color": unreal.LinearColor(0.31, 0.39, 0.20, 1.0),
|
||||
"color": unreal.LinearColor(0.15, 0.28, 0.10, 1.0),
|
||||
"roughness": 0.9,
|
||||
"used_with_instanced_static_meshes": True,
|
||||
},
|
||||
"grass": {
|
||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Grass_DryCoastal",
|
||||
"color": unreal.LinearColor(0.47, 0.42, 0.23, 1.0),
|
||||
"color": unreal.LinearColor(0.32, 0.34, 0.13, 1.0),
|
||||
"roughness": 0.95,
|
||||
"used_with_instanced_static_meshes": True,
|
||||
},
|
||||
"wood_resource": {
|
||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Wood_Resource",
|
||||
@@ -85,7 +90,7 @@ ENVIRONMENT_MATERIALS = {
|
||||
},
|
||||
"fresh_water": {
|
||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_FreshWater",
|
||||
"color": unreal.LinearColor(0.08, 0.28, 0.38, 1.0),
|
||||
"color": unreal.LinearColor(0.02, 0.16, 0.30, 1.0),
|
||||
"roughness": 0.35,
|
||||
},
|
||||
}
|
||||
@@ -346,7 +351,7 @@ WEATHER_EXPOSURE_ZONES = [
|
||||
ENVIRONMENT_VARIATION_ACTORS = [
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_Tree_Canopy_01",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"mesh_key": "sphere",
|
||||
"material_key": "tree",
|
||||
"location_xy": unreal.Vector(-27500.0, 6900.0, 0.0),
|
||||
"z_offset": 390.0,
|
||||
@@ -364,7 +369,7 @@ ENVIRONMENT_VARIATION_ACTORS = [
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_Tree_Canopy_02",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"mesh_key": "sphere",
|
||||
"material_key": "tree",
|
||||
"location_xy": unreal.Vector(17600.0, 31800.0, 0.0),
|
||||
"z_offset": 430.0,
|
||||
@@ -382,7 +387,7 @@ ENVIRONMENT_VARIATION_ACTORS = [
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_Bush_Rounded_01",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"mesh_key": "sphere",
|
||||
"material_key": "shrub",
|
||||
"location_xy": unreal.Vector(-33400.0, -15200.0, 0.0),
|
||||
"z_offset": 70.0,
|
||||
@@ -391,7 +396,7 @@ ENVIRONMENT_VARIATION_ACTORS = [
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_Bush_Rounded_02",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"mesh_key": "sphere",
|
||||
"material_key": "shrub",
|
||||
"location_xy": unreal.Vector(30400.0, -3900.0, 0.0),
|
||||
"z_offset": 75.0,
|
||||
@@ -551,6 +556,78 @@ ENVIRONMENT_VARIATION_ACTORS = [
|
||||
"scale": unreal.Vector(0.07, 0.07, 1.15),
|
||||
"rotation": unreal.Rotator(0.0, -19.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_OakCanopy_01",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"material_key": "tree",
|
||||
"location_xy": unreal.Vector(-26200.0, -7200.0, 0.0),
|
||||
"z_offset": 410.0,
|
||||
"scale": unreal.Vector(2.2, 2.6, 1.55),
|
||||
"rotation": unreal.Rotator(0.0, 24.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_OakTrunk_01",
|
||||
"mesh_key": "cylinder",
|
||||
"material_key": "wood_resource",
|
||||
"location_xy": unreal.Vector(-26200.0, -7200.0, 0.0),
|
||||
"z_offset": 180.0,
|
||||
"scale": unreal.Vector(0.28, 0.28, 3.7),
|
||||
"rotation": unreal.Rotator(0.0, 24.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_Brush_01",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"material_key": "shrub",
|
||||
"location_xy": unreal.Vector(-19100.0, -8350.0, 0.0),
|
||||
"z_offset": 72.0,
|
||||
"scale": unreal.Vector(1.65, 1.25, 0.68),
|
||||
"rotation": unreal.Rotator(0.0, -14.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_Brush_02",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"material_key": "shrub",
|
||||
"location_xy": unreal.Vector(-23900.0, 1300.0, 0.0),
|
||||
"z_offset": 76.0,
|
||||
"scale": unreal.Vector(1.25, 1.95, 0.72),
|
||||
"rotation": unreal.Rotator(0.0, 39.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_GrassMat_01",
|
||||
"mesh_key": "plane",
|
||||
"material_key": "grass",
|
||||
"location_xy": unreal.Vector(-20500.0, -6400.0, 0.0),
|
||||
"z_offset": 15.0,
|
||||
"scale": unreal.Vector(3.8, 2.4, 1.0),
|
||||
"rotation": unreal.Rotator(0.0, 18.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_GrassMat_02",
|
||||
"mesh_key": "plane",
|
||||
"material_key": "grass",
|
||||
"location_xy": unreal.Vector(-24200.0, -1800.0, 0.0),
|
||||
"z_offset": 15.0,
|
||||
"scale": unreal.Vector(2.9, 3.7, 1.0),
|
||||
"rotation": unreal.Rotator(0.0, -31.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_Rock_01",
|
||||
"mesh_key": "quarter_cylinder",
|
||||
"material_key": "stone_resource",
|
||||
"location_xy": unreal.Vector(-18400.0, -2400.0, 0.0),
|
||||
"z_offset": 45.0,
|
||||
"scale": unreal.Vector(1.15, 1.55, 0.52),
|
||||
"rotation": unreal.Rotator(0.0, 66.0, 0.0),
|
||||
},
|
||||
{
|
||||
"label": "AGR_GZ_EnvVar_FirstLook_Rock_02",
|
||||
"mesh_key": "chamfer_cube",
|
||||
"material_key": "stone_resource",
|
||||
"location_xy": unreal.Vector(-27900.0, -3600.0, 0.0),
|
||||
"z_offset": 42.0,
|
||||
"scale": unreal.Vector(0.98, 0.82, 0.36),
|
||||
"rotation": unreal.Rotator(0.0, -52.0, 0.0),
|
||||
},
|
||||
]
|
||||
|
||||
RUIN_PLACEHOLDER_ACTORS = [
|
||||
@@ -604,7 +681,7 @@ RUIN_PLACEHOLDER_ACTORS = [
|
||||
|
||||
FOLIAGE_ZONES = {
|
||||
"trees": {
|
||||
"count": 64,
|
||||
"count": 96,
|
||||
"x_range": (-25000.0, 42000.0),
|
||||
"y_range": (-12000.0, 42000.0),
|
||||
"min_elevation_m": 18.0,
|
||||
@@ -612,7 +689,7 @@ FOLIAGE_ZONES = {
|
||||
"scale_range": (0.75, 1.35),
|
||||
},
|
||||
"shrubs": {
|
||||
"count": 148,
|
||||
"count": 220,
|
||||
"x_range": (-42000.0, 45000.0),
|
||||
"y_range": (-38000.0, 45000.0),
|
||||
"min_elevation_m": 7.0,
|
||||
@@ -620,7 +697,7 @@ FOLIAGE_ZONES = {
|
||||
"scale_range": (0.45, 1.05),
|
||||
},
|
||||
"grass": {
|
||||
"count": 260,
|
||||
"count": 420,
|
||||
"x_range": (-46000.0, 46000.0),
|
||||
"y_range": (-46000.0, 46000.0),
|
||||
"min_elevation_m": 4.0,
|
||||
@@ -735,6 +812,31 @@ def ensure_environment_materials():
|
||||
unreal.MaterialEditingLibrary.recompile_material(material)
|
||||
unreal.EditorAssetLibrary.save_asset(spec["path"])
|
||||
unreal.log(f"Created Ground Zero environment material: {spec['path']}")
|
||||
else:
|
||||
base_color = unreal.MaterialEditingLibrary.get_material_property_input_node(
|
||||
material, unreal.MaterialProperty.MP_BASE_COLOR
|
||||
)
|
||||
if base_color and hasattr(base_color, "set_editor_property"):
|
||||
try:
|
||||
base_color.set_editor_property("constant", spec["color"])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
roughness = unreal.MaterialEditingLibrary.get_material_property_input_node(
|
||||
material, unreal.MaterialProperty.MP_ROUGHNESS
|
||||
)
|
||||
if roughness and hasattr(roughness, "set_editor_property"):
|
||||
try:
|
||||
roughness.set_editor_property("r", spec["roughness"])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
unreal.MaterialEditingLibrary.recompile_material(material)
|
||||
unreal.EditorAssetLibrary.save_asset(spec["path"], only_if_is_dirty=False)
|
||||
if spec.get("used_with_instanced_static_meshes"):
|
||||
material.set_editor_property("used_with_instanced_static_meshes", True)
|
||||
unreal.MaterialEditingLibrary.recompile_material(material)
|
||||
unreal.EditorAssetLibrary.save_asset(spec["path"], only_if_is_dirty=False)
|
||||
created_or_loaded[key] = material
|
||||
|
||||
return created_or_loaded
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify 0.1 is complete and 0.2 readiness notes are present."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
REVIEW = ROOT / "Docs" / "CodebaseReadinessReview.md"
|
||||
|
||||
REQUIRED_ROADMAP_SNIPPETS = [
|
||||
"# Version 0.2 - Persistent Homesteading",
|
||||
"Engineering posture for 0.2:",
|
||||
"Define claim data schema with claim ID",
|
||||
"Define crop data asset schema",
|
||||
"Define animal data asset schema",
|
||||
"Define storage data schema",
|
||||
"Define structure piece data asset schema",
|
||||
"Define transaction record schema",
|
||||
"Convert the 0.1.R knowledge foundation into first data records",
|
||||
"## 0.2.I 0.1 Hardening And Technical Debt",
|
||||
"Split `AgrarianEditorAutomationLibrary.cpp`",
|
||||
"Create a grouped verifier runner",
|
||||
"Separate admin/dev console command authority",
|
||||
"## 0.2.J Homesteading Visual And Biome Realism Pass",
|
||||
"Replace tan/flat terrain presentation",
|
||||
"Add investor visual QA screenshots",
|
||||
]
|
||||
|
||||
REQUIRED_REVIEW_SNIPPETS = [
|
||||
"# Codebase Readiness Review",
|
||||
"All `0.1.A` through `0.1.R` roadmap checkboxes are complete.",
|
||||
"AgrarianEditorAutomationLibrary.cpp` is doing too much",
|
||||
"Low-Risk Cleanup Applied",
|
||||
"Removed duplicate `SavingAndQuit` branch logic",
|
||||
"0.2 Engineering Priorities",
|
||||
]
|
||||
|
||||
|
||||
def get_0_1_text(roadmap: str) -> str:
|
||||
start = roadmap.find("## 0.1.A ")
|
||||
end = roadmap.find("# Version 0.2")
|
||||
if start == -1 or end == -1 or end <= start:
|
||||
raise SystemExit("FAILED: could not isolate 0.1 roadmap section")
|
||||
return roadmap[start:end]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
roadmap = ROADMAP.read_text(encoding="utf-8")
|
||||
review = REVIEW.read_text(encoding="utf-8")
|
||||
|
||||
failures: list[str] = []
|
||||
|
||||
section_0_1 = get_0_1_text(roadmap)
|
||||
unchecked_0_1 = [
|
||||
line.strip()
|
||||
for line in section_0_1.splitlines()
|
||||
if line.strip().startswith("- [ ]")
|
||||
]
|
||||
if unchecked_0_1:
|
||||
failures.append("0.1 has unchecked items: " + "; ".join(unchecked_0_1[:5]))
|
||||
|
||||
for snippet in REQUIRED_ROADMAP_SNIPPETS:
|
||||
if snippet not in roadmap:
|
||||
failures.append(f"roadmap missing {snippet!r}")
|
||||
|
||||
for snippet in REQUIRED_REVIEW_SNIPPETS:
|
||||
if snippet not in review:
|
||||
failures.append(f"readiness review missing {snippet!r}")
|
||||
|
||||
if failures:
|
||||
raise SystemExit("FAILED: " + "; ".join(failures))
|
||||
|
||||
print("OK: 0.1 completion and 0.2 readiness are documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify deeper-question timing rules are documented."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOC = ROOT / "Docs" / "KnowledgeAndSkillFoundation.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
DOC: [
|
||||
"## When Deeper Questions Matter",
|
||||
"Quality improvements:",
|
||||
"Safer work:",
|
||||
"Complex crafting:",
|
||||
"Teaching others:",
|
||||
"Advanced branches:",
|
||||
"Depth rule:",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Define when deeper questions should matter: quality improvements, safer work, complex crafting, teaching others, and advanced branches.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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: deeper-question timing rules are documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify the MVP elementary survival question bank is documented."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOC = ROOT / "Docs" / "KnowledgeAndSkillFoundation.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
DOC: [
|
||||
"## MVP Elementary Survival Question Bank",
|
||||
"id: fire.clearance.001",
|
||||
"id: water.potable.001",
|
||||
"id: exposure.cold.001",
|
||||
"id: shelter.drainage.001",
|
||||
"id: injury.bleeding.001",
|
||||
"id: resource.fiber.001",
|
||||
"Question-bank rule:",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Add a small MVP question bank for elementary survival knowledge.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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: elementary survival question bank is documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -4,9 +4,9 @@ import unreal
|
||||
MAP_PATH = "/Game/Agrarian/Maps/L_GroundZeroTerrain_Test"
|
||||
FOLIAGE_LABEL = "AGR_GroundZeroFoliage_FirstPass"
|
||||
EXPECTED_COUNTS = {
|
||||
"trees": 42,
|
||||
"shrubs": 96,
|
||||
"grass": 180,
|
||||
"trees": 96,
|
||||
"shrubs": 220,
|
||||
"grass": 420,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ MATERIALS = {
|
||||
"fresh_water": "/Game/Agrarian/Materials/M_AGR_GZ_FreshWater",
|
||||
}
|
||||
EXPECTED_FOLIAGE_COUNTS = {
|
||||
"trees": 64,
|
||||
"shrubs": 148,
|
||||
"grass": 260,
|
||||
"trees": 96,
|
||||
"shrubs": 220,
|
||||
"grass": 420,
|
||||
}
|
||||
RESOURCE_MATERIALS = {
|
||||
"AGR_GZ_Wood": "wood_resource",
|
||||
@@ -29,8 +29,13 @@ RESOURCE_MATERIALS = {
|
||||
"AGR_GZ_FreshWaterSource": "fresh_water",
|
||||
}
|
||||
VARIATION_PREFIX = "AGR_GZ_EnvVar_"
|
||||
EXPECTED_VARIATION_COUNT = 23
|
||||
EXPECTED_VARIATION_COUNT = 31
|
||||
EXPECTED_VARIATION_MATERIALS = {
|
||||
"AGR_GZ_EnvVar_FirstLook_OakCanopy": "tree",
|
||||
"AGR_GZ_EnvVar_FirstLook_OakTrunk": "wood_resource",
|
||||
"AGR_GZ_EnvVar_FirstLook_Brush": "shrub",
|
||||
"AGR_GZ_EnvVar_FirstLook_GrassMat": "grass",
|
||||
"AGR_GZ_EnvVar_FirstLook_Rock": "stone_resource",
|
||||
"AGR_GZ_EnvVar_Tree_Canopy": "tree",
|
||||
"AGR_GZ_EnvVar_Tree_Trunk": "wood_resource",
|
||||
"AGR_GZ_EnvVar_Bush": "shrub",
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify knowledge and skill persistence requirements are documented."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOC = ROOT / "Docs" / "KnowledgeAndSkillFoundation.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
DOC: [
|
||||
"## Knowledge Persistence Requirements",
|
||||
"Stable knowledge profile ID",
|
||||
"Learned concepts by stable concept ID.",
|
||||
"Practical skill experience by taxonomy domain.",
|
||||
"Failed attempts that produced useful feedback",
|
||||
"Tutorial and contextual prompt state",
|
||||
"Optional knowledge checks answered, skipped, failed, or recently displayed.",
|
||||
"Teaching, observation, and shared-work learning events",
|
||||
"Schema version, migration marker",
|
||||
"The server is authoritative for writes in multiplayer.",
|
||||
"Persistence rule:",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Add persistence requirements for knowledge, skill experience, learned concepts, failed attempts, and tutorial state.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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: knowledge persistence requirements are documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify exploit-farming and rote-memorization guardrails are documented."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOC = ROOT / "Docs" / "KnowledgeAndSkillFoundation.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
DOC: [
|
||||
"## Exploit And Rote-Memorization Guardrails",
|
||||
"Exploit farming risks:",
|
||||
"Guardrails:",
|
||||
"diminishing returns",
|
||||
"meaningful context",
|
||||
"varied wording",
|
||||
"Anti-exploit rule:",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Add design notes for avoiding exploit farming and rote memorization.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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: learning exploit guardrails are documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify multiplayer learning rules are documented."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOC = ROOT / "Docs" / "KnowledgeAndSkillFoundation.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
DOC: [
|
||||
"## Multiplayer Learning Rules",
|
||||
"Teaching:",
|
||||
"The server validates teaching credit.",
|
||||
"Observation:",
|
||||
"Passive observation grants less credit than direct practice.",
|
||||
"Shared work:",
|
||||
"Group tasks can grant role-specific experience to active contributors.",
|
||||
"Group skill benefits:",
|
||||
"Benefits should be capped",
|
||||
"No global aura:",
|
||||
"Network rules:",
|
||||
"Server authority validates distance, visibility, participation, role,",
|
||||
"Multiplayer rule:",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Add multiplayer rules for teaching, observation, shared work, and group skill benefits.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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: multiplayer learning rules are documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -12,8 +12,6 @@ CONFIG = ROOT / "Config" / "DefaultGame.ini"
|
||||
DOC = ROOT / "Docs" / "Characters" / "MvpCharacterProxies.md"
|
||||
SETUP = ROOT / "Scripts" / "setup_mvp_character_proxies.py"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
MALE_MATERIAL = ROOT / "Content" / "Agrarian" / "Characters" / "Materials" / "M_AGR_CharacterProxy_Workwear_Male.uasset"
|
||||
FEMALE_MATERIAL = ROOT / "Content" / "Agrarian" / "Characters" / "Materials" / "M_AGR_CharacterProxy_Workwear_Female.uasset"
|
||||
|
||||
|
||||
def require(condition: bool, message: str) -> None:
|
||||
@@ -43,6 +41,11 @@ def main() -> None:
|
||||
"M_AGR_CharacterProxy_Workwear_Female",
|
||||
"SetSkeletalMesh",
|
||||
"SetMaterial",
|
||||
"MvpWorkwearTorsoProxy",
|
||||
"MvpWorkwearBackpackProxy",
|
||||
"MvpWorkwearBedrollProxy",
|
||||
"MvpWorkwearBootsProxy",
|
||||
"EnsureMvpPresentationComponent",
|
||||
"SelectedMvpCharacterProxyId = TEXT(\"male\")",
|
||||
"SelectedMvpCharacterProxyId = TEXT(\"female\")",
|
||||
"ApplyMvpCharacterProxyToPawn();",
|
||||
@@ -82,8 +85,6 @@ def main() -> None:
|
||||
"- [x] Add first realistic playable character proxies for the selected young adult male and female archetypes" in roadmap,
|
||||
"0.1.O character proxy roadmap item is not checked off",
|
||||
)
|
||||
require(MALE_MATERIAL.exists(), "male character proxy material asset is missing")
|
||||
require(FEMALE_MATERIAL.exists(), "female character proxy material asset is missing")
|
||||
|
||||
print("OK: MVP male/female playable character proxy flow is wired and documented.")
|
||||
|
||||
|
||||
@@ -37,9 +37,8 @@ def main() -> None:
|
||||
"UButton::StaticClass()",
|
||||
"UTextBlock::StaticClass()",
|
||||
"OnClicked.AddDynamic",
|
||||
"OnHovered.AddDynamic",
|
||||
"ButtonStyle.Hovered.TintColor",
|
||||
"ButtonStyle.Pressed.TintColor",
|
||||
"SetBackgroundColor",
|
||||
"DeferFrontendAction",
|
||||
"SetIsFocusable(true)",
|
||||
"SetKeyboardFocus()",
|
||||
"BackFromActiveScreen()",
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify the first subject content format is documented."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOC = ROOT / "Docs" / "KnowledgeAndSkillFoundation.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
REQUIRED = {
|
||||
DOC: [
|
||||
"## Subject Content Format",
|
||||
"`topic`",
|
||||
"`concepts`",
|
||||
"`difficulty_tier`",
|
||||
"`prerequisite_concepts`",
|
||||
"`in_game_effect`",
|
||||
"`practice_action`",
|
||||
"`source_note`",
|
||||
"topic: fire.clearance",
|
||||
"Format rule:",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Define the first subject content format: topic, concepts, difficulty tier, prerequisite concepts, in-game effect, practice action, and source note.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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: subject content format is documented.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "AgrarianPersistenceSubsystem.h"
|
||||
#include "AgrarianShelterActor.h"
|
||||
#include "AgrarianSurvivalComponent.h"
|
||||
#include "Camera/CameraActor.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
@@ -92,6 +93,7 @@ namespace
|
||||
? TEXT("/Game/Agrarian/Characters/Materials/M_AGR_CharacterProxy_Workwear_Female.M_AGR_CharacterProxy_Workwear_Female")
|
||||
: TEXT("/Game/Agrarian/Characters/Materials/M_AGR_CharacterProxy_Workwear_Male.M_AGR_CharacterProxy_Workwear_Male");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::BeginPlay()
|
||||
@@ -102,8 +104,7 @@ void AAgrarianGamePlayerController::BeginPlay()
|
||||
{
|
||||
if (MvpFrontendStartupDelaySeconds > 0.0f)
|
||||
{
|
||||
SetIgnoreMoveInput(true);
|
||||
SetIgnoreLookInput(true);
|
||||
SetMvpFrontendPresentationActive(true);
|
||||
SetInputMode(FInputModeUIOnly());
|
||||
bShowMouseCursor = false;
|
||||
GetWorldTimerManager().SetTimer(
|
||||
@@ -139,6 +140,22 @@ void AAgrarianGamePlayerController::BeginPlay()
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::AcknowledgePossession(APawn* P)
|
||||
{
|
||||
Super::AcknowledgePossession(P);
|
||||
|
||||
if (!IsLocalPlayerController())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyMvpCharacterProxyToPawn();
|
||||
if (bMvpFrontendPresentationActive)
|
||||
{
|
||||
SetMvpFrontendPresentationActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::SetupInputComponent()
|
||||
{
|
||||
Super::SetupInputComponent();
|
||||
@@ -204,6 +221,7 @@ void AAgrarianGamePlayerController::ShowMvpFrontend()
|
||||
{
|
||||
MvpFrontendWidget->AddToPlayerScreen(10);
|
||||
}
|
||||
SetMvpFrontendPresentationActive(true);
|
||||
SetInputMode(FInputModeUIOnly().SetWidgetToFocus(MvpFrontendWidget->TakeWidget()).SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock));
|
||||
bShowMouseCursor = true;
|
||||
SetIgnoreMoveInput(true);
|
||||
@@ -241,6 +259,7 @@ void AAgrarianGamePlayerController::ShowMvpPauseMenu()
|
||||
}
|
||||
|
||||
MvpFrontendWidget->SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
|
||||
SetMvpFrontendPresentationActive(true);
|
||||
SetInputMode(FInputModeUIOnly().SetWidgetToFocus(MvpFrontendWidget->TakeWidget()).SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock));
|
||||
bShowMouseCursor = true;
|
||||
SetIgnoreMoveInput(true);
|
||||
@@ -275,6 +294,132 @@ void AAgrarianGamePlayerController::HandleMvpEscapeInput()
|
||||
ShowMvpPauseMenu();
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::SetMvpFrontendPresentationActive(bool bNewActive)
|
||||
{
|
||||
bMvpFrontendPresentationActive = bNewActive;
|
||||
|
||||
SetIgnoreMoveInput(bNewActive);
|
||||
SetIgnoreLookInput(bNewActive);
|
||||
|
||||
APawn* ControlledPawn = GetPawn();
|
||||
if (ControlledPawn)
|
||||
{
|
||||
ControlledPawn->SetActorHiddenInGame(bNewActive);
|
||||
ControlledPawn->SetActorEnableCollision(!bNewActive);
|
||||
|
||||
if (ACharacter* ControlledCharacter = Cast<ACharacter>(ControlledPawn))
|
||||
{
|
||||
if (UCharacterMovementComponent* MovementComponent = ControlledCharacter->GetCharacterMovement())
|
||||
{
|
||||
if (bNewActive)
|
||||
{
|
||||
MovementComponent->DisableMovement();
|
||||
}
|
||||
else
|
||||
{
|
||||
MovementComponent->SetMovementMode(MOVE_Walking);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CacheAndApplyMvpHudSuppression(bNewActive);
|
||||
|
||||
if (bNewActive)
|
||||
{
|
||||
CreateOrUpdateMvpFrontendCamera();
|
||||
if (MvpFrontendCameraActor)
|
||||
{
|
||||
SetViewTarget(MvpFrontendCameraActor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ControlledPawn)
|
||||
{
|
||||
SetViewTarget(ControlledPawn);
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::CreateOrUpdateMvpFrontendCamera()
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const APawn* ControlledPawn = GetPawn();
|
||||
const FVector TargetLocation = ControlledPawn
|
||||
? ControlledPawn->GetActorLocation() + FVector(0.0f, 0.0f, 95.0f)
|
||||
: GroundZeroDeveloperTravelHomeLocation;
|
||||
const FVector CameraLocation = TargetLocation + FVector(-620.0f, -520.0f, 260.0f);
|
||||
const FRotator CameraRotation = (TargetLocation - CameraLocation).Rotation();
|
||||
|
||||
if (!MvpFrontendCameraActor)
|
||||
{
|
||||
FActorSpawnParameters SpawnParameters;
|
||||
SpawnParameters.Owner = this;
|
||||
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
MvpFrontendCameraActor = World->SpawnActor<ACameraActor>(ACameraActor::StaticClass(), CameraLocation, CameraRotation, SpawnParameters);
|
||||
if (MvpFrontendCameraActor)
|
||||
{
|
||||
MvpFrontendCameraActor->SetActorHiddenInGame(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MvpFrontendCameraActor->SetActorLocationAndRotation(CameraLocation, CameraRotation);
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::CacheAndApplyMvpHudSuppression(bool bSuppress)
|
||||
{
|
||||
AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>();
|
||||
if (!AgrarianHUD)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bSuppress)
|
||||
{
|
||||
if (!bCachedMvpHudState)
|
||||
{
|
||||
bCachedShowDebugHUD = AgrarianHUD->bShowDebugHUD;
|
||||
bCachedShowMvpHudFrame = AgrarianHUD->bShowMvpHudFrame;
|
||||
bCachedShowCriticalStatsHUD = AgrarianHUD->bShowCriticalStatsHUD;
|
||||
bCachedShowInventoryHUD = AgrarianHUD->bShowInventoryHUD;
|
||||
bCachedShowCraftingHUD = AgrarianHUD->bShowCraftingHUD;
|
||||
bCachedShowInteractionPrompt = AgrarianHUD->bShowInteractionPrompt;
|
||||
bCachedShowDeathRespawnUI = AgrarianHUD->bShowDeathRespawnUI;
|
||||
bCachedShowDebugDevMenu = AgrarianHUD->bShowDebugDevMenu;
|
||||
bCachedMvpHudState = true;
|
||||
}
|
||||
|
||||
AgrarianHUD->bShowDebugHUD = false;
|
||||
AgrarianHUD->bShowMvpHudFrame = false;
|
||||
AgrarianHUD->bShowCriticalStatsHUD = false;
|
||||
AgrarianHUD->bShowInventoryHUD = false;
|
||||
AgrarianHUD->bShowCraftingHUD = false;
|
||||
AgrarianHUD->bShowInteractionPrompt = false;
|
||||
AgrarianHUD->bShowDeathRespawnUI = false;
|
||||
AgrarianHUD->bShowDebugDevMenu = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bCachedMvpHudState)
|
||||
{
|
||||
AgrarianHUD->bShowDebugHUD = bCachedShowDebugHUD;
|
||||
AgrarianHUD->bShowMvpHudFrame = bCachedShowMvpHudFrame;
|
||||
AgrarianHUD->bShowCriticalStatsHUD = bCachedShowCriticalStatsHUD;
|
||||
AgrarianHUD->bShowInventoryHUD = bCachedShowInventoryHUD;
|
||||
AgrarianHUD->bShowCraftingHUD = bCachedShowCraftingHUD;
|
||||
AgrarianHUD->bShowInteractionPrompt = bCachedShowInteractionPrompt;
|
||||
AgrarianHUD->bShowDeathRespawnUI = bCachedShowDeathRespawnUI;
|
||||
AgrarianHUD->bShowDebugDevMenu = bCachedShowDebugDevMenu;
|
||||
bCachedMvpHudState = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::ApplyMvpCharacterProxyToPawn()
|
||||
{
|
||||
AAgrarianGameCharacter* AgrarianCharacter = GetPawn<AAgrarianGameCharacter>();
|
||||
@@ -297,6 +442,8 @@ void AAgrarianGamePlayerController::ApplyMvpCharacterProxyToPawn()
|
||||
MeshComponent->SetMaterial(MaterialIndex, ProxyMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
// Blockout clothing and pack geometry was removed because it read as broken placeholder art in the investor build.
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::AgrarianGrantItem(FName ItemId, int32 Quantity)
|
||||
@@ -558,6 +705,19 @@ void AAgrarianGamePlayerController::AgrarianSelectCharacter(FName Archetype)
|
||||
ClientMessage(TEXT("Usage: AgrarianSelectCharacter male|female"));
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::AgrarianCompleteFrontend()
|
||||
{
|
||||
ApplyMvpCharacterProxyToPawn();
|
||||
SetMvpFrontendPresentationActive(false);
|
||||
SetInputMode(FInputModeGameOnly());
|
||||
bShowMouseCursor = false;
|
||||
|
||||
if (const APawn* ControlledPawn = GetPawn())
|
||||
{
|
||||
SetControlRotation(FRotator(-8.0f, ControlledPawn->GetActorRotation().Yaw, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::AgrarianShowMvpScreen(FName ScreenName)
|
||||
{
|
||||
if (!MvpFrontendWidget)
|
||||
@@ -604,6 +764,78 @@ void AAgrarianGamePlayerController::AgrarianShowMvpScreen(FName ScreenName)
|
||||
ClientMessage(TEXT("Usage: AgrarianShowMvpScreen main|character|join|loading|saving"));
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::AgrarianInvestorSmokeTest(float CaptureDelaySeconds, float QuitDelaySeconds)
|
||||
{
|
||||
if (!IsLocalPlayerController())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const float ClampedCaptureDelaySeconds = FMath::Max(2.0f, CaptureDelaySeconds);
|
||||
const float ClampedQuitDelaySeconds = FMath::Max(ClampedCaptureDelaySeconds + 4.0f, QuitDelaySeconds);
|
||||
|
||||
FTimerDelegate EnterWorldDelegate;
|
||||
EnterWorldDelegate.BindWeakLambda(this, [this]()
|
||||
{
|
||||
if (!MvpFrontendWidget)
|
||||
{
|
||||
ShowMvpFrontend();
|
||||
}
|
||||
|
||||
SelectedMvpCharacterProxyId = TEXT("female");
|
||||
if (MvpFrontendWidget)
|
||||
{
|
||||
MvpFrontendWidget->SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||
}
|
||||
|
||||
AgrarianCompleteFrontend();
|
||||
if (AAgrarianDebugHUD* AgrarianHUD = GetHUD<AAgrarianDebugHUD>())
|
||||
{
|
||||
AgrarianHUD->bShowDebugHUD = false;
|
||||
AgrarianHUD->bShowCriticalStatsHUD = false;
|
||||
AgrarianHUD->bShowInventoryHUD = false;
|
||||
AgrarianHUD->bShowCraftingHUD = false;
|
||||
}
|
||||
|
||||
if (APawn* ControlledPawn = GetPawn())
|
||||
{
|
||||
const FRotator InvestorViewRotation(-4.0f, 42.0f, 0.0f);
|
||||
ControlledPawn->SetActorRotation(FRotator(0.0f, InvestorViewRotation.Yaw, 0.0f));
|
||||
SetControlRotation(InvestorViewRotation);
|
||||
}
|
||||
|
||||
ClientMessage(TEXT("Investor smoke test entered Ground Zero as the female MVP character proxy."));
|
||||
});
|
||||
|
||||
FTimerHandle EnterWorldTimerHandle;
|
||||
GetWorldTimerManager().SetTimer(EnterWorldTimerHandle, EnterWorldDelegate, 0.75f, false);
|
||||
|
||||
FTimerDelegate ScreenshotDelegate;
|
||||
ScreenshotDelegate.BindWeakLambda(this, [this]()
|
||||
{
|
||||
ClientMessage(TEXT("Investor smoke test capturing gameplay screenshot."));
|
||||
ConsoleCommand(TEXT("HighResShot 1"));
|
||||
});
|
||||
|
||||
FTimerHandle ScreenshotTimerHandle;
|
||||
GetWorldTimerManager().SetTimer(ScreenshotTimerHandle, ScreenshotDelegate, ClampedCaptureDelaySeconds, false);
|
||||
|
||||
FTimerDelegate QuitDelegate;
|
||||
QuitDelegate.BindWeakLambda(this, [this]()
|
||||
{
|
||||
ClientMessage(TEXT("Investor smoke test completed; quitting packaged client."));
|
||||
ConsoleCommand(TEXT("quit"));
|
||||
});
|
||||
|
||||
FTimerHandle QuitTimerHandle;
|
||||
GetWorldTimerManager().SetTimer(QuitTimerHandle, QuitDelegate, ClampedQuitDelaySeconds, false);
|
||||
|
||||
ClientMessage(FString::Printf(
|
||||
TEXT("Investor smoke test scheduled: screenshot in %.1fs, quit in %.1fs."),
|
||||
ClampedCaptureDelaySeconds,
|
||||
ClampedQuitDelaySeconds));
|
||||
}
|
||||
|
||||
void AAgrarianGamePlayerController::AgrarianTravel(float X, float Y, float Z)
|
||||
{
|
||||
ServerAgrarianTravel(FVector(X, Y, Z));
|
||||
|
||||
@@ -10,6 +10,7 @@ class UInputMappingContext;
|
||||
class UUserWidget;
|
||||
class UAgrarianMvpFrontendWidget;
|
||||
class AAgrarianShelterActor;
|
||||
class ACameraActor;
|
||||
|
||||
/**
|
||||
* Basic PlayerController class for a third person game
|
||||
@@ -67,9 +68,27 @@ protected:
|
||||
void HandleMvpBackInput();
|
||||
void HandleMvpEscapeInput();
|
||||
void ApplyMvpCharacterProxyToPawn();
|
||||
void SetMvpFrontendPresentationActive(bool bNewActive);
|
||||
void CreateOrUpdateMvpFrontendCamera();
|
||||
void CacheAndApplyMvpHudSuppression(bool bSuppress);
|
||||
virtual void AcknowledgePossession(APawn* P) override;
|
||||
|
||||
FName SelectedMvpCharacterProxyId = TEXT("male");
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<ACameraActor> MvpFrontendCameraActor;
|
||||
|
||||
bool bMvpFrontendPresentationActive = false;
|
||||
bool bCachedMvpHudState = false;
|
||||
bool bCachedShowDebugHUD = true;
|
||||
bool bCachedShowMvpHudFrame = true;
|
||||
bool bCachedShowCriticalStatsHUD = true;
|
||||
bool bCachedShowInventoryHUD = true;
|
||||
bool bCachedShowCraftingHUD = true;
|
||||
bool bCachedShowInteractionPrompt = true;
|
||||
bool bCachedShowDeathRespawnUI = true;
|
||||
bool bCachedShowDebugDevMenu = false;
|
||||
|
||||
public:
|
||||
UFUNCTION(Exec)
|
||||
void AgrarianGrantItem(FName ItemId, int32 Quantity);
|
||||
@@ -128,9 +147,15 @@ public:
|
||||
UFUNCTION(Exec)
|
||||
void AgrarianSelectCharacter(FName Archetype);
|
||||
|
||||
UFUNCTION(Exec)
|
||||
void AgrarianCompleteFrontend();
|
||||
|
||||
UFUNCTION(Exec)
|
||||
void AgrarianShowMvpScreen(FName ScreenName);
|
||||
|
||||
UFUNCTION(Exec)
|
||||
void AgrarianInvestorSmokeTest(float CaptureDelaySeconds = 6.0f, float QuitDelaySeconds = 18.0f);
|
||||
|
||||
UFUNCTION(Exec)
|
||||
void AgrarianTravel(float X, float Y, float Z);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ AAgrarianMapBoundaryVolume::AAgrarianMapBoundaryVolume()
|
||||
RootComponent = BoundaryVolume;
|
||||
BoundaryVolume->SetBoxExtent(FVector(50000.0f, 50000.0f, 25000.0f));
|
||||
BoundaryVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
BoundaryVolume->SetHiddenInGame(false);
|
||||
BoundaryVolume->SetHiddenInGame(true);
|
||||
BoundaryVolume->ShapeColor = FColor::Yellow;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,32 +9,14 @@
|
||||
#include "Components/HorizontalBox.h"
|
||||
#include "Components/HorizontalBoxSlot.h"
|
||||
#include "Components/SizeBox.h"
|
||||
#include "Components/Slider.h"
|
||||
#include "Components/TextBlock.h"
|
||||
#include "Components/VerticalBox.h"
|
||||
#include "Components/VerticalBoxSlot.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Styling/CoreStyle.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
FButtonStyle MakeAgrarianButtonStyle(const FLinearColor& NormalColor, const FLinearColor& HoveredColor)
|
||||
{
|
||||
FButtonStyle ButtonStyle = FCoreStyle::Get().GetWidgetStyle<FButtonStyle>(TEXT("Button"));
|
||||
ButtonStyle.Normal.TintColor = FSlateColor(NormalColor);
|
||||
ButtonStyle.Hovered.TintColor = FSlateColor(HoveredColor);
|
||||
ButtonStyle.Pressed.TintColor = FSlateColor(FLinearColor(
|
||||
FMath::Min(HoveredColor.R + 0.12f, 1.0f),
|
||||
FMath::Min(HoveredColor.G + 0.12f, 1.0f),
|
||||
FMath::Min(HoveredColor.B + 0.12f, 1.0f),
|
||||
HoveredColor.A));
|
||||
return ButtonStyle;
|
||||
}
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
@@ -50,21 +32,18 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
|
||||
const FKey Key = InKeyEvent.GetKey();
|
||||
if (Key == EKeys::Left || Key == EKeys::A)
|
||||
{
|
||||
PlayUiSound(UiSelectionSound);
|
||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
if (Key == EKeys::Right || Key == EKeys::D)
|
||||
{
|
||||
PlayUiSound(UiSelectionSound);
|
||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
if (Key == EKeys::Enter || Key == EKeys::SpaceBar)
|
||||
{
|
||||
PlayUiSound(UiConfirmSound);
|
||||
ConfirmActiveScreen();
|
||||
return FReply::Handled();
|
||||
}
|
||||
@@ -74,14 +53,12 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
|
||||
const FKey Key = InKeyEvent.GetKey();
|
||||
if (Key == EKeys::Enter || Key == EKeys::SpaceBar)
|
||||
{
|
||||
PlayUiSound(UiConfirmSound);
|
||||
ConfirmActiveScreen();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
if (Key == EKeys::BackSpace || Key == EKeys::Escape)
|
||||
{
|
||||
PlayUiSound(UiBackSound);
|
||||
BackFromActiveScreen();
|
||||
return FReply::Handled();
|
||||
}
|
||||
@@ -91,7 +68,6 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
|
||||
const FKey Key = InKeyEvent.GetKey();
|
||||
if (Key == EKeys::Enter || Key == EKeys::SpaceBar)
|
||||
{
|
||||
PlayUiSound(UiConfirmSound);
|
||||
ConfirmActiveScreen();
|
||||
return FReply::Handled();
|
||||
}
|
||||
@@ -101,14 +77,12 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
|
||||
const FKey Key = InKeyEvent.GetKey();
|
||||
if (Key == EKeys::Enter || Key == EKeys::SpaceBar || Key == EKeys::Escape)
|
||||
{
|
||||
PlayUiSound(UiConfirmSound);
|
||||
ConfirmActiveScreen();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
if (Key == EKeys::Q)
|
||||
{
|
||||
PlayUiSound(UiSaveQuitSound);
|
||||
SaveAndQuit();
|
||||
return FReply::Handled();
|
||||
}
|
||||
@@ -185,7 +159,7 @@ void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
|
||||
{
|
||||
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
|
||||
{
|
||||
SetActiveScreen(EAgrarianMvpFrontendScreen::JoinServer);
|
||||
CompleteFrontendFlow();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -211,11 +185,6 @@ void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
|
||||
CompleteFrontendFlow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveScreen == EAgrarianMvpFrontendScreen::SavingAndQuit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::ReturnFromActiveScreen()
|
||||
@@ -236,13 +205,14 @@ void UAgrarianMvpFrontendWidget::CompleteFrontendFlow()
|
||||
{
|
||||
if (APlayerController* PlayerController = GetOwningPlayer())
|
||||
{
|
||||
if (ActiveScreen == EAgrarianMvpFrontendScreen::Loading)
|
||||
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection || ActiveScreen == EAgrarianMvpFrontendScreen::Loading)
|
||||
{
|
||||
PlayerController->ConsoleCommand(SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale
|
||||
? TEXT("AgrarianSelectCharacter female")
|
||||
: TEXT("AgrarianSelectCharacter male"));
|
||||
}
|
||||
|
||||
PlayerController->ConsoleCommand(TEXT("AgrarianCompleteFrontend"));
|
||||
PlayerController->SetInputMode(FInputModeGameOnly());
|
||||
PlayerController->bShowMouseCursor = false;
|
||||
PlayerController->SetIgnoreMoveInput(false);
|
||||
@@ -300,35 +270,9 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
||||
AddText(Panel, FText::FromString(TEXT("Gameplay is paused while this menu is active.")), FMath::RoundToInt(22.0f * Scale), false, MutedTextColor, 72.0f * Scale);
|
||||
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Resume")), ButtonColor, ButtonHoverColor, 16.0f * Scale);
|
||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
||||
|
||||
UButton* QuitButton = AddButton(Panel, FText::FromString(TEXT("Save & Quit")), QuitButtonColor, FLinearColor(0.58f, 0.28f, 0.22f, 1.0f), 34.0f * Scale);
|
||||
QuitButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked);
|
||||
AddText(Panel, FText::FromString(TEXT("Audio")), FMath::RoundToInt(18.0f * Scale), true, AccentColor, 8.0f * Scale);
|
||||
if (USlider* Slider = AddVolumeSlider(Panel, FText::FromString(TEXT("Master")), MasterVolume, 8.0f * Scale))
|
||||
{
|
||||
Slider->OnValueChanged.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleMasterVolumeChanged);
|
||||
}
|
||||
if (USlider* Slider = AddVolumeSlider(Panel, FText::FromString(TEXT("Ambient")), AmbientVolume, 8.0f * Scale))
|
||||
{
|
||||
Slider->OnValueChanged.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleAmbientVolumeChanged);
|
||||
}
|
||||
if (USlider* Slider = AddVolumeSlider(Panel, FText::FromString(TEXT("Weather")), WeatherVolume, 8.0f * Scale))
|
||||
{
|
||||
Slider->OnValueChanged.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleWeatherVolumeChanged);
|
||||
}
|
||||
if (USlider* Slider = AddVolumeSlider(Panel, FText::FromString(TEXT("Effects")), EffectsVolume, 8.0f * Scale))
|
||||
{
|
||||
Slider->OnValueChanged.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleEffectsVolumeChanged);
|
||||
}
|
||||
if (USlider* Slider = AddVolumeSlider(Panel, FText::FromString(TEXT("Wildlife")), WildlifeVolume, 8.0f * Scale))
|
||||
{
|
||||
Slider->OnValueChanged.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleWildlifeVolumeChanged);
|
||||
}
|
||||
if (USlider* Slider = AddVolumeSlider(Panel, FText::FromString(TEXT("UI")), UiVolume, 24.0f * Scale))
|
||||
{
|
||||
Slider->OnValueChanged.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleUiVolumeChanged);
|
||||
}
|
||||
AddText(Panel, FText::FromString(TEXT("Escape opens this menu. Save & Quit writes the current world save before closing.")), FMath::RoundToInt(16.0f * Scale), false, MutedTextColor, 0.0f);
|
||||
return;
|
||||
}
|
||||
@@ -348,7 +292,7 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
||||
const bool bMaleSelected = SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultMale;
|
||||
const bool bFemaleSelected = SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale;
|
||||
UButton* MaleButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("MalePioneerButton"));
|
||||
MaleButton->SetStyle(MakeAgrarianButtonStyle(bMaleSelected ? ButtonHoverColor : SecondaryButtonColor, ButtonHoverColor));
|
||||
MaleButton->SetBackgroundColor(bMaleSelected ? ButtonHoverColor : SecondaryButtonColor);
|
||||
MaleButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked);
|
||||
UVerticalBox* MaleStack = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("MalePioneerStack"));
|
||||
MaleButton->SetContent(MaleStack);
|
||||
@@ -362,7 +306,7 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
||||
}
|
||||
|
||||
UButton* FemaleButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("FemalePioneerButton"));
|
||||
FemaleButton->SetStyle(MakeAgrarianButtonStyle(bFemaleSelected ? ButtonHoverColor : SecondaryButtonColor, ButtonHoverColor));
|
||||
FemaleButton->SetBackgroundColor(bFemaleSelected ? ButtonHoverColor : SecondaryButtonColor);
|
||||
FemaleButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleFemaleCharacterClicked);
|
||||
UVerticalBox* FemaleStack = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("FemalePioneerStack"));
|
||||
FemaleButton->SetContent(FemaleStack);
|
||||
@@ -375,9 +319,8 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
||||
FemaleSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||
}
|
||||
|
||||
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Continue")), ButtonColor, ButtonHoverColor, 10.0f * Scale);
|
||||
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Enter Ground Zero")), ButtonColor, ButtonHoverColor, 10.0f * Scale);
|
||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
||||
AddText(Panel, FText::Format(FText::FromString(TEXT("Selected {0}: {1}. Click a card or use Left/Right, then continue.")), GetSelectedRoleLabel(), GetSelectedCharacterLabel()), FMath::RoundToInt(15.0f * Scale), false, MutedTextColor, 0.0f);
|
||||
return;
|
||||
}
|
||||
@@ -397,9 +340,8 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
||||
}
|
||||
|
||||
PrimaryFocusButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("ContinueToLoadingButton"));
|
||||
PrimaryFocusButton->SetStyle(MakeAgrarianButtonStyle(ButtonColor, ButtonHoverColor));
|
||||
PrimaryFocusButton->SetBackgroundColor(ButtonColor);
|
||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
||||
UTextBlock* ContinueLabel = AddText(nullptr, FText::FromString(TEXT("Continue to loading")), FMath::RoundToInt(20.0f * Scale), true, TextColor, 0.0f);
|
||||
PrimaryFocusButton->SetContent(ContinueLabel);
|
||||
if (UButtonSlot* ContinueLabelSlot = Cast<UButtonSlot>(ContinueLabel->Slot))
|
||||
@@ -414,7 +356,7 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
||||
}
|
||||
|
||||
UButton* BackButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("BackButton"));
|
||||
BackButton->SetStyle(MakeAgrarianButtonStyle(SecondaryButtonColor, ButtonHoverColor));
|
||||
BackButton->SetBackgroundColor(SecondaryButtonColor);
|
||||
BackButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleBackClicked);
|
||||
UTextBlock* BackLabel = AddText(nullptr, FText::FromString(TEXT("Back")), FMath::RoundToInt(20.0f * Scale), true, TextColor, 0.0f);
|
||||
BackButton->SetContent(BackLabel);
|
||||
@@ -447,7 +389,6 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
||||
AddText(Panel, FText::Format(FText::FromString(TEXT("{0}: {1} | Server: {2}")), GetSelectedRoleLabel(), GetSelectedCharacterLabel(), JoinServerAddress), FMath::RoundToInt(18.0f * Scale), false, TextColor, 34.0f * Scale);
|
||||
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Enter Ground Zero")), ButtonColor, ButtonHoverColor, 14.0f * Scale);
|
||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
||||
AddText(Panel, FText::FromString(TEXT("Click or press Enter to close the MVP menu and begin testing.")), FMath::RoundToInt(15.0f * Scale), false, MutedTextColor, 0.0f);
|
||||
}
|
||||
|
||||
@@ -474,7 +415,7 @@ UTextBlock* UAgrarianMvpFrontendWidget::AddText(UVerticalBox* Parent, const FTex
|
||||
UButton* UAgrarianMvpFrontendWidget::AddButton(UVerticalBox* Parent, const FText& Text, const FLinearColor& NormalColor, const FLinearColor& HoveredColor, float BottomPadding)
|
||||
{
|
||||
UButton* Button = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass());
|
||||
Button->SetStyle(MakeAgrarianButtonStyle(NormalColor, HoveredColor));
|
||||
Button->SetBackgroundColor(NormalColor);
|
||||
Button->SetClickMethod(EButtonClickMethod::MouseDown);
|
||||
Button->SetTouchMethod(EButtonTouchMethod::Down);
|
||||
|
||||
@@ -501,78 +442,6 @@ UButton* UAgrarianMvpFrontendWidget::AddButton(UVerticalBox* Parent, const FText
|
||||
return Button;
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::PlayUiSound(USoundBase* Sound) const
|
||||
{
|
||||
if (Sound)
|
||||
{
|
||||
UGameplayStatics::PlaySound2D(this, Sound, FMath::Clamp(MasterVolume * UiVolume, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
USlider* UAgrarianMvpFrontendWidget::AddVolumeSlider(UVerticalBox* Parent, const FText& Label, float Value, float BottomPadding)
|
||||
{
|
||||
if (!Parent || !WidgetTree)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UHorizontalBox* Row = WidgetTree->ConstructWidget<UHorizontalBox>(UHorizontalBox::StaticClass());
|
||||
if (UVerticalBoxSlot* RowSlot = Parent->AddChildToVerticalBox(Row))
|
||||
{
|
||||
RowSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, BottomPadding));
|
||||
RowSlot->SetHorizontalAlignment(HAlign_Fill);
|
||||
}
|
||||
|
||||
UTextBlock* LabelText = AddText(nullptr, Label, 15, false, FLinearColor(0.82f, 0.90f, 0.76f, 1.0f), 0.0f);
|
||||
if (UHorizontalBoxSlot* LabelSlot = Row->AddChildToHorizontalBox(LabelText))
|
||||
{
|
||||
LabelSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||
LabelSlot->SetHorizontalAlignment(HAlign_Left);
|
||||
LabelSlot->SetVerticalAlignment(VAlign_Center);
|
||||
}
|
||||
|
||||
USlider* Slider = WidgetTree->ConstructWidget<USlider>(USlider::StaticClass());
|
||||
Slider->SetValue(FMath::Clamp(Value, 0.0f, 1.0f));
|
||||
if (UHorizontalBoxSlot* SliderSlot = Row->AddChildToHorizontalBox(Slider))
|
||||
{
|
||||
SliderSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||
SliderSlot->SetHorizontalAlignment(HAlign_Fill);
|
||||
SliderSlot->SetVerticalAlignment(VAlign_Center);
|
||||
}
|
||||
|
||||
return Slider;
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleMasterVolumeChanged(float Value)
|
||||
{
|
||||
MasterVolume = FMath::Clamp(Value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleAmbientVolumeChanged(float Value)
|
||||
{
|
||||
AmbientVolume = FMath::Clamp(Value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleWeatherVolumeChanged(float Value)
|
||||
{
|
||||
WeatherVolume = FMath::Clamp(Value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleEffectsVolumeChanged(float Value)
|
||||
{
|
||||
EffectsVolume = FMath::Clamp(Value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleWildlifeVolumeChanged(float Value)
|
||||
{
|
||||
WildlifeVolume = FMath::Clamp(Value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleUiVolumeChanged(float Value)
|
||||
{
|
||||
UiVolume = FMath::Clamp(Value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::FocusPrimaryButton()
|
||||
{
|
||||
if (PrimaryFocusButton)
|
||||
@@ -587,32 +456,66 @@ void UAgrarianMvpFrontendWidget::FocusPrimaryButton()
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked()
|
||||
{
|
||||
PlayUiSound(UiConfirmSound);
|
||||
ConfirmActiveScreen();
|
||||
DeferFrontendAction([this]()
|
||||
{
|
||||
ConfirmActiveScreen();
|
||||
});
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleBackClicked()
|
||||
{
|
||||
PlayUiSound(UiBackSound);
|
||||
BackFromActiveScreen();
|
||||
DeferFrontendAction([this]()
|
||||
{
|
||||
BackFromActiveScreen();
|
||||
});
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked()
|
||||
{
|
||||
PlayUiSound(UiSaveQuitSound);
|
||||
SaveAndQuit();
|
||||
DeferFrontendAction([this]()
|
||||
{
|
||||
SaveAndQuit();
|
||||
});
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked()
|
||||
{
|
||||
PlayUiSound(UiSelectionSound);
|
||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
||||
DeferFrontendAction([this]()
|
||||
{
|
||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
||||
});
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::HandleFemaleCharacterClicked()
|
||||
{
|
||||
PlayUiSound(UiSelectionSound);
|
||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||
DeferFrontendAction([this]()
|
||||
{
|
||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||
});
|
||||
}
|
||||
|
||||
void UAgrarianMvpFrontendWidget::DeferFrontendAction(TFunction<void()> Action)
|
||||
{
|
||||
if (!Action)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UWorld* World = GetWorld())
|
||||
{
|
||||
FTimerDelegate DeferredAction;
|
||||
DeferredAction.BindLambda([WeakThis = TWeakObjectPtr<UAgrarianMvpFrontendWidget>(this), Action = MoveTemp(Action)]() mutable
|
||||
{
|
||||
if (WeakThis.IsValid())
|
||||
{
|
||||
Action();
|
||||
}
|
||||
});
|
||||
World->GetTimerManager().SetTimerForNextTick(DeferredAction);
|
||||
return;
|
||||
}
|
||||
|
||||
Action();
|
||||
}
|
||||
|
||||
FText UAgrarianMvpFrontendWidget::GetSelectedCharacterLabel() const
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "AgrarianMvpFrontendWidget.generated.h"
|
||||
|
||||
class UButton;
|
||||
class USlider;
|
||||
class USoundBase;
|
||||
class UTextBlock;
|
||||
class UVerticalBox;
|
||||
|
||||
@@ -59,36 +57,6 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI")
|
||||
bool bUseHighContrast = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio")
|
||||
TObjectPtr<USoundBase> UiConfirmSound;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio")
|
||||
TObjectPtr<USoundBase> UiBackSound;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio")
|
||||
TObjectPtr<USoundBase> UiSelectionSound;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio")
|
||||
TObjectPtr<USoundBase> UiSaveQuitSound;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float MasterVolume = 1.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float AmbientVolume = 0.70f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float WeatherVolume = 0.75f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float EffectsVolume = 0.80f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float WildlifeVolume = 0.70f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI|Audio", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float UiVolume = 0.65f;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
|
||||
void SetActiveScreen(EAgrarianMvpFrontendScreen NewScreen);
|
||||
|
||||
@@ -122,26 +90,6 @@ private:
|
||||
void RebuildFrontendTree();
|
||||
UTextBlock* AddText(UVerticalBox* Parent, const FText& Text, int32 FontSize, bool bBold, const FLinearColor& Color, float BottomPadding);
|
||||
UButton* AddButton(UVerticalBox* Parent, const FText& Text, const FLinearColor& NormalColor, const FLinearColor& HoveredColor, float BottomPadding);
|
||||
void PlayUiSound(USoundBase* Sound) const;
|
||||
USlider* AddVolumeSlider(UVerticalBox* Parent, const FText& Label, float Value, float BottomPadding);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleMasterVolumeChanged(float Value);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleAmbientVolumeChanged(float Value);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleWeatherVolumeChanged(float Value);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleEffectsVolumeChanged(float Value);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleWildlifeVolumeChanged(float Value);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleUiVolumeChanged(float Value);
|
||||
|
||||
UFUNCTION()
|
||||
void FocusPrimaryButton();
|
||||
@@ -164,6 +112,8 @@ private:
|
||||
UFUNCTION()
|
||||
void HandleFemaleCharacterClicked();
|
||||
|
||||
void DeferFrontendAction(TFunction<void()> Action);
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UButton> PrimaryFocusButton;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Components/DirectionalLightComponent.h"
|
||||
#include "Components/ExponentialHeightFogComponent.h"
|
||||
#include "Components/SceneComponent.h"
|
||||
#include "Components/SkyAtmosphereComponent.h"
|
||||
#include "Components/SkyLightComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "ProfilingDebugging/CpuProfilerTrace.h"
|
||||
@@ -30,6 +31,10 @@ AAgrarianSkyLightingController::AAgrarianSkyLightingController()
|
||||
SkyLight->SetIntensity(ClearSkyLightIntensity);
|
||||
SkyLight->SetMobility(EComponentMobility::Movable);
|
||||
|
||||
SkyAtmosphere = CreateDefaultSubobject<USkyAtmosphereComponent>(TEXT("SkyAtmosphere"));
|
||||
SkyAtmosphere->SetupAttachment(SceneRoot);
|
||||
SkyAtmosphere->SetMobility(EComponentMobility::Movable);
|
||||
|
||||
HeightFog = CreateDefaultSubobject<UExponentialHeightFogComponent>(TEXT("HeightFog"));
|
||||
HeightFog->SetupAttachment(SceneRoot);
|
||||
HeightFog->SetFogDensity(ClearFogDensity);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
class UDirectionalLightComponent;
|
||||
class UExponentialHeightFogComponent;
|
||||
class USceneComponent;
|
||||
class USkyAtmosphereComponent;
|
||||
class USkyLightComponent;
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
@@ -32,6 +33,9 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Sky")
|
||||
TObjectPtr<USkyLightComponent> SkyLight;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Sky")
|
||||
TObjectPtr<USkyAtmosphereComponent> SkyAtmosphere;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Sky")
|
||||
TObjectPtr<UExponentialHeightFogComponent> HeightFog;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user