@v12-engine/create 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/README.md +4 -1
  2. package/base/_gitignore +5 -0
  3. package/base/package.json +3 -3
  4. package/base/src/main.ts +36 -10
  5. package/base/src/navigation.worker.ts +7 -0
  6. package/base/vite.config.ts +24 -2
  7. package/cli.mjs +21 -6
  8. package/index.mjs +27 -7
  9. package/layers/desktop/electron/app.mjs +237 -0
  10. package/layers/desktop/electron/config.mjs +16 -0
  11. package/layers/desktop/electron/dev.mjs +63 -0
  12. package/layers/desktop/electron/files.mjs +86 -0
  13. package/layers/desktop/electron/icons/icon.icns +0 -0
  14. package/layers/desktop/electron/icons/icon.png +0 -0
  15. package/layers/desktop/electron/main.mjs +12 -0
  16. package/layers/desktop/electron/package.mjs +75 -0
  17. package/layers/desktop/electron/preload.cjs +41 -0
  18. package/layers/desktop/electron/presentation.m +87 -0
  19. package/layers/desktop/electron/serve.mjs +76 -0
  20. package/layers/desktop/electron/tools.mjs +63 -0
  21. package/layers/desktop/electron/updates.mjs +71 -0
  22. package/layers/desktop/package.json +13 -0
  23. package/layers/desktop/src/desktop.ts +175 -0
  24. package/layers/desktop/src/shell.ts +279 -0
  25. package/layers/desktop/tests/main.test.ts +189 -0
  26. package/layers/desktop/tests/shell.test.ts +152 -0
  27. package/layers/rooms/server.ts +13 -10
  28. package/layers/server/server.ts +4 -5
  29. package/layers/source/vite.config.ts +21 -1
  30. package/package.json +2 -1
  31. package/starters/campaign/README.md +9 -2
  32. package/starters/campaign/layers/rooms/server.ts +3 -3
  33. package/starters/campaign/layers/server/server.ts +3 -5
  34. package/starters/campaign/layers/tests/tests/rules.test.ts +40 -21
  35. package/starters/campaign/src/campaign/game.ts +26 -5
  36. package/starters/campaign/src/campaign/index.ts +104 -68
  37. package/starters/campaign/src/campaign/kills.ts +1 -1
  38. package/starters/campaign/src/campaign/missions/homecoming.ts +7 -7
  39. package/starters/campaign/src/campaign/missions/siege.ts +6 -6
  40. package/starters/campaign/src/campaign/missions/warlord.ts +13 -14
  41. package/starters/campaign/src/campaign/scenes.ts +8 -7
  42. package/starters/campaign/src/campaign/units.ts +59 -20
  43. package/starters/campaign/src/campaign/view/attacks.ts +19 -0
  44. package/starters/campaign/src/campaign/view/fire.ts +2 -2
  45. package/starters/campaign/src/campaign/view/index.ts +54 -43
  46. package/starters/campaign/src/campaign/waves.ts +18 -17
  47. package/starters/campaign/src/game.ts +8 -1
  48. package/starters/campaign/src/main.ts +38 -19
  49. package/starters/campaign/src/maps/fort.names.ts +2 -2
  50. package/starters/campaign/src/maps/fort.vgmap.json +102 -2
  51. package/starters/campaign/src/maps/homestead.names.ts +2 -2
  52. package/starters/campaign/src/maps/homestead.vgmap.json +85 -1
  53. package/starters/campaign/src/maps/pass.names.ts +2 -2
  54. package/starters/campaign/src/maps/pass.vgmap.json +69 -1
  55. package/starters/campaign/src/waves-panel.ts +2 -1
  56. package/starters/empty/README.md +18 -10
  57. package/starters/empty/layers/tests/tests/rules.test.ts +16 -17
  58. package/starters/empty/src/game.ts +11 -75
  59. package/starters/empty/src/generate.worker.ts +5 -5
  60. package/starters/empty/src/rules.ts +83 -25
  61. package/starters/empty/src/view.ts +82 -0
  62. package/starters/examples/README.md +83 -0
  63. package/starters/examples/layers/tests/tests/examples.test.ts +30 -0
  64. package/starters/examples/package.json +5 -0
  65. package/starters/examples/src/examples/abilities/areas.ts +165 -0
  66. package/starters/examples/src/examples/abilities/areasLooks.ts +30 -0
  67. package/starters/examples/src/examples/abilities/buffs.ts +287 -0
  68. package/starters/examples/src/examples/abilities/buffsLooks.ts +46 -0
  69. package/starters/examples/src/examples/abilities/changing.ts +228 -0
  70. package/starters/examples/src/examples/abilities/changingLooks.ts +61 -0
  71. package/starters/examples/src/examples/abilities/control.ts +158 -0
  72. package/starters/examples/src/examples/abilities/controlLooks.ts +78 -0
  73. package/starters/examples/src/examples/abilities/damage.ts +156 -0
  74. package/starters/examples/src/examples/abilities/damageLooks.ts +37 -0
  75. package/starters/examples/src/examples/abilities/family.ts +29 -0
  76. package/starters/examples/src/examples/abilities/index.ts +32 -0
  77. package/starters/examples/src/examples/abilities/kit.ts +179 -0
  78. package/starters/examples/src/examples/abilities/lookKit.ts +71 -0
  79. package/starters/examples/src/examples/abilities/looks.ts +47 -0
  80. package/starters/examples/src/examples/abilities/missiles.ts +203 -0
  81. package/starters/examples/src/examples/abilities/missilesLooks.ts +38 -0
  82. package/starters/examples/src/examples/abilities/moves.ts +206 -0
  83. package/starters/examples/src/examples/abilities/movesLooks.ts +52 -0
  84. package/starters/examples/src/examples/abilities/recasts.ts +181 -0
  85. package/starters/examples/src/examples/abilities/recastsLooks.ts +41 -0
  86. package/starters/examples/src/examples/abilities/resources.ts +166 -0
  87. package/starters/examples/src/examples/abilities/resourcesLooks.ts +42 -0
  88. package/starters/examples/src/examples/abilities/summons.ts +296 -0
  89. package/starters/examples/src/examples/abilities/summonsLooks.ts +115 -0
  90. package/starters/examples/src/examples/abilities/triggers.ts +204 -0
  91. package/starters/examples/src/examples/abilities/triggersLooks.ts +53 -0
  92. package/starters/examples/src/examples/abilities/vision.ts +137 -0
  93. package/starters/examples/src/examples/abilities/visionLooks.ts +50 -0
  94. package/starters/examples/src/examples/animation/index.ts +235 -0
  95. package/starters/examples/src/examples/animation/looks.ts +444 -0
  96. package/starters/examples/src/examples/catalog.ts +52 -0
  97. package/starters/examples/src/examples/cinematics/index.ts +217 -0
  98. package/starters/examples/src/examples/cinematics/looks.ts +476 -0
  99. package/starters/examples/src/examples/cinematics/tour.json +43 -0
  100. package/starters/examples/src/examples/cinematics/village.ts +88 -0
  101. package/starters/examples/src/examples/common/looks.ts +65 -0
  102. package/starters/examples/src/examples/common/units.ts +70 -0
  103. package/starters/examples/src/examples/construction/index.ts +329 -0
  104. package/starters/examples/src/examples/construction/looks.ts +275 -0
  105. package/starters/examples/src/examples/custom-animation/bones.ts +92 -0
  106. package/starters/examples/src/examples/custom-animation/index.ts +190 -0
  107. package/starters/examples/src/examples/custom-animation/looks.ts +523 -0
  108. package/starters/examples/src/examples/custom-animation/runes.ts +82 -0
  109. package/starters/examples/src/examples/custom-fx/glaive.look.ts +110 -0
  110. package/starters/examples/src/examples/custom-fx/glaive.ts +40 -0
  111. package/starters/examples/src/examples/custom-fx/gpu.look.ts +145 -0
  112. package/starters/examples/src/examples/custom-fx/gpu.ts +22 -0
  113. package/starters/examples/src/examples/custom-fx/index.ts +25 -0
  114. package/starters/examples/src/examples/custom-fx/lights.look.ts +144 -0
  115. package/starters/examples/src/examples/custom-fx/lights.ts +25 -0
  116. package/starters/examples/src/examples/custom-fx/looks.ts +36 -0
  117. package/starters/examples/src/examples/custom-fx/meteor.look.ts +133 -0
  118. package/starters/examples/src/examples/custom-fx/meteor.ts +26 -0
  119. package/starters/examples/src/examples/custom-fx/parts.look.ts +193 -0
  120. package/starters/examples/src/examples/custom-fx/parts.ts +21 -0
  121. package/starters/examples/src/examples/custom-fx/portal.look.ts +193 -0
  122. package/starters/examples/src/examples/custom-fx/portal.ts +48 -0
  123. package/starters/examples/src/examples/custom-fx/renderer.look.ts +139 -0
  124. package/starters/examples/src/examples/custom-fx/renderer.ts +18 -0
  125. package/starters/examples/src/examples/custom-fx/ribbons.look.ts +151 -0
  126. package/starters/examples/src/examples/custom-fx/ribbons.ts +22 -0
  127. package/starters/examples/src/examples/custom-fx/shader.ts +42 -0
  128. package/starters/examples/src/examples/custom-fx/shield.look.ts +226 -0
  129. package/starters/examples/src/examples/custom-fx/shield.ts +43 -0
  130. package/starters/examples/src/examples/define.ts +86 -0
  131. package/starters/examples/src/examples/destruction/index.ts +570 -0
  132. package/starters/examples/src/examples/destruction/looks.ts +447 -0
  133. package/starters/examples/src/examples/dialogs/index.ts +383 -0
  134. package/starters/examples/src/examples/dialogs/looks.ts +546 -0
  135. package/starters/examples/src/examples/dynamic-map/index.ts +603 -0
  136. package/starters/examples/src/examples/dynamic-map/looks.ts +301 -0
  137. package/starters/examples/src/examples/fx/blood.look.ts +62 -0
  138. package/starters/examples/src/examples/fx/blood.ts +59 -0
  139. package/starters/examples/src/examples/fx/feel.look.ts +86 -0
  140. package/starters/examples/src/examples/fx/feel.ts +57 -0
  141. package/starters/examples/src/examples/fx/fight.ts +28 -0
  142. package/starters/examples/src/examples/fx/floats.look.ts +108 -0
  143. package/starters/examples/src/examples/fx/floats.ts +24 -0
  144. package/starters/examples/src/examples/fx/gallery.look.ts +168 -0
  145. package/starters/examples/src/examples/fx/gallery.ts +23 -0
  146. package/starters/examples/src/examples/fx/index.ts +30 -0
  147. package/starters/examples/src/examples/fx/links.look.ts +83 -0
  148. package/starters/examples/src/examples/fx/links.ts +49 -0
  149. package/starters/examples/src/examples/fx/looks.ts +55 -0
  150. package/starters/examples/src/examples/fx/map.look.ts +93 -0
  151. package/starters/examples/src/examples/fx/map.ts +33 -0
  152. package/starters/examples/src/examples/fx/marks.look.ts +100 -0
  153. package/starters/examples/src/examples/fx/marks.ts +36 -0
  154. package/starters/examples/src/examples/fx/shots.look.ts +92 -0
  155. package/starters/examples/src/examples/fx/shots.ts +71 -0
  156. package/starters/examples/src/examples/fx/spells.look.ts +87 -0
  157. package/starters/examples/src/examples/fx/spells.ts +122 -0
  158. package/starters/examples/src/examples/fx/stage.ts +31 -0
  159. package/starters/examples/src/examples/gui/babylonScreen.ts +167 -0
  160. package/starters/examples/src/examples/gui/babylonWorld.ts +191 -0
  161. package/starters/examples/src/examples/gui/htmlPart.ts +95 -0
  162. package/starters/examples/src/examples/gui/index.ts +143 -0
  163. package/starters/examples/src/examples/gui/keys.ts +91 -0
  164. package/starters/examples/src/examples/gui/layout.ts +42 -0
  165. package/starters/examples/src/examples/gui/looks.ts +270 -0
  166. package/starters/examples/src/examples/gui/minimap.ts +127 -0
  167. package/starters/examples/src/examples/gui/ownInterface.ts +129 -0
  168. package/starters/examples/src/examples/gui/plates.ts +87 -0
  169. package/starters/examples/src/examples/gui/radial.ts +104 -0
  170. package/starters/examples/src/examples/gui/solidPart.ts +107 -0
  171. package/starters/examples/src/examples/looks.ts +45 -0
  172. package/starters/examples/src/examples/mechanics/battle.ts +51 -0
  173. package/starters/examples/src/examples/mechanics/damage.ts +80 -0
  174. package/starters/examples/src/examples/mechanics/flying.ts +93 -0
  175. package/starters/examples/src/examples/mechanics/formations.ts +58 -0
  176. package/starters/examples/src/examples/mechanics/highGround.ts +82 -0
  177. package/starters/examples/src/examples/mechanics/index.ts +44 -0
  178. package/starters/examples/src/examples/mechanics/looks.ts +289 -0
  179. package/starters/examples/src/examples/mechanics/maze.ts +119 -0
  180. package/starters/examples/src/examples/mechanics/orders.ts +68 -0
  181. package/starters/examples/src/examples/mechanics/selection.ts +30 -0
  182. package/starters/examples/src/examples/moba/index.ts +377 -0
  183. package/starters/examples/src/examples/moba/looks.ts +349 -0
  184. package/starters/examples/src/examples/moba/maps.ts +69 -0
  185. package/starters/examples/src/examples/moba/spells.ts +34 -0
  186. package/starters/examples/src/examples/moba/units.ts +196 -0
  187. package/starters/examples/src/examples/nature/camera.ts +15 -0
  188. package/starters/examples/src/examples/nature/grass.ts +64 -0
  189. package/starters/examples/src/examples/nature/index.ts +327 -0
  190. package/starters/examples/src/examples/nature/land.ts +173 -0
  191. package/starters/examples/src/examples/nature/looks.ts +577 -0
  192. package/starters/examples/src/examples/nature/maps.ts +370 -0
  193. package/starters/examples/src/examples/nature/skies.ts +324 -0
  194. package/starters/examples/src/examples/nature/weather.ts +80 -0
  195. package/starters/examples/src/examples/performance/index.ts +263 -0
  196. package/starters/examples/src/examples/performance/looks.ts +372 -0
  197. package/starters/examples/src/examples/performance/meter.ts +158 -0
  198. package/starters/examples/src/examples/post/grades.ts +86 -0
  199. package/starters/examples/src/examples/post/index.ts +202 -0
  200. package/starters/examples/src/examples/post/looks.ts +884 -0
  201. package/starters/examples/src/examples/post/passes.ts +213 -0
  202. package/starters/examples/src/examples/rts/ai.ts +49 -0
  203. package/starters/examples/src/examples/rts/airlift.ts +43 -0
  204. package/starters/examples/src/examples/rts/base.ts +129 -0
  205. package/starters/examples/src/examples/rts/build.ts +65 -0
  206. package/starters/examples/src/examples/rts/camps.ts +36 -0
  207. package/starters/examples/src/examples/rts/carry.ts +103 -0
  208. package/starters/examples/src/examples/rts/creeps.ts +61 -0
  209. package/starters/examples/src/examples/rts/data.ts +406 -0
  210. package/starters/examples/src/examples/rts/ferry.ts +41 -0
  211. package/starters/examples/src/examples/rts/garrison.ts +50 -0
  212. package/starters/examples/src/examples/rts/harvest.ts +29 -0
  213. package/starters/examples/src/examples/rts/hero.ts +67 -0
  214. package/starters/examples/src/examples/rts/index.ts +77 -0
  215. package/starters/examples/src/examples/rts/looks.ts +721 -0
  216. package/starters/examples/src/examples/rts/shop.ts +46 -0
  217. package/starters/examples/src/examples/rts/tech.ts +39 -0
  218. package/starters/examples/src/examples/rts/train.ts +48 -0
  219. package/starters/examples/src/examples/rts/waterside.ts +185 -0
  220. package/starters/examples/src/examples/showcase/index.ts +714 -0
  221. package/starters/examples/src/examples/showcase/looks.ts +238 -0
  222. package/starters/examples/src/game.ts +17 -0
  223. package/starters/examples/src/main.ts +82 -0
  224. package/starters/examples/src/rules.ts +31 -0
  225. package/starters/examples/src/shell/look.ts +79 -0
  226. package/starters/examples/src/shell/registry.ts +7 -0
  227. package/starters/examples/src/shell/scenes.ts +178 -0
  228. package/starters/examples/src/shell/shell.ts +290 -0
  229. package/starters/examples/src/shell/style.ts +202 -0
  230. package/starters/examples/src/view.ts +50 -0
  231. package/starters/melee/README.md +12 -3
  232. package/starters/melee/layers/tests/tests/heroes.test.ts +402 -0
  233. package/starters/melee/layers/tests/tests/rules.test.ts +10 -8
  234. package/starters/melee/src/game.ts +32 -2
  235. package/starters/melee/src/maps/bay.names.ts +2 -2
  236. package/starters/melee/src/maps/bay.vgmap.json +198 -1
  237. package/starters/melee/src/maps.ts +11 -9
  238. package/starters/melee/src/melee/abilities.ts +1 -194
  239. package/starters/melee/src/melee/blight.ts +7 -7
  240. package/starters/melee/src/melee/cellLayers.json +3 -3
  241. package/starters/melee/src/melee/data.json +1 -18
  242. package/starters/melee/src/melee/game.ts +6 -4
  243. package/starters/melee/src/melee/heroes.ts +1025 -0
  244. package/starters/melee/src/melee/index.ts +64 -61
  245. package/starters/melee/src/melee/items.ts +15 -362
  246. package/starters/melee/src/melee/languages/he.json +14 -2
  247. package/starters/melee/src/melee/pings.ts +5 -6
  248. package/starters/melee/src/melee/saves.ts +1 -1
  249. package/starters/melee/src/melee/undead.ts +15 -230
  250. package/starters/melee/src/melee/units.ts +37 -171
  251. package/starters/melee/src/melee/view/attacks.ts +19 -0
  252. package/starters/melee/src/melee/view/heroSpellLooks.ts +39 -8
  253. package/starters/melee/src/melee/view/index.ts +56 -52
  254. package/starters/melee/src/melee/view/looks.ts +86 -63
  255. package/starters/melee/src/melee/view/undeadLooks.ts +70 -31
  256. package/starters/melee/src/melee/view/walls.ts +1 -1
  257. package/starters.mjs +6 -0
  258. package/v12.mjs +14 -15
  259. package/starters/campaign/src/rules.ts +0 -20
  260. package/starters/empty/src/maps.ts +0 -44
  261. package/starters/empty/src/tiles.ts +0 -12
  262. package/starters/melee/src/melee/heroSpells.ts +0 -38
  263. package/starters/melee/src/rules.ts +0 -24
