summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/LivingVillage.Desktop.Tests/PrototypeTests.fs14
-rw-r--r--src/LivingVillage.Desktop/Game.fs26
-rw-r--r--src/LivingVillage.Desktop/M6Presentation.fs7
3 files changed, 36 insertions, 11 deletions
diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
index 4d2c895..b250824 100644
--- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
+++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
@@ -902,6 +902,20 @@ type PrototypeTests () =
Assert.AreEqual<int64>(6L * ticksPerHour, M6Presentation.startTickForHour 6.0)
[<TestMethod>]
+ member _.StartTickResolutionHonoursEvidenceHourThenDaylightThenMidnight () =
+ let ticksPerHour = Sim.ticksPerDay / 24L
+
+ // P17-FIX: the evidence hour must win on the sample/autoplay path too, otherwise
+ // StartNewGame re-created the world at 00:00 and dusk/day shots were actually night.
+ Assert.AreEqual<int64>(M6Presentation.startTickForHour 20.75, M6Presentation.resolveStartTick (Some 20.75) false)
+ Assert.AreEqual<int64>(int64 20 * ticksPerHour + ticksPerHour * 3L / 4L, M6Presentation.resolveStartTick (Some 20.75) false)
+ // Explicit hour overrides the daylight bootstrap.
+ Assert.AreEqual<int64>(12L * ticksPerHour, M6Presentation.resolveStartTick (Some 12.0) true)
+ // No override: daylight -> 6:00, otherwise midnight.
+ Assert.AreEqual<int64>(6L * ticksPerHour, M6Presentation.resolveStartTick None true)
+ Assert.AreEqual<int64>(0L, M6Presentation.resolveStartTick None false)
+
+ [<TestMethod>]
member _.TitleEntranceProgressIsDeterministicAndCompleteAfterTwoSeconds () =
// 120 frames at the fixed 60Hz step = 2s entrance, pure function of frame count.
Assert.AreEqual<float32>(1.0f, TitleScreen.entranceProgress 0L)
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index 4760d4e..765cea8 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -134,13 +134,16 @@ type LivingVillageGame() as this =
match Double.TryParse(value, Globalization.NumberStyles.Float, Globalization.CultureInfo.InvariantCulture) with
| true, hour -> Some hour
| _ -> None
+ // Single source of truth for the starting tick so the sample/autoplay path (which
+ // re-creates the world via StartNewGame) honours the evidence hour too.
+ let evidenceStartTick () =
+ M6Presentation.resolveStartTick startHourOverride daylightAutoplay
+ let withStartTick (world: World) : World =
+ let tick = evidenceStartTick ()
+ { world with Tick = tick; Time = float tick * Sim.dtSeconds }
let createInitialWorld () =
- let baseWorld = WorldBootstrap.initialWorldForDaylight daylightAutoplay 42UL Sim.npcCount
- match startHourOverride with
- | Some hour ->
- let tick = M6Presentation.startTickForHour hour
- { baseWorld with Tick = tick; Time = float tick * Sim.dtSeconds }
- | None -> baseWorld
+ WorldBootstrap.initialWorldForDaylight daylightAutoplay 42UL Sim.npcCount
+ |> withStartTick
let mutable world = createInitialWorld ()
let mutable camera = Vector2.Zero
let mutable avatarFacing = VillageArt.SouthFacing
@@ -223,17 +226,18 @@ type LivingVillageGame() as this =
this.CenterCamera()
member private this.StartNewGame (occupation: Occupation.Kind option) =
+ let startTick = evidenceStartTick ()
let state =
occupation
- |> Option.map (fun kind ->
- let startTick = if daylightAutoplay then 6L * Sim.ticksPerDay / 24L else 0L
- WorldBootstrap.occupationStateFor 42UL kind startTick)
- world <- WorldBootstrap.initialWorldWithOccupation daylightAutoplay 42UL Sim.npcCount state
+ |> Option.map (fun kind -> WorldBootstrap.occupationStateFor 42UL kind startTick)
+ world <-
+ WorldBootstrap.initialWorldWithOccupation daylightAutoplay 42UL Sim.npcCount state
+ |> withStartTick
this.ResetWorldView()
m5View <- { m5View with Task = state }
menu <- menu |> MenuState.setCurrentWorld true |> MenuState.enterGame
let occupationName = occupation |> Option.map Occupation.nameOf |> Option.defaultValue "none"
- printfn $"new-game=ok seed=42 npcs={Sim.npcCount} occupation={occupationName}"
+ printfn $"new-game=ok seed=42 npcs={Sim.npcCount} occupation={occupationName} tick={world.Tick}"
member private this.SaveWorld (prefix: string) : bool =
try
diff --git a/src/LivingVillage.Desktop/M6Presentation.fs b/src/LivingVillage.Desktop/M6Presentation.fs
index a64d494..752034a 100644
--- a/src/LivingVillage.Desktop/M6Presentation.fs
+++ b/src/LivingVillage.Desktop/M6Presentation.fs
@@ -104,6 +104,13 @@ module M6Presentation =
let startTickForHour (hour: float) : int64 =
int64 (hour * float (Sim.ticksPerDay / 24L))
+ /// Start-tick decision shared by bootstrap and new-game: an explicit evidence hour wins,
+ /// else daylight starts at 6:00, else midnight. Pure function, no side effects.
+ let resolveStartTick (startHour: float option) (startAtDaylight: bool) : int64 =
+ match startHour with
+ | Some hour -> startTickForHour hour
+ | None -> if startAtDaylight then 6L * Sim.ticksPerDay / 24L else 0L
+
let private hourOf (tick: int64) : int =
int ((tick / (Sim.ticksPerDay / 24L)) % 24L)