namespace LivingVillage.Desktop module TitleScreen = type ColorRgb = { R: int G: int B: int } type TitleLayout = { SkyBands: ColorRgb list RoofTileSlots: int list RidgeY: int EaveY: int WaterRows: int list Lanterns: (int * int) list TitleText: string TitleX: int TitleY: int TitleScale: int MenuX: int MenuTopY: int MenuLineHeight: int PanelX: int PanelY: int PanelWidth: int PanelHeight: int HeadingY: int FirstItemY: int CursorX: int CursorWidth: int TextX: int ControlsPanelY: int ControlsRowHeight: int ControlsKeyX: int ControlsKeyWidth: int ControlsActionX: int } type TextRect = { X: int Y: int Width: int Height: int } let rectsDisjoint (a: TextRect) (b: TextRect) : bool = a.X + a.Width <= b.X || b.X + b.Width <= a.X || a.Y + a.Height <= b.Y || b.Y + b.Height <= a.Y /// Key-name and action rectangles for one controls-page row. The key column has a /// fixed width shared by every row, so all action texts start on the same x. let controlRowRects (layout: TitleLayout) (rowIndex: int) : TextRect * TextRect = let y = layout.FirstItemY + rowIndex * layout.ControlsRowHeight { X = layout.ControlsKeyX; Y = y; Width = layout.ControlsKeyWidth; Height = 16 }, { X = layout.ControlsActionX; Y = y; Width = layout.PanelX + layout.PanelWidth - layout.ControlsActionX - 24; Height = 16 } /// Cursor and label rectangles for one menu row. The cursor is drawn in its own /// column; the label always starts to the right of it, so they can never overprint. let selectionRects (layout: TitleLayout) (rowIndex: int) : TextRect * TextRect = let y = layout.FirstItemY + rowIndex * layout.MenuLineHeight { X = layout.CursorX; Y = y; Width = layout.CursorWidth; Height = 16 }, { X = layout.TextX; Y = y; Width = layout.PanelX + layout.PanelWidth - layout.TextX - 24; Height = 16 } // Palette mirrors the VillageArt atlas: roof (45,49,57)/(30,33,38), water (54,116,145), // lantern paper (212,92,74). Purely decorative reuse of existing art colors. let private skyBands : ColorRgb list = [ { R = 16; G = 26; B = 42 } { R = 20; G = 36; B = 52 } { R = 24; G = 46; B = 62 } { R = 28; G = 56; B = 68 } { R = 32; G = 64; B = 72 } ] let private roofRidgeSlot = 8 let private roofEaveSlot = 9 let private waterTileSlot = 1 let private lanternColor = { R = 212; G = 92; B = 74 } let private lanternHalo = { R = 252; G = 216; B = 148 } let lanternColors : ColorRgb * ColorRgb = lanternColor, lanternHalo let skyBandCount = skyBands.Length let waterSlot = waterTileSlot let roofSlots = roofRidgeSlot, roofEaveSlot let layout (viewportWidth: int) (viewportHeight: int) : TitleLayout = let width = max 320 viewportWidth let height = max 240 viewportHeight let tilePixels = 32 let columns = width / tilePixels let roofTileSlots = [ 0 .. columns - 1 ] |> List.map (fun column -> if column % 4 = 0 || column % 7 = 3 then roofRidgeSlot else roofEaveSlot) let waterRows = [ height - 96 height - 64 ] let lanternCount = max 3 (columns / 10) let lanterns = [ 1 .. lanternCount ] |> List.map (fun index -> let x = index * width / (lanternCount + 1) let y = (List.head waterRows) - 44 + (if index % 2 = 0 then 12 else 0) x, y) let panelWidth = min 460 (width - 64) { SkyBands = skyBands RoofTileSlots = roofTileSlots RidgeY = 44 EaveY = 76 WaterRows = waterRows Lanterns = lanterns TitleText = "江南水乡" TitleX = (width - 4 * (4 * 16)) / 2 TitleY = 120 TitleScale = 4 MenuX = (width - panelWidth) / 2 + 40 MenuTopY = height / 2 - 40 MenuLineHeight = 30 PanelX = (width - panelWidth) / 2 PanelY = height / 2 - 64 PanelWidth = panelWidth PanelHeight = min 360 (height - height / 2 - 40) HeadingY = height / 2 - 64 + 20 FirstItemY = height / 2 - 64 + 60 CursorX = (width - panelWidth) / 2 + 24 CursorWidth = 20 TextX = (width - panelWidth) / 2 + 24 + 20 + 12 ControlsPanelY = max 16 (height - 480) ControlsRowHeight = 25 ControlsKeyX = (width - panelWidth) / 2 + 24 ControlsKeyWidth = 120 ControlsActionX = (width - panelWidth) / 2 + 24 + 120 + 12 }