@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
@@ -3,6 +3,7 @@ import { Color3 } from '@babylonjs/core/Maths/math.color.js'
3
3
  import { CreateCylinder } from '@babylonjs/core/Meshes/Builders/cylinderBuilder.js'
4
4
  import type { AbilityLooks, ModelDef, ShotContext, ShotDrawing, UnitLooks } from '@v12-engine/client'
5
5
  import { building } from './buildings'
6
+ import { DUAL_WIELD, ONE_HANDED } from './attacks'
6
7
 
7
8
  /*
8
9
  * How the Restless looks (features-plan C4): the shared art's skeletons with their own gear, and its Fantasy RTS
@@ -68,7 +69,7 @@ export const undeadLooks: UnitLooks = {
68
69
  blood: false,
69
70
  height: 1.5,
70
71
  model: skeleton('characters/skeleton_minion.glb', 0.6, 'skeleton-gear/staff.glb'),
71
- parts: [
72
+ shapes: [
72
73
  ...bones(1.5),
73
74
  { shape: 'sphere', size: [0.42, 0.36, 0.42], offset: [0, 0.75, -0.34], color: GOLD, carrying: 'gold' },
74
75
  { shape: 'cylinder', size: [0.22, 0.9, 0.22], offset: [0, 0.8, -0.34], color: WOOD, tilt: Math.PI / 2, turn: Math.PI / 2, carrying: 'lumber' }
@@ -78,9 +79,10 @@ export const undeadLooks: UnitLooks = {
78
79
  blood: false,
79
80
  height: 1.6,
80
81
  model: skeleton('characters/skeleton_minion.glb', 0.68, 'skeleton-gear/blade.glb', 'skeleton-gear/blade.glb'),
82
+ attacks: [DUAL_WIELD],
81
83
  // packs of them: drawn as copies of one mesh, their animations baked (S41)
82
84
  baked: true,
83
- parts: bones(1.6)
85
+ shapes: bones(1.6)
84
86
  },
85
87
  cryptCrawler: {
86
88
  blood: false,
@@ -88,23 +90,24 @@ export const undeadLooks: UnitLooks = {
88
90
  model: {
89
91
  ...skeleton('characters/skeleton_rogue.glb', 0.75, 'skeleton-gear/crossbow.glb'),
90
92
  animations: { attack: 'Ranged_2H_Shoot' },
91
- hitAt: 0.3
93
+ clips: { Ranged_2H_Shoot: { marks: [{ name: 'hit', at: 0.3 }] } }
92
94
  },
93
95
  shot: { color: [0.55, 0.85, 0.45], size: 0.25 },
94
- parts: bones(1.7)
96
+ shapes: bones(1.7)
95
97
  },
96
98
  abomination: {
97
99
  blood: false,
98
100
  height: 2.6,
99
101
  model: skeleton('characters/skeleton_warrior.glb', 1.2, 'skeleton-gear/axe.glb', 'skeleton-gear/shield_large_a.glb'),
100
- parts: bones(2.6)
102
+ attacks: [ONE_HANDED],
103
+ shapes: bones(2.6)
101
104
  },
102
105
  necromancer: {
103
106
  blood: false,
104
107
  height: 1.8,
105
108
  model: { ...skeleton('characters/skeleton_mage.glb', 0.75, 'skeleton-gear/staff.glb'), animations: { attack: 'Ranged_Magic_Shoot' } },
106
109
  shot: { color: [0.55, 0.85, 0.45], size: 0.3 },
107
- parts: bones(1.8)
110
+ shapes: bones(1.8)
108
111
  },
109
112
  gargoyle: {
110
113
  blood: false,
@@ -112,40 +115,58 @@ export const undeadLooks: UnitLooks = {
112
115
  model: skeleton('characters/skeleton_minion.glb', 0.75, 'skeleton-gear/axe.glb'),
113
116
  altitude: 3.2,
114
117
  shot: { color: [0.6, 0.6, 0.65], size: 0.3 },
115
- parts: bones(1.6)
118
+ shapes: bones(1.6)
116
119
  },
117
- deathKnight: {
120
+ // ---- the Restless' heroes (`heroes.ts`)
121
+ gravelord: {
118
122
  blood: false,
119
123
  height: 2.3,
120
124
  model: skeleton('characters/skeleton_warrior.glb', 0.95, 'skeleton-gear/blade.glb', 'skeleton-gear/shield_small_a.glb'),
121
- parts: bones(2.3)
125
+ // a hero's blade: a streak of blood behind it
126
+ attacks: [{ ...ONE_HANDED, trail: { color: [0.8, 0.15, 0.2], width: 0.16 } }],
127
+ shapes: bones(2.3)
122
128
  },
123
- lich: {
129
+ rimeWitch: {
124
130
  blood: false,
125
131
  height: 2.2,
126
132
  model: { ...skeleton('characters/skeleton_mage.glb', 1, 'skeleton-gear/staff.glb'), animations: { attack: 'Ranged_Magic_Shoot' } },
127
133
  shot: { color: [0.55, 0.85, 1], size: 0.35 },
128
- parts: bones(2.2)
134
+ shapes: bones(2.2)
129
135
  },
130
- gravehold: { height: 4.6, model: building('rts/wooden_temple.glb', 3.6), parts: crypt(3.6, 4.6) },
131
- hallOfBones: { height: 5.6, model: building('rts/fortress.glb', 3.7), parts: crypt(3.7, 5.6) },
132
- duskCitadel: { height: 6.6, model: building('rts/wooden_fortress.glb', 3.9), parts: crypt(3.8, 6.6) },
136
+ duskblade: {
137
+ blood: false,
138
+ height: 2.1,
139
+ model: skeleton('characters/skeleton_rogue.glb', 0.9, 'skeleton-gear/blade.glb', 'skeleton-gear/blade.glb'),
140
+ // two blades: both together, and each alone, with a dusk-violet streak
141
+ attacks: [{ ...DUAL_WIELD, trail: { color: [0.6, 0.35, 0.9], width: 0.12 } }],
142
+ shapes: bones(2.1)
143
+ },
144
+ boneReaver: {
145
+ blood: false,
146
+ height: 2.6,
147
+ model: skeleton('characters/skeleton_warrior.glb', 1.1, 'skeleton-gear/axe.glb'),
148
+ attacks: [{ ...ONE_HANDED, trail: { color: [0.9, 0.85, 0.7], width: 0.18 } }],
149
+ shapes: bones(2.6)
150
+ },
151
+ gravehold: { height: 4.6, model: building('rts/wooden_temple.glb', 3.6), shapes: crypt(3.6, 4.6) },
152
+ hallOfBones: { height: 5.6, model: building('rts/fortress.glb', 3.7), shapes: crypt(3.7, 5.6) },
153
+ duskCitadel: { height: 6.6, model: building('rts/wooden_fortress.glb', 3.9), shapes: crypt(3.8, 6.6) },
133
154
  // over the gold mine's own model, a little bigger
134
- cursedMine: { height: 3.4, model: building('rts/mine.glb', 4), parts: crypt(4, 3.4) },
135
- boneSpire: { height: 2.4, model: building('rts/wooden_monument.glb', 1.9), parts: crypt(1.7, 2.4) },
136
- crypt: { height: 3.2, model: building('rts/storage_house.glb', 2.7), parts: crypt(2.7, 3.2) },
137
- graveyard: { height: 3, model: building('rts/houses.glb', 2.7), parts: crypt(2.7, 3) },
138
- darkAltar: { height: 3.6, model: building('rts/temple.glb', 2.8), parts: crypt(2.8, 3.6) },
139
- slaughterhouse: { height: 3.4, model: building('rts/storage_shed.glb', 2.7), parts: crypt(2.7, 3.4) },
140
- temple: { height: 3.6, model: building('rts/temple_2.glb', 2.6), parts: crypt(2.6, 3.6) },
155
+ cursedMine: { height: 3.4, model: building('rts/mine.glb', 4), shapes: crypt(4, 3.4) },
156
+ boneSpire: { height: 2.4, model: building('rts/wooden_monument.glb', 1.9), shapes: crypt(1.7, 2.4) },
157
+ crypt: { height: 3.2, model: building('rts/storage_house.glb', 2.7), shapes: crypt(2.7, 3.2) },
158
+ graveyard: { height: 3, model: building('rts/houses.glb', 2.7), shapes: crypt(2.7, 3) },
159
+ darkAltar: { height: 3.6, model: building('rts/temple.glb', 2.8), shapes: crypt(2.8, 3.6) },
160
+ slaughterhouse: { height: 3.4, model: building('rts/storage_shed.glb', 2.7), shapes: crypt(2.7, 3.4) },
161
+ temple: { height: 3.6, model: building('rts/temple_2.glb', 2.6), shapes: crypt(2.6, 3.6) },
141
162
  spiritTower: {
142
163
  height: 4.4,
143
164
  model: building('rts/stone_tower_2.glb', 1.9, { points: { launch: { offset: [0, 3, 0] } } }),
144
165
  // a beam of the dead's green from the tower's top to where the shot is: drawn by melee, not the client
145
166
  shot: { draw: beam([0.55, 0.95, 0.45]) },
146
- parts: crypt(1.5, 4.4)
167
+ shapes: crypt(1.5, 4.4)
147
168
  },
148
- tomb: { height: 3.2, model: building('rts/archery_towers.glb', 2.7), parts: crypt(2.7, 3.2) }
169
+ tomb: { height: 3.2, model: building('rts/archery_towers.glb', 2.7), shapes: crypt(2.7, 3.2) }
149
170
  }
150
171
 
151
172
  /** What the Restless' buildings are darkened with */
