summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/Interaction.fs
diff options
context:
space:
mode:
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 =