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
|
namespace LivingVillage.Desktop
open Microsoft.Xna.Framework
open LivingVillage.Kernel
open LivingVillage.Kernel.Sim
/// 顶部窄信息条 HUD 的纯布局/配色模型:只做确定性计算,实际绘制在 Game.fs。
/// 常驻状态(时段/时间/倍速/体力/钱币)压进一条窄带,绝不用整屏纯色或大黑块。
module HudLayout =
let barHeight = 36
let accentHeight = 3
let paddingX = 16
let segmentGap = 20
let textScale = 2
type Segment =
{ Label: string
Value: string }
let segmentText (segment: Segment) : string = sprintf "%s %s" segment.Label segment.Value
/// 常驻状态片段,数值全部取自世界真实状态,绝不编造。
let segments (world: World) (control: SimulationControl) : Segment list =
let needs = world.Avatar.Mind.Needs
[ { Label = "时段"; Value = M6Presentation.timePeriodLabel world.Tick }
{ Label = "时间"; Value = M6Presentation.clockTimeLabel world.Tick }
{ Label = "倍速"; Value = M6Presentation.speedLabel control }
{ Label = "体力"; Value = sprintf "%.0f" needs.Energy }
{ Label = "钱币"; Value = sprintf "%.0f" needs.Money } ]
/// 段文本(含标签)的像素宽度。
let segmentWidth (segment: Segment) : int =
FloatingPrompt.measureWidth (segmentText segment) textScale
/// 左对齐排布,返回 (起点 x, 宽度, 分段);纯函数,便于布局回归断言。
let layout (segments: Segment list) : (int * int * Segment) list =
let _, placed =
segments
|> List.fold
(fun (x, acc) segment ->
let width = segmentWidth segment
x + width + segmentGap, (x, width, segment) :: acc)
(paddingX, [])
List.rev placed
/// P26:常驻片段文本的 1px 描边偏移(上/下/左/右),保证白天/夜景下都可读。
let textOutlineOffsets : (int * int) list = [ (-1, 0); (1, 0); (0, -1); (0, 1) ]
/// P26:常驻片段的半透明底色药丸,贴合文字高度并在左右各留 6px;纯函数便于回归。
let segmentBackdrop (x: int) (width: int) : Microsoft.Xna.Framework.Rectangle =
let height = 8 * textScale + 10
let y = (barHeight - height) / 2
Microsoft.Xna.Framework.Rectangle(x - 6, y, width + 12, height)
type Palette =
{ BarTop: Color
BarBottom: Color
Accent: Color
Label: Color
Value: Color
SegmentBackdrop: Color
Outline: Color }
/// 白天暖色 / 夜晚(冬夜)冷色微调;均为半透明渐变窄条,无大黑块。
/// 每个常驻片段再叠一层半透明底色 + 1px 深色描边,双时段都能读清。
let palette (mode: M6DayNight) : Palette =
match mode with
| Day ->
{ BarTop = Color(40, 56, 62, 200)
BarBottom = Color(22, 34, 40, 200)
Accent = Color(212, 168, 96)
Label = Color(206, 220, 208)
Value = Color(242, 248, 234)
SegmentBackdrop = Color(8, 14, 20, 150)
Outline = Color(6, 10, 14, 255) }
| Night ->
{ BarTop = Color(22, 30, 48, 205)
BarBottom = Color(13, 19, 33, 205)
Accent = Color(120, 168, 196)
Label = Color(176, 196, 214)
Value = Color(228, 238, 246)
SegmentBackdrop = Color(4, 8, 16, 160)
Outline = Color(3, 6, 12, 255) }
|