@@ -0,0 +1,370 @@
1
+ /**
2
+ * The nature examples' maps, each made in code (`./land.ts`): a forest round a glade, a meadow of every style of grass,
3
+ * a lake under a waterfall, rolling hills, a misty valley, a village, and a dry field by a wood. Where each puts its
4
+ * trees, worked out from the map so that a command can plant them again where they stood
5
+ */
6
+ import { exactMath } from '@v12-engine/engine'
7
+ import type { GameMap } from '@v12-engine/engine'
8
+
9
+ type Rgb = [number, number, number]
10
+ import { STYLE } from './grass'
11
+ import { type At, band, blob, distanceToLine, field, grow, mow, noise, paint, place, pour, raise, scatter, sculpt, smooth } from './land'
12
+
13
+ /** Ground that rolls a little, `amount` high: the field's 1 as its middle */
14
+ const rolling =
15
+ (amount: number, scale = 14, seed = 3) =>
16
+ (x: number, z: number) =>
17
+ 1 + (noise(x, z, scale, seed) - 0.5) * 2 * amount
18
+
19
+ // ---- a forest round a glade
20
+
21
+ /** The glade in the forest's middle, and the path that leads into it from the south */
22
+ export const GLADE: At = { x: 32, z: 32 }
23
+ const FOREST_PATH: At[] = [
24
+ { x: 30, z: 64 },
25
+ { x: 33, z: 52 },
26
+ { x: 30, z: 42 },
27
+ { x: 32, z: 33 }
28
+ ]
29
+
30
+ /** A forest of 64 cells a side round a glade of meadow and flowers, a path winding in from the south */
31
+ export function forestMap(): GameMap {
32
+ const map = field('Forest', 64)
33
+ sculpt(map, rolling(0.8, 16))
34
+ const glade = blob(GLADE, 10, 2.5, 4)
35
+ const path = band(FOREST_PATH, 2.2, 0.4)
36
+ paint(map, 'darkGrass', (x, z) => !glade(x, z))
37
+ paint(map, 'dirt', (x, z) => !glade(x, z) && noise(x, z, 5, 9) > 0.68)
38
+ paint(map, 'grass', glade)
39
+ grow(map, STYLE.meadow, glade)
40
+ grow(map, STYLE.flowers, (x, z) => glade(x, z) && noise(x, z, 4, 12) > 0.6)
41
+ grow(map, STYLE.tallGrass, (x, z) => !glade(x, z) && noise(x, z, 7, 13) > 0.62)
42
+ grow(map, STYLE.thicket, (x, z) => blob(GLADE, 12.5, 1.5, 4)(x, z) && !glade(x, z) && noise(x, z, 3, 14) > 0.5)
43
+ paint(map, 'dirt', path)
44
+ mow(map, path)
45
+ for (const [i, at] of [
46
+ { x: 27, z: 29 },
47
+ { x: 37, z: 35 },
48
+ { x: 35, z: 27 }
49
+ ].entries())
50
+ place(map, `forest/rock_medium_${i + 1}.glb`, at, { yaw: i * 2.1, scale: 0.9 })
51
+ return map
52
+ }
53
+
54
+ /** Where the forest's trees stand: everywhere but the glade and the path, thicker away from the glade */
55
+ export const forestTrees = (map: GameMap): At[] =>
56
+ scatter(map, (x, z) => !blob(GLADE, 11, 2.5, 4)(x, z) && distanceToLine(FOREST_PATH, x, z) > 2.2 && noise(x, z, 6, 21) > 0.22, 1.7, 11)
57
+
58
+ // ---- a meadow of every style
59
+
60
+ /** The meadow's walkers' two sides: they cross from one to the other through the grass */
61
+ export const MEADOW_WEST = 10
62
+ export const MEADOW_EAST = 46
63
+
64
+ /** A meadow of 56 cells a side, each style of grass in a field of its own, a pond with reeds, trees along its edges */
65
+ export function meadowMap(): GameMap {
66
+ const map = field('Meadow', 56)
67
+ sculpt(map, rolling(0.5, 18))
68
+ const pond = blob({ x: 41, z: 15 }, 4.2, 1, 6)
69
+ pour(map, pond, blob({ x: 41, z: 15 }, 2.8, 0.8, 6))
70
+ paint(map, 'sand', blob({ x: 41, z: 15 }, 5.4, 1, 6))
71
+ grow(map, STYLE.meadow, (x, z) => x > 6 && x < 50 && z > 6 && z < 50)
72
+ // the fields, west to east across the middle where the walkers cross
73
+ grow(map, STYLE.wheat, (x, z) => x > 14 && x < 23 && z > 20 && z < 36 && noise(x, z, 3, 7) > 0.15)
74
+ grow(map, STYLE.tallGrass, blob({ x: 28, z: 28 }, 4.5, 1.2, 7))
75
+ grow(map, STYLE.flowers, blob({ x: 37, z: 31 }, 4, 1.5, 8))
76
+ grow(map, STYLE.dryGrass, blob({ x: 35, z: 42 }, 4.5, 1.5, 9))
77
+ grow(map, STYLE.thicket, blob({ x: 20, z: 43 }, 3, 1, 10))
78
+ grow(map, STYLE.reeds, (x, z) => blob({ x: 41, z: 15 }, 5.6, 1, 6)(x, z) && !blob({ x: 41, z: 15 }, 3.4, 0.8, 6)(x, z))
79
+ mow(map, pond)
80
+ paint(
81
+ map,
82
+ 'dirt',
83
+ band(
84
+ [
85
+ { x: 0, z: 9 },
86
+ { x: 56, z: 7 }
87
+ ],
88
+ 1.6
89
+ )
90
+ )
91
+ mow(
92
+ map,
93
+ band(
94
+ [
95
+ { x: 0, z: 9 },
96
+ { x: 56, z: 7 }
97
+ ],
98
+ 1.6
99
+ )
100
+ )
101
+ return map
102
+ }
103
+
104
+ /** The meadow's trees: a ring round its edge */
105
+ export const meadowTrees = (map: GameMap): At[] => scatter(map, (x, z) => (x < 6 || x > 50 || z < 5 || z > 50) && noise(x, z, 5, 4) > 0.3, 1.9, 12)
106
+
107
+ // ---- a lake under a waterfall
108
+
109
+ /** The lake's middle, and where the falls come down into it */
110
+ export const LAKE: At = { x: 33, z: 40 }
111
+ export const FALLS: At = { x: 32, z: 22 }
112
+ const UPPER_RIVER: At[] = [
113
+ { x: 26, z: 0 },
114
+ { x: 29, z: 8 },
115
+ { x: 31, z: 15 },
116
+ { x: 32, z: 22 }
117
+ ]
118
+ const LOWER_RIVER: At[] = [
119
+ { x: 40, z: 44 },
120
+ { x: 48, z: 47 },
121
+ { x: 55, z: 45 },
122
+ { x: 64, z: 49 }
123
+ ]
124
+
125
+ /**
126
+ * A lake of 64 cells a side: a plateau (a cliff level up) across the north with a river on it that spills over the
127
+ * cliff into a pool, a lake below with a sandy shore and reeds, and a river out of it to the east. The waterfalls
128
+ * are found where the water spills from the plateau, by the client (`WaterLook.falls`)
129
+ */
130
+ export function lakeMap(): GameMap {
131
+ const map = field('Lake', 64)
132
+ sculpt(map, (x, z) => rolling(0.6, 12)(x, z) + smooth(10, 0, z) * 1.5)
133
+ const plateau = (x: number, z: number) => z < 21 + (noise(x, 0, 6, 5) - 0.5) * 3 || (z < 23 && Math.abs(x - FALLS.x) < 4)
134
+ raise(map, 1, plateau)
135
+ const lake = (x: number, z: number) => blob(LAKE, 10, 2, 3)(x, z) || (z >= 21 && z < 32 && Math.abs(x - FALLS.x) < 4.5 + (z - 21) * 0.3)
136
+ const upper = band(UPPER_RIVER, 3.2, 0.3, 4)
137
+ const lower = band(LOWER_RIVER, 2.6, 0.3, 5)
138
+ pour(
139
+ map,
140
+ (x, z) => (plateau(x, z) ? upper(x, z) : lake(x, z) || lower(x, z)),
141
+ (x, z) => (plateau(x, z) ? band(UPPER_RIVER, 1.8)(x, z) : blob(LAKE, 7, 1.5, 3)(x, z))
142
+ )
143
+ paint(map, 'dirt', (x, z) => !plateau(x, z) && blob(LAKE, 12.5, 2, 3)(x, z))
144
+ paint(map, 'sand', (x, z) => !plateau(x, z) && blob(LAKE, 11, 2, 3)(x, z) && noise(x, z, 3, 11) > 0.45)
145
+ paint(map, 'rock', (x, z) => plateau(x, z) && noise(x, z, 4, 8) > 0.6)
146
+ paint(map, 'darkGrass', (x, z) => noise(x, z, 9, 6) > 0.6)
147
+ grow(map, STYLE.meadow, (x, z) => !blob(LAKE, 12.5, 2, 3)(x, z) && noise(x, z, 8, 3) > 0.3)
148
+ grow(map, STYLE.reeds, (x, z) => !plateau(x, z) && blob(LAKE, 12, 2, 3)(x, z) && !blob(LAKE, 10.5, 2, 3)(x, z) && noise(x, z, 3, 2) > 0.45)
149
+ grow(map, STYLE.flowers, (x, z) => noise(x, z, 5, 15) > 0.72)
150
+ mow(map, (x, z) => map.water[Math.floor(z) * map.width + Math.floor(x)] > 0)
151
+ place(map, 'forest/rock_medium_2.glb', { x: 27.5, z: 24 }, { scale: 1.4, yaw: 1 })
152
+ place(map, 'forest/rock_medium_3.glb', { x: 37.5, z: 25 }, { scale: 1.2, yaw: 2.5 })
153
+ place(map, 'village/fantasy_house_2.glb', { x: 48, z: 34 }, { yaw: -2.2, scale: 0.9, blocks: true })
154
+ return map
155
+ }
156
+
157
+ /** The lake's trees: in woods round it, on the plateau too, never on the shore */
158
+ export const lakeTrees = (map: GameMap): At[] =>
159
+ scatter(
160
+ map,
161
+ (x, z) =>
162
+ !blob(LAKE, 13.5, 2, 3)(x, z) &&
163
+ distanceToLine(UPPER_RIVER, x, z) > 3 &&
164
+ distanceToLine(LOWER_RIVER, x, z) > 3 &&
165
+ exactMath.hypot(x - 48, z - 34) > 4.5 &&
166
+ noise(x, z, 7, 22) > 0.45,
167
+ 1.8,
168
+ 13
169
+ )
170
+
171
+ // ---- rolling hills, for the clouds
172
+
173
+ /** Hills of 96 cells a side, woods in their hollows: what clouds' shadows are seen running over */
174
+ export function hillsMap(): GameMap {
175
+ const map = field('Hills', 96)
176
+ sculpt(map, (x, z) => 1 + noise(x, z, 22, 2) * 5 + noise(x, z, 7, 3) * 0.8)
177
+ paint(map, 'darkGrass', (x, z) => noise(x, z, 12, 4) > 0.55)
178
+ paint(map, 'rock', (x, z) => noise(x, z, 22, 2) > 0.78)
179
+ grow(map, STYLE.meadow, (x, z) => noise(x, z, 22, 2) < 0.7)
180
+ grow(map, STYLE.flowers, (x, z) => noise(x, z, 6, 9) > 0.75)
181
+ return map
182
+ }
183
+
184
+ /** The hills' woods */
185
+ export const hillsTrees = (map: GameMap): At[] => scatter(map, (x, z) => noise(x, z, 12, 4) > 0.58, 1.9, 14, 900)
186
+
187
+ // ---- a misty valley
188
+
189
+ /** The line the valley winds along, south to north */
190
+ const VALLEY: At[] = Array.from({ length: 9 }, (_, i) => ({ x: 40 + exactMath.sin(i * 0.9) * 9, z: i * 10 }))
191
+
192
+ /** A valley of 80 cells a side between steep hills, a stream along its floor: the mist lies in it */
193
+ export function valleyMap(): GameMap {
194
+ const map = field('Valley', 80)
195
+ sculpt(map, (x, z) => {
196
+ const away = distanceToLine(VALLEY, x, z)
197
+ return 0.6 + smooth(4, 22, away) * 9 + noise(x, z, 10, 6) * 1.6 * smooth(4, 12, away)
198
+ })
199
+ const stream = band(VALLEY, 2.4, 0.4, 7)
200
+ pour(map, stream, band(VALLEY, 1, 0.2, 7))
201
+ paint(map, 'dirt', (x, z) => distanceToLine(VALLEY, x, z) < 3)
202
+ paint(map, 'darkGrass', (x, z) => distanceToLine(VALLEY, x, z) > 12)
203
+ paint(map, 'rock', (x, z) => distanceToLine(VALLEY, x, z) > 9 && distanceToLine(VALLEY, x, z) < 16 && noise(x, z, 4, 3) > 0.5)
204
+ grow(map, STYLE.tallGrass, (x, z) => distanceToLine(VALLEY, x, z) < 7 && noise(x, z, 5, 4) > 0.4)
205
+ grow(map, STYLE.reeds, (x, z) => distanceToLine(VALLEY, x, z) < 3)
206
+ mow(map, stream)
207
+ return map
208
+ }
209
+
210
+ /** The valley's trees: up its sides, pines high */
211
+ export const valleyTrees = (map: GameMap): At[] =>
212
+ scatter(map, (x, z) => distanceToLine(VALLEY, x, z) > 4 && noise(x, z, 6, 30) > 0.35, 1.8, 15, 1200)
213
+
214
+ /** A point on the valley's floor, `share` of the way up it */
215
+ export const valleyAt = (share: number): At => {
216
+ const i = Math.min(VALLEY.length - 2, Math.floor(share * (VALLEY.length - 1)))
217
+ const t = share * (VALLEY.length - 1) - i
218
+ return { x: VALLEY[i].x + (VALLEY[i + 1].x - VALLEY[i].x) * t, z: VALLEY[i].z + (VALLEY[i + 1].z - VALLEY[i].z) * t }
219
+ }
220
+
221
+ // ---- a village, for the weather and the time of day
222
+
223
+ /** The village square's middle */
224
+ export const SQUARE: At = { x: 32, z: 32 }
225
+
226
+ /** Where the village's lamps stand: torches round the square, each a light of the map too (`MapEnvironment.lights`) */
227
+ export const LAMPS: At[] = [
228
+ { x: 27, z: 27 },
229
+ { x: 37, z: 27 },
230
+ { x: 27, z: 37 },
231
+ { x: 37, z: 37 },
232
+ { x: 32, z: 22 },
233
+ { x: 32, z: 44 },
234
+ { x: 20, z: 32 },
235
+ { x: 44, z: 32 }
236
+ ]
237
+
238
+ /** A village of 64 cells a side: houses round a square with a well and a fire, roads out, fields and woods about */
239
+ export function villageMap(): GameMap {
240
+ const map = field('Village', 64)
241
+ sculpt(map, rolling(0.5, 14))
242
+ const roads = [
243
+ band([{ x: 32, z: 0 }, { x: 31, z: 20 }, SQUARE, { x: 33, z: 46 }, { x: 32, z: 64 }], 2.4, 0.3, 3),
244
+ band([{ x: 0, z: 31 }, { x: 18, z: 33 }, SQUARE, { x: 46, z: 31 }, { x: 64, z: 34 }], 2.4, 0.3, 4)
245
+ ]
246
+ const square = blob(SQUARE, 6.5, 0.6, 2)
247
+ grow(map, STYLE.meadow, (x, z) => noise(x, z, 9, 2) > 0.25)
248
+ grow(map, STYLE.wheat, (x, z) => x > 42 && x < 56 && z > 40 && z < 54)
249
+ grow(map, STYLE.wheat, (x, z) => x > 8 && x < 20 && z > 8 && z < 19)
250
+ grow(map, STYLE.flowers, (x, z) => noise(x, z, 4, 8) > 0.74)
251
+ paint(map, 'road', (x, z) => square(x, z) || roads.some((road) => road(x, z)))
252
+ mow(map, (x, z) => square(x, z) || roads.some((road) => road(x, z)))
253
+ const houses: [string, At, number][] = [
254
+ ['village/fantasy_house.glb', { x: 24, z: 22 }, 0.6],
255
+ ['village/fantasy_house_2.glb', { x: 41, z: 22 }, -0.7],
256
+ ['village/fantasy_inn.glb', { x: 42, z: 41 }, -2.4],
257
+ ['village/fantasy_house_3.glb', { x: 22, z: 41 }, 2.3],
258
+ ['village/fantasy_house.glb', { x: 15, z: 27 }, 1.5],
259
+ ['village/mill.glb', { x: 50, z: 17 }, -1],
260
+ ['village/fantasy_stable.glb', { x: 15, z: 44 }, 2]
261
+ ]
262
+ for (const [model, at, yaw] of houses) {
263
+ mow(map, blob(at, 3.2, 0.5))
264
+ place(map, model, at, { yaw, blocks: true })
265
+ }
266
+ place(map, 'village/well.glb', { x: 34.5, z: 30 }, { scale: 1.1 })
267
+ place(map, 'village/bonfire.glb', { x: 30, z: 34 }, { scale: 1.6, name: 'fire' })
268
+ place(map, 'village/market_stand.glb', { x: 28, z: 29 }, { yaw: 0.8 })
269
+ place(map, 'village/cart.glb', { x: 36, z: 36 }, { yaw: 2.4 })
270
+ place(map, 'village/barrel.glb', { x: 38.6, z: 25.8 })
271
+ place(map, 'village/hay.glb', { x: 46, z: 38 }, { yaw: 1 })
272
+ for (const at of LAMPS) place(map, 'dungeon-kit/torch.glb', at, { scale: 1.2 })
273
+ // a light at each torch's flame and at the fire, on always (a game without a day has no night to come on at); how
274
+ // bright all of them are is the map's lamps (`MapEnvironment.lamps`), which the time of day turns up at dusk
275
+ map.environment = {
276
+ ...map.environment,
277
+ lights: [
278
+ ...LAMPS.map((at, i) => ({
279
+ id: i + 1,
280
+ name: `torch ${i + 1}`,
281
+ ...at,
282
+ height: 1.7,
283
+ color: [1, 0.68, 0.38] as Rgb,
284
+ intensity: 1.5,
285
+ range: 8,
286
+ flicker: 0.12,
287
+ halo: 0.3,
288
+ when: 'always' as const
289
+ })),
290
+ {
291
+ id: 20,
292
+ name: 'fire',
293
+ x: 30,
294
+ z: 34,
295
+ height: 0.9,
296
+ color: [1, 0.55, 0.25],
297
+ intensity: 2.2,
298
+ range: 11,
299
+ flicker: { amount: 0.3, speed: 1.4 },
300
+ when: 'always'
301
+ }
302
+ ],
303
+ fx: [
304
+ { id: 1, name: 'fire', fx: 'campfire', x: 30, z: 34, scale: 1.3 },
305
+ ...LAMPS.map((at, i) => ({ id: i + 2, name: `torch ${i + 1}`, fx: 'torch', ...at, height: 1.55, scale: 0.8 }))
306
+ ]
307
+ }
308
+ return map
309
+ }
310
+
311
+ /** The village's trees: woods round its edge, a few in it */
312
+ export const villageTrees = (map: GameMap): At[] =>
313
+ scatter(
314
+ map,
315
+ (x, z) =>
316
+ (exactMath.hypot(x - SQUARE.x, z - SQUARE.z) > 26 || noise(x, z, 5, 3) > 0.82) &&
317
+ exactMath.hypot(x - SQUARE.x, z - SQUARE.z) > 9 &&
318
+ noise(x, z, 6, 40) > 0.3,
319
+ 1.9,
320
+ 16
321
+ ).filter((at) => !onRoadOrHouse(at))
322
+
323
+ /** Whether a place is a road, the square or a house's ground: trees keep off */
324
+ function onRoadOrHouse({ x, z }: At): boolean {
325
+ return (
326
+ Math.abs(x - 32) < 3 ||
327
+ Math.abs(z - 32) < 3 ||
328
+ [
329
+ { x: 24, z: 22 },
330
+ { x: 41, z: 22 },
331
+ { x: 42, z: 41 },
332
+ { x: 22, z: 41 },
333
+ { x: 15, z: 27 },
334
+ { x: 50, z: 17 },
335
+ { x: 15, z: 44 }
336
+ ].some((house) => exactMath.hypot(x - house.x, z - house.z) < 5)
337
+ )
338
+ }
339
+
340
+ // ---- a dry field by a wood, to burn
341
+
342
+ /** Where the fire is lit: the dry field's west end */
343
+ export const FIRE_START: At = { x: 14, z: 30 }
344
+
345
+ /** A field of 64 cells a side: dry grass in the west, meadow in the middle, a wood in the east, a road across */
346
+ export function fireMap(): GameMap {
347
+ const map = field('Dry field', 64)
348
+ sculpt(map, rolling(0.5, 16))
349
+ paint(map, 'dirt', (x, z) => noise(x, z, 6, 3) > 0.6)
350
+ grow(map, STYLE.dryGrass, (x, z) => x < 30 && noise(x, z, 8, 4) > 0.2)
351
+ grow(map, STYLE.meadow, (x, z) => x >= 26 && noise(x, z, 8, 4) > 0.25)
352
+ grow(map, STYLE.tallGrass, (x, z) => x > 22 && x < 40 && noise(x, z, 6, 5) > 0.6)
353
+ grow(map, STYLE.wheat, (x, z) => x > 28 && x < 38 && z > 6 && z < 20)
354
+ const road = band(
355
+ [
356
+ { x: 0, z: 52 },
357
+ { x: 30, z: 50 },
358
+ { x: 64, z: 54 }
359
+ ],
360
+ 2.2
361
+ )
362
+ paint(map, 'road', road)
363
+ mow(map, road)
364
+ place(map, 'village/fantasy_house.glb', { x: 22, z: 56 }, { yaw: 3, blocks: true })
365
+ return map
366
+ }
367
+
368
+ /** The fire's wood: east of the middle, and a few trees out in the field */
369
+ export const fireTrees = (map: GameMap): At[] =>
370
+ scatter(map, (x, z) => z < 48 && ((x > 38 && noise(x, z, 6, 7) > 0.25) || (x > 10 && noise(x, z, 4, 8) > 0.86)), 1.7, 17)
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Skies for the nature examples: the look of the world at any hour of the day, and named looks to cross-fade between.
3
+ * Looks of the map (`MapEnvironment`) as data: given to `client.setEnvironment` they are one screen's, to
4
+ * `world.setEnvironment` every player's. The engine alone, so a map or the rules may take them too
5
+ */
6
+ import { exactMath } from '@v12-engine/engine'
7
+ import type { MapEnvironment } from '@v12-engine/engine'
8
+
9
+ type Rgb = [number, number, number]
10
+
11
+ // ---- the time of day
12
+
13
+ /** The world at an hour: what the sun, the sky, the light, the mist and the lamps are */
14
+ interface HourLook {
15
+ hour: number
16
+ sky: Rgb
17
+ sun: number
18
+ sunColor: Rgb
19
+ ambient: number
20
+ ambientColor: Rgb
21
+ ground: Rgb
22
+ clouds: Rgb
23
+ fog: Rgb
24
+ mist: number
25
+ lamps: number
26
+ }
27
+
28
+ /** The day, hour by hour: night, dawn, morning, noon, afternoon, golden hour, dusk, night. Between two, mixed */
29
+ const DAY: HourLook[] = [
30
+ {
31
+ hour: 0,
32
+ sky: [0.03, 0.05, 0.12],
33
+ sun: 0.32,
34
+ sunColor: [0.55, 0.65, 1],
35
+ ambient: 0.22,
36
+ ambientColor: [0.35, 0.42, 0.75],
37
+ ground: [0.08, 0.08, 0.14],
38
+ clouds: [0.2, 0.22, 0.32],
39
+ fog: [0.06, 0.08, 0.16],
40
+ mist: 0.3,
41
+ lamps: 1
42
+ },
43
+ {
44
+ hour: 4.5,
45
+ sky: [0.06, 0.08, 0.18],
46
+ sun: 0.3,
47
+ sunColor: [0.55, 0.65, 1],
48
+ ambient: 0.24,
49
+ ambientColor: [0.38, 0.44, 0.75],
50
+ ground: [0.08, 0.08, 0.14],
51
+ clouds: [0.24, 0.26, 0.36],
52
+ fog: [0.08, 0.1, 0.2],
53
+ mist: 0.35,
54
+ lamps: 1
55
+ },
56
+ {
57
+ hour: 6,
58
+ sky: [0.75, 0.55, 0.55],
59
+ sun: 0.75,
60
+ sunColor: [1, 0.55, 0.35],
61
+ ambient: 0.4,
62
+ ambientColor: [0.75, 0.65, 0.85],
63
+ ground: [0.2, 0.17, 0.18],
64
+ clouds: [1, 0.7, 0.6],
65
+ fog: [0.8, 0.62, 0.6],
66
+ mist: 0.45,
67
+ lamps: 0.6
68
+ },
69
+ {
70
+ hour: 8,
71
+ sky: [0.6, 0.72, 0.88],
72
+ sun: 1.15,
73
+ sunColor: [1, 0.85, 0.68],
74
+ ambient: 0.5,
75
+ ambientColor: [0.85, 0.88, 1],
76
+ ground: [0.3, 0.28, 0.25],
77
+ clouds: [1, 0.95, 0.9],
78
+ fog: [0.75, 0.78, 0.82],
79
+ mist: 0.15,
80
+ lamps: 0
81
+ },
82
+ {
83
+ hour: 12,
84
+ sky: [0.46, 0.66, 0.92],
85
+ sun: 1.45,
86
+ sunColor: [1, 0.97, 0.9],
87
+ ambient: 0.6,
88
+ ambientColor: [0.92, 0.96, 1],
89
+ ground: [0.35, 0.33, 0.3],
90
+ clouds: [1, 1, 1],
91
+ fog: [0.72, 0.8, 0.9],
92
+ mist: 0,
93
+ lamps: 0
94
+ },
95
+ {
96
+ hour: 16,
97
+ sky: [0.5, 0.66, 0.88],
98
+ sun: 1.35,
99
+ sunColor: [1, 0.92, 0.78],
100
+ ambient: 0.55,
101
+ ambientColor: [0.9, 0.92, 1],
102
+ ground: [0.33, 0.3, 0.27],
103
+ clouds: [1, 0.97, 0.92],
104
+ fog: [0.75, 0.78, 0.85],
105
+ mist: 0,
106
+ lamps: 0
107
+ },
108
+ {
109
+ hour: 18,
110
+ sky: [0.85, 0.58, 0.42],
111
+ sun: 1.05,
112
+ sunColor: [1, 0.62, 0.32],
113
+ ambient: 0.42,
114
+ ambientColor: [0.85, 0.65, 0.65],
115
+ ground: [0.25, 0.18, 0.15],
116
+ clouds: [1, 0.68, 0.48],
117
+ fog: [0.85, 0.6, 0.48],
118
+ mist: 0.15,
119
+ lamps: 0.4
120
+ },
121
+ {
122
+ hour: 19.5,
123
+ sky: [0.32, 0.25, 0.42],
124
+ sun: 0.5,
125
+ sunColor: [0.85, 0.45, 0.45],
126
+ ambient: 0.3,
127
+ ambientColor: [0.5, 0.42, 0.7],
128
+ ground: [0.12, 0.1, 0.14],
129
+ clouds: [0.6, 0.42, 0.52],
130
+ fog: [0.3, 0.24, 0.36],
131
+ mist: 0.3,
132
+ lamps: 0.9
133
+ },
134
+ {
135
+ hour: 21,
136
+ sky: [0.05, 0.07, 0.16],
137
+ sun: 0.32,
138
+ sunColor: [0.55, 0.65, 1],
139
+ ambient: 0.22,
140
+ ambientColor: [0.35, 0.42, 0.75],
141
+ ground: [0.08, 0.08, 0.14],
142
+ clouds: [0.2, 0.22, 0.32],
143
+ fog: [0.06, 0.08, 0.16],
144
+ mist: 0.3,
145
+ lamps: 1
146
+ },
147
+ {
148
+ hour: 24,
149
+ sky: [0.03, 0.05, 0.12],
150
+ sun: 0.32,
151
+ sunColor: [0.55, 0.65, 1],
152
+ ambient: 0.22,
153
+ ambientColor: [0.35, 0.42, 0.75],
154
+ ground: [0.08, 0.08, 0.14],
155
+ clouds: [0.2, 0.22, 0.32],
156
+ fog: [0.06, 0.08, 0.16],
157
+ mist: 0.3,
158
+ lamps: 1
159
+ }
160
+ ]
161
+
162
+ const mix = (a: number, b: number, t: number) => a + (b - a) * t
163
+ const mixRgb = (a: Rgb, b: Rgb, t: number): Rgb => [mix(a[0], b[0], t), mix(a[1], b[1], t), mix(a[2], b[2], t)]
164
+
165
+ /** Where the sun (or by night the moon) stands at an hour: degrees round from north, and up from the horizon */
166
+ function sunAt(hour: number): { around: number; up: number } {
167
+ const day = (hour - 6) / 12
168
+ if (day >= 0 && day <= 1) return { around: 90 + day * 180, up: Math.max(4, exactMath.sin(day * Math.PI) * 68) }
169
+ // the moon: across the sky the other way, never as high
170
+ const night = (((hour - 18 + 24) % 24) / 12) % 1
171
+ return { around: 90 + night * 180, up: Math.max(12, exactMath.sin(night * Math.PI) * 45) }
172
+ }
173
+
174
+ /** The look of the world at an hour, 0 to 24: the sun's place and colour, the sky, the clouds, the mist and the lamps */
175
+ export function lookAtHour(hour: number): Partial<MapEnvironment> {
176
+ const h = ((hour % 24) + 24) % 24
177
+ const next = DAY.findIndex((each) => each.hour > h)
178
+ const a = DAY[Math.max(0, next - 1)]
179
+ const b = DAY[next < 0 ? DAY.length - 1 : next]
180
+ const t = b.hour > a.hour ? (h - a.hour) / (b.hour - a.hour) : 0
181
+ const sun = sunAt(h)
182
+ return {
183
+ sky: mixRgb(a.sky, b.sky, t),
184
+ clouds: { cover: 0.35, color: mixRgb(a.clouds, b.clouds, t) },
185
+ light: {
186
+ sunAround: sun.around,
187
+ sunUp: sun.up,
188
+ sun: mix(a.sun, b.sun, t),
189
+ sunColor: mixRgb(a.sunColor, b.sunColor, t),
190
+ ambient: mix(a.ambient, b.ambient, t),
191
+ ambientColor: mixRgb(a.ambientColor, b.ambientColor, t),
192
+ ground: mixRgb(a.ground, b.ground, t),
193
+ // the glow at dawn and dusk is the table's own
194
+ glow: false
195
+ },
196
+ fog: { start: 50, end: 170, color: mixRgb(a.fog, b.fog, t) },
197
+ mist: { ground: { base: 0, top: 1.8, density: mix(a.mist, b.mist, t), color: mixRgb(a.fog, b.fog, t) } },
198
+ lamps: { brightness: mix(a.lamps, b.lamps, t) }
199
+ }
200
+ }
201
+
202
+ /** How much the lamps are lit at an hour, 0 to 1 */
203
+ export const lampsAtHour = (hour: number): number => (lookAtHour(hour).lamps?.brightness as number) ?? 0
204
+
205
+ /** An hour as a clock shows it */
206
+ export const clock = (hour: number): string => {
207
+ const h = ((hour % 24) + 24) % 24
208
+ return `${String(Math.floor(h)).padStart(2, '0')}:${String(Math.floor((h % 1) * 60)).padStart(2, '0')}`
209
+ }
210
+
211
+ // ---- named looks, to cross-fade between
212
+
213
+ /** Looks of the world by name: what the looks example picks from. Each says all of what it changes, so any follows any */
214
+ export const LOOKS: Record<string, Partial<MapEnvironment>> = {
215
+ 'Summer noon': {
216
+ ...lookAtHour(12),
217
+ clouds: { cover: 0.3 },
218
+ cloudLayer: { height: 22, cover: 0.3, opacity: 0.5, shadows: 0.4 },
219
+ wind: { around: 100, strength: 0.3, gusts: 0.3 },
220
+ weather: { kind: 'rain', amount: 0 },
221
+ water: { shallow: [0.3, 0.62, 0.6], deep: [0.05, 0.2, 0.3], waves: { strength: 0.6 } }
222
+ },
223
+ 'Golden hour': {
224
+ ...lookAtHour(17.6),
225
+ light: { ...lookAtHour(17.6).light, sun: 1.35, ambient: 0.55 },
226
+ clouds: { cover: 0.45, color: [1, 0.72, 0.5] },
227
+ cloudLayer: { height: 24, cover: 0.35, opacity: 0.55, shadows: 0.35, color: [1, 0.8, 0.65] },
228
+ wind: { around: 80, strength: 0.2, gusts: 0.2 },
229
+ weather: { kind: 'rain', amount: 0 },
230
+ water: { shallow: [0.5, 0.6, 0.6], deep: [0.12, 0.2, 0.3], waves: { strength: 0.5 } }
231
+ },
232
+ 'Blue night': {
233
+ ...lookAtHour(0.5),
234
+ clouds: { cover: 0.2, color: [0.25, 0.28, 0.4] },
235
+ cloudLayer: { height: 22, cover: 0.2, opacity: 0.3, shadows: 0.2 },
236
+ wind: { around: 80, strength: 0.15, gusts: 0.2 },
237
+ weather: { kind: 'rain', amount: 0 },
238
+ water: { shallow: [0.12, 0.2, 0.32], deep: [0.02, 0.04, 0.1], waves: { strength: 0.4 } }
239
+ },
240
+ 'Grey storm': {
241
+ sky: [0.24, 0.27, 0.32],
242
+ clouds: { cover: 0.95, speed: 2.5, color: [0.4, 0.42, 0.46] },
243
+ cloudLayer: { height: 14, cover: 0.85, opacity: 0.75, shadows: 0.6, scale: 18 },
244
+ light: {
245
+ sunUp: 35,
246
+ sunAround: 200,
247
+ sun: 0.45,
248
+ sunColor: [0.75, 0.82, 1],
249
+ ambient: 0.4,
250
+ ambientColor: [0.6, 0.66, 0.8],
251
+ ground: [0.15, 0.15, 0.17],
252
+ glow: false
253
+ },
254
+ wind: { around: 120, strength: 0.95, gusts: 0.9 },
255
+ weather: { kind: 'rain', amount: 1, splashes: 1, size: 1.2, speed: 1.3 },
256
+ fog: { start: 25, end: 95, color: [0.3, 0.33, 0.38] },
257
+ mist: { ground: { base: 0, top: 2.5, density: 0.4, color: [0.4, 0.43, 0.5] } },
258
+ lamps: { brightness: 0.8 },
259
+ water: { shallow: [0.25, 0.32, 0.34], deep: [0.06, 0.09, 0.12], waves: { strength: 1, length: 2.2, speed: 2 } }
260
+ },
261
+ 'Winter morning': {
262
+ sky: [0.78, 0.82, 0.9],
263
+ clouds: { cover: 0.7, color: [0.95, 0.95, 0.98] },
264
+ cloudLayer: { height: 20, cover: 0.5, opacity: 0.45, shadows: 0.3 },
265
+ light: {
266
+ sunUp: 18,
267
+ sunAround: 120,
268
+ sun: 1,
269
+ sunColor: [1, 0.9, 0.8],
270
+ ambient: 0.75,
271
+ ambientColor: [0.85, 0.9, 1],
272
+ ground: [0.5, 0.52, 0.56],
273
+ glow: false
274
+ },
275
+ wind: { around: 60, strength: 0.25, gusts: 0.3 },
276
+ weather: { kind: 'snow', amount: 0.55, settle: 1 },
277
+ fog: { start: 40, end: 140, color: [0.86, 0.88, 0.92] },
278
+ mist: { ground: { base: 0, top: 2, density: 0.3, color: [0.92, 0.93, 0.96] } },
279
+ lamps: { brightness: 0.3 },
280
+ water: { shallow: [0.55, 0.68, 0.72], deep: [0.15, 0.25, 0.32], waves: { strength: 0.25 } }
281
+ },
282
+ 'Autumn haze': {
283
+ sky: [0.85, 0.7, 0.52],
284
+ clouds: { cover: 0.5, color: [1, 0.85, 0.7] },
285
+ cloudLayer: { height: 24, cover: 0.3, opacity: 0.4, shadows: 0.25, color: [1, 0.9, 0.75] },
286
+ light: {
287
+ sunUp: 28,
288
+ sunAround: 230,
289
+ sun: 1.15,
290
+ sunColor: [1, 0.75, 0.48],
291
+ ambient: 0.5,
292
+ ambientColor: [0.95, 0.8, 0.65],
293
+ ground: [0.3, 0.22, 0.15],
294
+ glow: false
295
+ },
296
+ wind: { around: 220, strength: 0.35, gusts: 0.5 },
297
+ weather: { kind: 'rain', amount: 0 },
298
+ fog: { start: 25, end: 120, color: [0.85, 0.66, 0.46] },
299
+ mist: { ground: { base: 0, top: 3, density: 0.35, color: [0.9, 0.72, 0.52] } },
300
+ lamps: { brightness: 0.2 },
301
+ water: { shallow: [0.45, 0.48, 0.35], deep: [0.12, 0.15, 0.12], waves: { strength: 0.4 } }
302
+ },
303
+ 'Eerie green': {
304
+ sky: [0.2, 0.3, 0.22],
305
+ clouds: { cover: 0.8, color: [0.45, 0.6, 0.45] },
306
+ cloudLayer: { height: 16, cover: 0.6, opacity: 0.6, shadows: 0.5, color: [0.6, 0.8, 0.6] },
307
+ light: {
308
+ sunUp: 30,
309
+ sunAround: 300,
310
+ sun: 0.7,
311
+ sunColor: [0.7, 1, 0.7],
312
+ ambient: 0.4,
313
+ ambientColor: [0.5, 0.8, 0.55],
314
+ ground: [0.1, 0.16, 0.1],
315
+ glow: false
316
+ },
317
+ wind: { around: 0, strength: 0.1, gusts: 0.6 },
318
+ weather: { kind: 'rain', amount: 0 },
319
+ fog: { start: 15, end: 90, color: [0.25, 0.38, 0.26] },
320
+ mist: { ground: { base: 0, top: 3.5, density: 0.6, color: [0.4, 0.6, 0.42] } },
321
+ lamps: { brightness: 1 },
322
+ water: { shallow: [0.22, 0.4, 0.2], deep: [0.04, 0.1, 0.05], waves: { strength: 0.2 } }
323
+ }
324
+ }