summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/M6Presentation.fs
blob: 65300f0ae8f6ecdd82bf0d73076406cf85308d2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
namespace LivingVillage.Desktop

open Microsoft.Xna.Framework
open LivingVillage.Kernel
open LivingVillage.Kernel.Sim

type M6DayNight =
    | Day
    | Night

type M6RenderProfile =
    { Mode: M6DayNight
      Background: Color
      WorldTint: Color
      Blend: float32
      LanternGlow: float32 }

module M6Presentation =

    let dayNightLabel mode : string =
        match mode with
        | Day -> "白天"
        | Night -> "夜晚"

    /// Continuous light blend: 0 = full day, 1 = full night. Ramps are deterministic
    /// pure functions of the simulation tick (dawn 4:00-6:00, dusk 20:00-22:00), so the
    /// boundaries agree with Sim.isNightTick (night >= 22:00 or < 6:00).
    let lightingBlend (tick: int64) : float32 =
        let hourTicks = Sim.ticksPerDay / 24L
        let hour = float32 (tick / hourTicks)
        let clamp01 v = if v < 0.0f then 0.0f elif v > 1.0f then 1.0f else v
        if hour >= 6.0f && hour < 20.0f then 0.0f
        elif hour >= 22.0f then 1.0f
        elif hour < 4.0f then 1.0f
        elif hour < 6.0f then clamp01 (1.0f - (hour - 4.0f) / 2.0f)
        else clamp01 ((hour - 20.0f) / 2.0f)

    let private lerp (a: int) (b: int) (blend: float32) : int =
        a + int (float32 (b - a) * blend)

    let backgroundAtBlend (blend: float32) : Color =
        Color(lerp 100 18 blend, lerp 160 25 blend, lerp 210 55 blend)

    let worldTintAtBlend (blend: float32) : Color =
        Color(lerp 255 118 blend, lerp 248 136 blend, lerp 224 184 blend)

    /// Pure day-phase lighting: deterministic tint/background plus lantern warm-glow boost.
    let lightingAtTick (tick: int64) : M6RenderProfile =
        let blend = lightingBlend tick
        { Mode = if blend < 0.5f then Day else Night
          Background = backgroundAtBlend blend
          Blend = blend
          WorldTint = worldTintAtBlend blend
          LanternGlow = blend }

    let profileAtTick (tick: int64) : M6RenderProfile = lightingAtTick tick

    let clockLabel (control: SimulationControl) : string =
        if control.Paused then
            "暂停"
        else
            match control.Speed with
            | OneX -> "1倍速"
            | TwoX -> "2倍速"
            | FiveX -> "5倍速"

    /// 倍速标签(语义别名,HUD 用)。
    let speedLabel (control: SimulationControl) : string = clockLabel control

    let private hourOf (tick: int64) : int =
        int ((tick / (Sim.ticksPerDay / 24L)) % 24L)

    /// 24 小时制的模拟时钟(HH:MM),由 tick 纯函数导出。
    let clockTimeLabel (tick: int64) : string =
        let hourTicks = Sim.ticksPerDay / 24L
        let hour = hourOf tick
        let minute = int ((tick % hourTicks) * 60L / hourTicks)
        sprintf "%02d:%02d" hour minute

    /// 时段标签:清晨/白天/黄昏/夜晚,与实际 tick 一致(纯函数)。
    let timePeriodLabel (tick: int64) : string =
        let hour = hourOf tick
        if hour < 5 then "夜晚"
        elif hour < 8 then "清晨"
        elif hour < 17 then "白天"
        elif hour < 20 then "黄昏"
        else "夜晚"