@@ -173,14 +194,32 @@ for (const id of [
173
194
  }
174
195
 
175
196
  export const undeadEffects: AbilityLooks = {
176
- deathCoil: { color: [0.55, 0.85, 0.45] },
177
- animateDead: { color: [0.55, 0.85, 0.45], style: 'burst', options: { count: 80, rise: 2.5, shape: 'rise' } },
178
- darkCharge: { color: [0.4, 0.3, 0.55] },
179
- unholy: { color: [0.55, 0.85, 0.45] },
180
- frostNova: { color: [0.55, 0.85, 1], style: 'burst', options: { count: 60, ring: 2.5, shape: 'about' } },
181
- frostArmor: { color: [0.62, 0.86, 1] },
182
- chilled: { color: [0.62, 0.86, 1] },
183
- deathAndDecay: { color: [0.45, 0.6, 0.3] },
197
+ // the Restless' heroes
198
+ graveLash: { color: [0.75, 0.15, 0.2] },
199
+ graveChill: { color: [0.55, 0.3, 0.45] },
200
+ siphon: { color: [0.8, 0.1, 0.2], style: 'tether' },
201
+ deathless: { color: [0.55, 0.85, 0.45] },
202
+ armyOfTheGrave: { color: [0.55, 0.85, 0.45], style: 'burst', options: { count: 80, rise: 2.5, shape: 'rise' } },
203
+ graveRallied: { color: [0.8, 0.2, 0.25] },
204
+ bloodTithe: { color: [0.7, 0.1, 0.15] },
205
+ rimeBolt: { color: [0.55, 0.85, 1] },
206
+ frozen: { color: [0.62, 0.86, 1] },
207
+ grasp: { color: [0.62, 0.86, 1], style: 'burst', options: { count: 60, ring: 2.5, shape: 'about' } },
208
+ iceRooted: { color: [0.62, 0.86, 1] },
209
+ frostbitten: { color: [0.62, 0.86, 1] },
210
+ whiteout: { color: [0.85, 0.93, 1], style: 'mark', options: { lasts: 5, radius: 4.5, sparks: 30 } },
211
+ whiteoutChill: { color: [0.85, 0.93, 1] },
212
+ duskMark: { color: [0.6, 0.35, 0.9] },
213
+ faded: { color: [0.45, 0.35, 0.6] },
214
+ shadowed: { color: [0.35, 0.25, 0.5] },
215
+ hushed: { color: [0.6, 0.35, 0.9] },
216
+ eclipse: { color: [0.4, 0.2, 0.6], style: 'burst', options: { count: 50, shape: 'about' } },
217
+ eclipsed: { color: [0.4, 0.2, 0.6] },
218
+ boneCleaver: { color: [0.9, 0.85, 0.7], style: 'burst', options: { count: 50, ring: 3, shape: 'about' } },
219
+ gorge: { color: [0.75, 0.15, 0.2] },
220
+ bonePit: { color: [0.9, 0.85, 0.7], style: 'mark', options: { lasts: 4, radius: 4.5, sparks: 24 } },
221
+ bonePitFrenzy: { color: [0.9, 0.85, 0.7] },
222
+ harvest: { color: [0.75, 0.15, 0.2] },
184
223
  unholyFrenzy: { color: [0.9, 0.3, 0.25] },
185
224
  crippled: { color: [0.5, 0.35, 0.6] },
186
225
  webbed: { color: [0.9, 0.9, 0.85] },
@@ -6,7 +6,7 @@ import { blightLayer } from '../blight'
6
6
  type CardButtons = NonNullable<NonNullable<GameView['hud']>['cardButtons']>
7
7
 
8
8
  /**
9
- * The button of a gate of the player's (`withWalls`, `wall.gate`): Open while it is shut, Shut while it is open, for
9
+ * The button of a gate of the player's (`wallsPack`, `wall.gate`): Open while it is shut, Shut while it is open, for
10
10
  * everyone (`wallGateCommand`). On the card of the gate picked first, once it stands
11
11
  */
12
12
  export const gateButtons: CardButtons = ({ primary }) => {
package/starters.mjs CHANGED
@@ -21,6 +21,12 @@ export const starters = [
21
21
  title: 'A campaign',
22
22
  summary: 'three missions with a hero carried between them: a base to defend, a siege of waves at night, a warlord to face',
23
23
  needs: ['assets']
24
+ },
25
+ {
26
+ name: 'examples',
27
+ title: 'Examples',
28
+ summary: "what the engine can do, a small scene each, browsed from a list as three.js's examples are: to read, copy and change",
29
+ needs: ['assets']
24
30
  }
25
31
  ]
26
32
 
package/v12.mjs CHANGED
@@ -6,22 +6,22 @@
6
6
  * | merge <base> <ours> <theirs> | generate <game> <name> [--generator id] [--seed N] …
7
7
  * v12 save diff <a> <b>
8
8
  *
9
- * The project is read as its page reads it, through its own Vite, so its game (`src/game.ts`: every game defined there,
10
- * `defineGame`) and a script for `map run` are TypeScript with nothing built first. Its maps are in `src/maps`
9
+ * The project is read as its page reads it, through its own Vite, so its game (`src/game.ts`: every game made there,
10
+ * `createGame`) and a script for `map run` are TypeScript with nothing built first. Its maps are in `src/maps`
11
11
  */
12
- import { readFileSync } from 'node:fs'
12
+ import { existsSync, readFileSync } from 'node:fs'
13
13
  import { createRequire } from 'node:module'
14
14
  import { join, resolve } from 'node:path'
15
15
  import { pathToFileURL } from 'node:url'
16
16
 
17
17
  const [command, ...args] = process.argv.slice(2)
18
18
 
19
- /** The project's games: every game defined in its `src/game.ts` (what `defineGame` gives: an id, rules and a view) */
20
- function gamesOf(module) {
19
+ /** The project's games: every game made in its `src/game.ts` (what `createGame` gives), its rules and its view fetched */
20
+ async function gamesOf(module) {
21
21
  const games = {}
22
22
  for (const value of Object.values(module)) {
23
- if (value && typeof value === 'object' && typeof value.id === 'string' && value.rules && value.view) {
24
- games[value.id] = { module: value.rules, view: value.view }
23
+ if (value && typeof value === 'object' && typeof value.id === 'string' && typeof value.toRules === 'function' && value.hasView) {
24
+ games[value.id] = { module: value.toRules(), view: await value.toView() }
25
25
  }
26
26
  }
27
27
  return games
@@ -38,14 +38,13 @@ async function map(project) {
38
38
  logLevel: 'error'
39
39
  })
40
40
  try {
41
- const games = gamesOf(await server.ssrLoadModule('/src/game.ts'))
42
- if (!Object.keys(games).length) throw new Error('src/game.ts defines no game (`export const game = defineGame({ rules, view })`)')
43
- // the map editor's: a project made without the editor has no map command
44
- try {
45
- require.resolve('@v12-engine/editor/map-command')
46
- } catch {
47
- throw new Error("v12 map is the map editor's: this project has none. npm install @v12-engine/editor")
48
- }
41
+ const games = await gamesOf(await server.ssrLoadModule('/src/game.ts'))
42
+ if (!Object.keys(games).length) throw new Error('src/game.ts makes no game (`export const game = createGame({ id, name })`, with its view)')
43
+ // the map editor's: a project made without the editor has no map command. Whether the package is there, not whether
44
+ // its command resolves: in the library's own repository the packages are read as they are written, nothing built,
45
+ // and the command's file resolves through Vite below, not through Node
46
+ const editor = (require.resolve.paths('@v12-engine/editor') ?? []).some((dir) => existsSync(join(dir, '@v12-engine/editor/package.json')))
47
+ if (!editor) throw new Error("v12 map is the map editor's: this project has none. npm install @v12-engine/editor")
49
48
  const { mapCommand } = await server.ssrLoadModule('@v12-engine/editor/map-command')
50
49
  return await mapCommand(args, {
51
50
  games,
@@ -1,20 +0,0 @@
1
- /**
2
- * The rules: the campaign's, in `src/campaign`. Its units, its missions and its waves are in that folder; this is the
3
- * name every part of the project knows them by
4
- */
5
- import type { GameMap, World } from '@v12-engine/engine'
6
- import { campaign, startMission } from './campaign'
7
-
8
- export { ENEMY, PLAYER, UNIT } from './campaign'
9
-
10
- export const rules = campaign
11
-
12
- /**
13
- * What the game does once a world is made from a map, before anyone plays: the map's mission is given (each map is
14
- * a mission's; a map of none plays on with no objectives). The page's free play, the server, the tests and the
15
- * editor's Play here each call it with the map; the campaign's own scene gives the mission itself, knowing whether
16
- * the game is new or loaded from a save
17
- */
18
- export function start(world: World, { map }: { map?: GameMap }): void {
19
- startMission(world, map)
20
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * The maps of the game. There is one, made here in code: a flat field with a player on it and a road across it.
3
- // @if editor
4
- * The map editor (`npm run dev`, then `/?editor`) opens it, and Save writes it into `src/maps/field.vgmap.json`. From
5
- * then on the file is the map: import it here in its place, as the lines below say, and `flatField` can go
6
- // @end
7
- */
8
- import { createMap, type GameMap } from '@v12-engine/engine'
9
- import { tiles } from './tiles'
10
- // @if editor
11
- // import { mapsFrom } from '@v12-engine/engine'
12
- // import field from './maps/field.vgmap.json'
13
- // @end
14
-
15
- /** A flat field of 32 cells a side, a player with a start in its middle, and a road across it */
16
- function flatField(): GameMap {
17
- const map = createMap({ name: 'Field', width: 32, depth: 32, players: 1, game: 'my-game', tileset: tiles.map((tile) => tile.id) })
18
- map.players[0].start = { x: 16, z: 16 }
19
- // a cell's paint is the place of its tile in the map's tileset, from 1 (0 is none: the ground's own colour)
20
- const road = map.tileset.indexOf('road') + 1
21
- for (let z = 20; z < 22; z++) for (let x = 0; x < map.width; x++) map.tiles[z * map.width + x] = road
22
- return map
23
- }
24
-
25
- /** The maps by name */
26
- // @if editor
27
- // once the editor has saved the field: mapsFrom({ './maps/field.vgmap.json': field })
28
- // @end
29
- export const maps: Readonly<Record<string, GameMap>> = { field: flatField() }
30
-
31
- // @if editor
32
- /** The map a game is on when nothing says which, and the one the editor opens first */
33
- // @end
34
- // @if !editor
35
- /** The map a game is on when nothing says which */
36
- // @end
37
- export const DEFAULT_MAP = 'field'
38
-
39
- /** A map by name, as a copy: a game changes nothing of what the next one starts from */
40
- export function mapNamed(name = DEFAULT_MAP): GameMap {
41
- const map = maps[name]
42
- if (!map) throw new Error(`There is no map "${name}": the maps are ${Object.keys(maps).join(', ')}`)
43
- return structuredClone(map)
44
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * What the ground can be painted with: the rules' names for it, which the map editor's Paint offers and the rules read
3
- * (`world.registry.tile('road')`). How each looks is the game's look's (`tiles` in `src/game.ts`). A new one is a line
4
- * here and one there; a map keeps its paint by these names, so their order can change
5
- */
6
- import { presetTiles } from '@v12-engine/engine'
7
-
8
- /**
9
- * The engine's tiles of an RTS (grass, dark grass, dirt, road, sand, rock, snow), drawn with the client's `TILE_LOOKS`.
10
- * A game's own goes beside them: `defineTile({ id: 'lava', name: 'Lava' })` here, its look in `src/game.ts`
11
- */
12
- export const tiles = [...presetTiles]
@@ -1,38 +0,0 @@
1
- import type { AbilityDef, BuffDef } from '@v12-engine/engine'
2
-
3
- /** The spells every hero brings along (`HeroDef.spells`): learned from the start, one point each, on long cooldowns */
4
-
5
- export const heroSpellBuffs: BuffDef[] = [
6
- { id: 'healHaste', name: 'Second Wind', description: 'Moves 30% faster', duration: 1, percent: { moveSpeed: 0.3 } },
7
- { id: 'barrier', name: 'Barrier', description: 'A shield takes the damage', duration: 2.5, shield: 120, shieldScaling: { level: 21.2 } }
8
- ]
9
-
10
- export const heroSpells: AbilityDef[] = [
11
- {
12
- id: 'flash',
13
- name: 'Flash',
14
- description: 'At once a short way towards the pointer, through whatever is in between',
15
- target: 'direction',
16
- cooldown: 300,
17
- effects: [{ type: 'blink', distance: 5.3 }]
18
- },
19
- {
20
- id: 'secondWind',
21
- name: 'Second Wind',
22
- description: 'The hero and allied heroes nearby regain 80 to 318 health, more with every level, and run 30% faster for a second',
23
- target: 'none',
24
- cooldown: 240,
25
- effects: [
26
- { type: 'heal', amount: 80, scaling: { level: 14 }, radius: 10.9, only: 'heroes' },
27
- { type: 'buff', buff: 'healHaste', radius: 10.9, only: 'heroes' }
28
- ]
29
- },
30
- {
31
- id: 'barrier',
32
- name: 'Barrier',
33
- description: 'A shield for 2.5 seconds that takes 120 to 480 damage, more with every level',
34
- target: 'none',
35
- cooldown: 180,
36
- effects: [{ type: 'buff', buff: 'barrier', on: 'caster' }]
37
- }
38
- ]
@@ -1,24 +0,0 @@
1
- /**
2
- * The rules: melee's, in `src/melee`. Its races, heroes, units, buildings and the generator of its random maps are
3
- * in that folder; this is the name every part of the project knows them by
4
- */
5
- import type { GameMap, PlayerId, World } from '@v12-engine/engine'
6
- import { melee } from './melee'
7
-
8
- export const rules = melee
9
-
10
- /**
11
- * What the game does once a world is made from a map, before anyone plays. Every player with a start is given a hall
12
- * and five workers by the rules themselves; in the page, the computer plays everyone but you, each against the other
13
- * teams' starts. A server seats its computers itself (`server.ts`), so there is no one to seat here
14
- */
15
- export function start(world: World, { map, player }: { map?: GameMap; player?: PlayerId }): void {
16
- if (!map || player === undefined) return
17
- const players = map.players.filter((other) => other.start)
18
- for (const other of players) {
19
- if (other.id === player) continue
20
- const enemyBases = players.filter((rival) => rival.team !== other.team).map((rival) => rival.start!)
21
- // remembered, so that a game loaded from a save of this one plays on against them
22
- world.computers.seat(other.id, { enemyBases, settings: { army: { firstAttack: 360, waveSize: 6 } } })
23
- }
24
- }