package elm_playground_gamekits
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Genre kits for Playground games: platformers, racing, shoot 'em ups, fighting and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/elm_playground_gamekits.kit_sectors/Sectors.ml.html
Source file Sectors.ml
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Sectors.mli *) type point = float * float type sector = { floor : float; ceiling : float; light : float; floor_rgb : int * int * int; ceiling_rgb : int * int * int; wall_rgb : int * int * int; loops : point list list; } type line = { x1 : float; y1 : float; x2 : float; y2 : float; front : int; back : int option } type level = { sectors : sector array; lines : line array; start : float * float * float; exit : int } let rect x1 y1 x2 y2 : point list = [ (x1, y1); (x1, y2); (x2, y2); (x2, y1) ] let hole x1 y1 x2 y2 : point list = List.rev (rect x1 y1 x2 y2) (*****************************************************************************) (* Finding the lines *) (*****************************************************************************) (* a loop's edges, each point to the next, the last back to the first *) let edges (loop : point list) : (point * point) list = match loop with [] -> [] | first :: _ -> List.mapi (fun i a -> (a, if i = List.length loop - 1 then first else List.nth loop (i + 1))) loop (* the edge a -> b cut at every point of [points] strictly between a * and b, in order *) let cut (points : point list) (((ax, ay) as a), ((bx, by) as b)) : (point * point) list = let dx = bx -. ax and dy = by -. ay in let len2 = (dx *. dx) +. (dy *. dy) in let between (px, py) = let t = (((px -. ax) *. dx) +. ((py -. ay) *. dy)) /. len2 in let cross = ((px -. ax) *. dy) -. ((py -. ay) *. dx) in if Float.abs cross < 1e-6 && t > 1e-9 && t < 1. -. 1e-9 then Some (t, (px, py)) else None in let inner = List.filter_map between points |> List.sort_uniq compare |> List.map snd in let all = (a :: inner) @ [ b ] in List.combine (List.filteri (fun i _ -> i < List.length all - 1) all) (List.tl all) let make (sectors : sector list) ~start ~exit : level = let points = List.concat_map (fun s -> List.concat s.loops) sectors in (* every sector's edges, cut: (a, b, the sector) *) let all = List.concat (List.mapi (fun i s -> List.concat_map (fun l -> List.concat_map (cut points) (edges l)) s.loops |> List.map (fun (a, b) -> (a, b, i))) sectors) in let tbl = Hashtbl.create 64 in List.iter (fun (a, b, i) -> if Hashtbl.mem tbl (a, b) then failwith "Sectors.make: two sectors overlap"; Hashtbl.add tbl (a, b) i) all; (* a line per edge, or per pair of opposite edges (kept once: from the * sector with the smaller index) *) let lines = List.filter_map (fun (((x1, y1) as a), ((x2, y2) as b), i) -> match Hashtbl.find_opt tbl (b, a) with | Some j when j < i -> None | back -> Some { x1; y1; x2; y2; front = i; back }) all in { sectors = Array.of_list sectors; lines = Array.of_list lines; start; exit } (*****************************************************************************) (* Where things are *) (*****************************************************************************) let side (l : line) (x : float) (y : float) : float = ((l.x2 -. l.x1) *. (y -. l.y1)) -. ((l.y2 -. l.y1) *. (x -. l.x1)) let inside (s : sector) (x : float) (y : float) : bool = let crossings (((ax, ay), (bx, by)) : point * point) = if ay > y <> (by > y) && x < ax +. ((y -. ay) /. (by -. ay) *. (bx -. ax)) then 1 else 0 in List.fold_left (fun n l -> List.fold_left (fun n e -> n + crossings e) n (edges l)) 0 s.loops mod 2 = 1 let sector_at (lv : level) (x : float) (y : float) : int = let rec go i = if i >= Array.length lv.sectors then 0 else if inside lv.sectors.(i) x y then i else go (i + 1) in go 0 (*****************************************************************************) (* Moving *) (*****************************************************************************) let radius = 16. let height = 56. let step = 24. (* the distance from (x, y) to the segment of [l] *) let distance (l : line) (x : float) (y : float) : float = let dx = l.x2 -. l.x1 and dy = l.y2 -. l.y1 in let t = Float.max 0. (Float.min 1. ((((x -. l.x1) *. dx) +. ((y -. l.y1) *. dy)) /. ((dx *. dx) +. (dy *. dy)))) in Float.hypot (x -. (l.x1 +. (t *. dx))) (y -. (l.y1 +. (t *. dy))) let move (lv : level) (sector_at : float -> float -> int) ((x, y) : float * float) ((dx, dy) : float * float) : float * float = let here = lv.sectors.(sector_at x y) in (* the line blocks: a wall, or the sector across it too high a step, * or too low a ceiling *) let blocks (l : line) = match l.back with | None -> true | Some back -> let there = lv.sectors.(if side l x y < 0. then back else l.front) in there.floor -. here.floor > step || Float.min there.ceiling here.ceiling -. Float.max there.floor here.floor < height in let free (nx, ny) = not (Array.exists (fun l -> distance l nx ny < radius && blocks l) lv.lines) in List.find_opt free [ (x +. dx, y +. dy); (x +. dx, y); (x, y +. dy) ] |> Option.value ~default:(x, y) (*****************************************************************************) (* The level *) (*****************************************************************************) (* The plan, from above, 32 map units a character across, 64 a line down (the steps up and down are 64 deep, 16 or 24 high): 1024 +---------------+ | +-----------+ | upstairs 64 | corridor | | +-------+---+ 768 +---+-------+---+ | | | | | | | up | |dn | | | | | 512 +-----+-------+-------++--+---+---+ | || | | || exit| | window ++ +-+ | | hall 0 +-+ || +-+ | | +-+ ++ | | pillar || | | || dark -32 | 0 +---------------------++----------+ 0 768 800 1152 *) let outpost : level = let sector ?(light = 0.8) ?(floor_rgb = (110, 100, 90)) ?(ceiling_rgb = (90, 90, 95)) ?(wall_rgb = (130, 120, 110)) floor ceiling loops = { floor; ceiling; light; floor_rgb; ceiling_rgb; wall_rgb; loops } in let wood = (140, 100, 60) and dark_wood = (110, 80, 50) in let stairs_up i = sector ~floor_rgb:wood ~wall_rgb:dark_wood (16. *. float_of_int (i + 1)) (176. +. (16. *. float_of_int i)) [ rect 256. (512. +. (64. *. float_of_int i)) 512. (576. +. (64. *. float_of_int i)) ] in let stairs_down i floor = sector ~light:(0.6 -. (0.05 *. float_of_int i)) ~wall_rgb:(100, 100, 110) floor (floor +. 128.) [ rect 896. (768. -. (64. *. float_of_int i)) 1024. (832. -. (64. *. float_of_int i)) ] in make ([ (* 0: the hall, a hole for the pillar *) sector ~light:0.85 0. 192. [ rect 0. 0. 768. 512.; hole 352. 224. 416. 288. ]; (* 1: the pillar, a sector with no room between floor and ceiling *) sector ~wall_rgb:(90, 100, 130) 0. 0. [ rect 352. 224. 416. 288. ] ] (* 2-5: four steps up, 16 each *) @ List.init 4 stairs_up @ [ (* 6: upstairs *) sector ~light:1. ~floor_rgb:(90, 110, 90) ~ceiling_rgb:(170, 170, 180) ~wall_rgb:(100, 130, 100) 64. 256. [ rect 128. 768. 640. 1024. ]; (* 7: the corridor *) sector ~light:0.7 64. 192. [ rect 640. 832. 1024. 960. ] ] (* 8-11: four steps down to the dark room *) @ List.mapi stairs_down [ 40.; 16.; -8. ] @ [ sector ~light:0.45 ~wall_rgb:(100, 100, 110) (-32.) 96. [ rect 896. 512. 1024. 640. ]; (* 12: the dark room, a hole for the exit *) sector ~light:0.4 ~floor_rgb:(60, 110, 40) ~wall_rgb:(90, 90, 80) (-32.) 160. [ rect 800. 0. 1152. 512.; hole 1024. 200. 1088. 264. ]; (* 13: the exit, raised a little, lit *) sector ~light:1. ~floor_rgb:(240, 210, 50) ~wall_rgb:(200, 60, 40) (-24.) 160. [ rect 1024. 200. 1088. 264. ]; (* 14: the window, a sector too, between the hall and the dark room; * its sill under the eye, too high a step *) sector ~light:0.85 32. 112. [ rect 768. 192. 800. 320. ] ]) ~start:(160., 96., 40.) ~exit:13
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>