summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/Interaction.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-29 15:44:35 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-29 15:44:35 +0800
commitdce50b835235a103af1d8e6ec836fe12d841f135 (patch)
tree820d2a5f8a092e66e31b5ca06527302dd81735ed /src/LivingVillage.Desktop/Interaction.fs
parent21b03c043466c3cbce1d71ff055c9d71631b6073 (diff)
downloadliving-village-dce50b835235a103af1d8e6ec836fe12d841f135.tar.gz
p77: 日历卡/浮动提示药丸化并修复面板遮挡快捷栏
- DrawCalendarCard 改半透明底+1px 描边+accent 发丝线,日历卡微缩(52→48) - FloatingPrompt 新增 layoutWrapped/wrapText,锚定长文本逐字折行(不丢字、居中锚点、避让 HUD 带) - 新增 HudLayout.panelRect,DrawPanel 用其几何;DialogOverlay.bottomMargin 24→72 1280x720 下对话 rect 底 648、任务面板底 652,均 < hotbar 顶 660,不再压 3-8 号槽 - 新增 P77LayoutTests(4 尺寸不破版/不遮挡 + 真实长提示折行) - 证据 LV_P43/P44/P67_SHOT + scripts/analyze-p77.py(verdict PASS) 落 docs/evidence/p77-* - Desktop 361/361, Kernel 120/120, digest 953775FA...F7F6C 不变
Diffstat (limited to 'src/LivingVillage.Desktop/Interaction.fs')
-rw-r--r--src/LivingVillage.Desktop/Interaction.fs53
1 files changed, 53 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/Interaction.fs b/src/LivingVillage.Desktop/Interaction.fs
index 3a65e8e..5281180 100644
--- a/src/LivingVillage.Desktop/Interaction.fs
+++ b/src/LivingVillage.Desktop/Interaction.fs
@@ -211,6 +211,59 @@ module FloatingPrompt =
let y = max topSafe (min bottomSafe screenY)
{ X = x; Y = y; Width = width; Height = height }
+ /// P77:锚定提示支持长文本折行(逐字、不丢字),供真实用例(如「E 与河边的货郎交谈并查看货品」)。
+ let maxPromptWidth = 360
+
+ type PromptLayout =
+ { Lines: string list
+ X: int
+ Y: int
+ Width: int
+ Height: int }
+
+ let wrapText (maxWidth: int) (scale: int) (text: string) : string list =
+ if text = "" then
+ [ "" ]
+ else
+ let builder = System.Text.StringBuilder()
+ let mutable lines = []
+ for ch in text.ToCharArray() do
+ let candidate = builder.ToString() + string ch
+ if builder.Length > 0 && measureWidth candidate scale > maxWidth then
+ lines <- builder.ToString() :: lines
+ builder.Clear() |> ignore
+ builder.Append(ch) |> ignore
+ else
+ builder.Append(ch) |> ignore
+ lines <- builder.ToString() :: lines
+ List.rev lines
+
+ /// 锚定点的多行提示版式:按需折行、水平居中于目标、垂直钳制在两条 HUD 带之间。
+ let layoutWrapped
+ (viewportWidth: int)
+ (viewportHeight: int)
+ (anchorWorldX: float32)
+ (anchorWorldY: float32)
+ (cameraX: float32)
+ (cameraY: float32)
+ (scale: int)
+ (text: string) : PromptLayout =
+ let margin = 8
+ let wrapWidth = min maxPromptWidth (max 80 (viewportWidth - 2 * margin))
+ let lines = wrapText wrapWidth scale text
+ let width = lines |> List.map (fun line -> measureWidth line scale) |> List.max
+ let lineHeight = 8 * scale + 4
+ let height = lines.Length * lineHeight + 10
+ let screenX = int anchorWorldX - int cameraX - width / 2
+ let screenY = int anchorWorldY - int cameraY - 24 - height
+ let x =
+ if width + margin * 2 > viewportWidth then margin
+ else max margin (min (viewportWidth - margin - width) screenX)
+ let topSafe = hudTopBarHeight + margin
+ let bottomSafe = max topSafe (viewportHeight - hudBottomBandHeight - height - margin)
+ let y = max topSafe (min bottomSafe screenY)
+ { Lines = lines; X = x; Y = y; Width = width; Height = height }
+
module M5Interaction =
let initial : M5View =