@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,71 @@
1
+ /**
2
+ * What the abilities examples share on the client's side (the MOBA ones too): a look laid into the game's (units and
3
+ * abilities by id), a button that casts an ability, and the camera and selection an example starts with
4
+ */
5
+ import type { AbilityDef } from '@v12-engine/engine'
6
+ import type { AbilityLook, GameClientApi, GameView, UnitLook } from '@v12-engine/client'
7
+ import { button, type ExampleControl } from '../../shell/look'
8
+ import { cast, release, reset } from './kit'
9
+
10
+ type Color = [number, number, number]
11
+
12
+ /** An ability's or a buff's look: a colour, a style of the client's (`ring` when not said) and anything else */
13
+ export const look = (color: Color, style?: string, more: Partial<AbilityLook> = {}): AbilityLook => ({ color, ...(style && { style }), ...more })
14
+
15
+ /** The game's look with these units' and abilities' (and buffs') looks laid over its own */
16
+ export function withLooks(view: GameView<any>, units: Record<string, UnitLook>, abilities: Record<string, AbilityLook>): GameView<any> {
17
+ const fx = view.fx ?? {}
18
+ return {
19
+ ...view,
20
+ units: { ...view.units, looks: { ...view.units?.looks, ...units } },
21
+ fx: { ...fx, abilities: { ...fx.abilities, looks: { ...fx.abilities?.looks, ...abilities } } }
22
+ }
23
+ }
24
+
25
+ /**
26
+ * A button casting an ability, named as it is, its description the hint (or what it is, where it has none): aimed for
27
+ * the player (`castFor`)
28
+ */
29
+ export const castButton = (
30
+ ability: AbilityDef,
31
+ options: { by?: string; at?: string; point?: { x: number; z: number }; label?: string } = {}
32
+ ): ExampleControl =>
33
+ button(
34
+ options.label ?? ability.name,
35
+ (client) =>
36
+ client.session.send(
37
+ cast.make({
38
+ ability: ability.id,
39
+ ...(options.by && { by: options.by }),
40
+ ...(options.at && { at: options.at }),
41
+ ...(options.point && { point: options.point })
42
+ })
43
+ ),
44
+ ability.description ?? `Casts ${ability.name}`
45
+ )
46
+
47
+ /** A button letting go of what the caster holds (hold to charge, release to fire) */
48
+ export const releaseButton = (label = 'Release', by?: string): ExampleControl =>
49
+ button(
50
+ label,
51
+ (client) => client.session.send(release.make({ ...(by && { by }) })),
52
+ 'Lets go of what is held: it fires as far and as hard as it charged'
53
+ )
54
+
55
+ /** A button laying the example out again as it started */
56
+ export const resetButton = (example: string): ExampleControl =>
57
+ button('Start again', (client) => client.session.send(reset.make({ example })), 'Takes everything away and lays the example out again')
58
+
59
+ /** A box of the ground, in cells */
60
+ export type Box = { minX: number; minZ: number; maxX: number; maxZ: number }
61
+
62
+ /**
63
+ * Makes the example ready: the camera framing `box` (the caster and its targets as `lineUp` lays them, when not said),
64
+ * and the player's unit of `type` selected, so its card shows its abilities
65
+ */
66
+ export function readyOn(client: GameClientApi, type: string | undefined, box: Box = { minX: 18, minZ: 15, maxX: 30, maxZ: 29 }): void {
67
+ client.camera.frame(box)
68
+ if (!type) return
69
+ const unit = client.session.snapshot().entities.find((each) => each.type === type && each.owner === client.session.playerId)
70
+ if (unit) client.controller.select([unit.id])
71
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The abilities examples on the client's side: how their casters, targets and abilities look, and each example's card
3
+ * (a button casting each of its family's abilities). Each family's looks are its own file (`./damageLooks.ts`…)
4
+ */
5
+ import type { CategoryLooks, ExampleLook } from '../../shell/look'
6
+ import type { AbilityLook, GameView, UnitLook } from '@v12-engine/client'
7
+ import { areasLooks } from './areasLooks'
8
+ import { buffsLooks } from './buffsLooks'
9
+ import { changingLooks } from './changingLooks'
10
+ import { controlLooks } from './controlLooks'
11
+ import { damageLooks } from './damageLooks'
12
+ import { withLooks } from './lookKit'
13
+ import { missilesLooks } from './missilesLooks'
14
+ import { movesLooks } from './movesLooks'
15
+ import { recastsLooks } from './recastsLooks'
16
+ import { resourcesLooks } from './resourcesLooks'
17
+ import { summonsLooks } from './summonsLooks'
18
+ import { triggersLooks } from './triggersLooks'
19
+ import { visionLooks } from './visionLooks'
20
+
21
+ /** A family's looks: its units' and abilities', and its example's card */
22
+ export interface FamilyLooks {
23
+ units: Record<string, UnitLook>
24
+ abilities: Record<string, AbilityLook>
25
+ examples: Record<string, ExampleLook>
26
+ }
27
+
28
+ const all: FamilyLooks[] = [
29
+ damageLooks,
30
+ missilesLooks,
31
+ areasLooks,
32
+ controlLooks,
33
+ buffsLooks,
34
+ movesLooks,
35
+ summonsLooks,
36
+ triggersLooks,
37
+ recastsLooks,
38
+ resourcesLooks,
39
+ visionLooks,
40
+ changingLooks
41
+ ]
42
+
43
+ export const category: CategoryLooks = {
44
+ looks: Object.assign({}, ...all.map((each) => each.examples)),
45
+ view: (view: GameView<any>) =>
46
+ withLooks(view, Object.assign({}, ...all.map((each) => each.units)), Object.assign({}, ...all.map((each) => each.abilities)))
47
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Missiles: the `missile` effect in every shape it takes. A bolt that chases, a wave through a line, one that stops at
3
+ * the first, a rocket to a spot, a grenade over everything, lightning that bounces, a fan, an orb that comes back,
4
+ * wisps that seek, a bolt that splits and a hook that drags its catch in
5
+ */
6
+ import type { AbilityDef, BuffDef } from '@v12-engine/engine'
7
+ import { exampleField } from '../define'
8
+ import type { Family } from './family'
9
+ import { caster, put, sparring } from './kit'
10
+ import { ENEMY, PLAYER } from '../common/units'
11
+
12
+ const buffs: BuffDef[] = [
13
+ { id: 'abilitiesBoltStun', name: 'Stunned', harmful: true, duration: 1.5, stun: true },
14
+ { id: 'abilitiesBound', name: 'Bound', harmful: true, duration: 2, root: true },
15
+ { id: 'abilitiesHooked', name: 'Hooked', harmful: true, duration: 0.6, stun: true }
16
+ ]
17
+
18
+ const abilities: AbilityDef[] = [
19
+ {
20
+ // follows the unit it was cast on, wherever it goes: Storm Bolt
21
+ id: 'abilitiesStormBolt',
22
+ name: 'Storm Bolt',
23
+ description: 'A hammer that chases an enemy: {damage} and a stun',
24
+ hotkey: 'Q',
25
+ target: 'unit',
26
+ targets: { enemies: true },
27
+ range: 10,
28
+ cooldown: 1,
29
+ effects: [
30
+ {
31
+ type: 'missile',
32
+ homing: true,
33
+ speed: 14,
34
+ effects: [
35
+ { type: 'damage', amount: 100 },
36
+ { type: 'buff', buff: 'abilitiesBoltStun' }
37
+ ]
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ // toward a spot, through everything on its way (`pierce` is true unless said)
43
+ id: 'abilitiesShockwave',
44
+ name: 'Shockwave',
45
+ description: 'A wave along a line, through every enemy on it',
46
+ hotkey: 'W',
47
+ target: 'direction',
48
+ range: 12,
49
+ cooldown: 1,
50
+ effects: [{ type: 'missile', speed: 16, distance: 12, radius: 1, effects: [{ type: 'damage', amount: 80 }] }]
51
+ },
52
+ {
53
+ // stops at the first enemy it meets: a skillshot
54
+ id: 'abilitiesLightBinding',
55
+ name: 'Light Binding',
56
+ description: 'A bolt that stops at the first enemy it meets and roots it',
57
+ hotkey: 'E',
58
+ target: 'direction',
59
+ range: 12,
60
+ cooldown: 1,
61
+ effects: [{ type: 'missile', speed: 14, distance: 12, radius: 0.6, pierce: false, effects: [{ type: 'buff', buff: 'abilitiesBound' }] }]
62
+ },
63
+ {
64
+ // to the spot clicked, blowing up there
65
+ id: 'abilitiesRocket',
66
+ name: 'Rocket',
67
+ description: 'Flies to the spot and blows up there',
68
+ hotkey: 'R',
69
+ target: 'point',
70
+ range: 12,
71
+ cooldown: 1,
72
+ effects: [{ type: 'missile', speed: 20, distance: 12, toPoint: true, onEnd: [{ type: 'damage', amount: 120, radius: 2.5 }] }]
73
+ },
74
+ {
75
+ // thrown in an arc, over whatever is between: hits around where it lands
76
+ id: 'abilitiesGrenade',
77
+ name: 'Grenade',
78
+ description: 'Thrown high over everything, it hits around where it lands',
79
+ hotkey: 'T',
80
+ target: 'point',
81
+ range: 12,
82
+ cooldown: 1,
83
+ effects: [{ type: 'missile', toPoint: true, distance: 12, radius: 2, arc: { height: 4, time: 1 }, effects: [{ type: 'damage', amount: 110 }] }]
84
+ },
85
+ {
86
+ // springs on to the nearest it has not hit, a third weaker each time
87
+ id: 'abilitiesChainLightning',
88
+ name: 'Chain Lightning',
89
+ description: 'Lightning that springs from enemy to enemy, weaker each time',
90
+ hotkey: 'D',
91
+ target: 'unit',
92
+ targets: { enemies: true },
93
+ range: 10,
94
+ cooldown: 1,
95
+ effects: [{ type: 'missile', homing: true, speed: 30, bounce: { count: 4, range: 6, falloff: 0.25 }, effects: [{ type: 'damage', amount: 120 }] }]
96
+ },
97
+ {
98
+ // several in a fan: none hits what another already hit
99
+ id: 'abilitiesVolley',
100
+ name: 'Volley',
101
+ description: 'Seven arrows in a fan, each stopping at the first it meets',
102
+ hotkey: 'F',
103
+ target: 'direction',
104
+ range: 10,
105
+ cooldown: 1,
106
+ effects: [{ type: 'missile', count: 7, spread: Math.PI / 2.5, speed: 18, distance: 10, pierce: false, effects: [{ type: 'damage', amount: 45 }] }]
107
+ },
108
+ {
109
+ // out and back, hitting on the way back too
110
+ id: 'abilitiesOrb',
111
+ name: 'Orb of Deception',
112
+ description: 'An orb out and back again, hurting on both ways',
113
+ hotkey: 'G',
114
+ target: 'direction',
115
+ range: 10,
116
+ cooldown: 1,
117
+ effects: [{ type: 'missile', speed: 15, distance: 10, radius: 0.9, returns: true, hitsOnReturn: true, effects: [{ type: 'damage', amount: 50 }] }]
118
+ },
119
+ {
120
+ // each finds its own: they fly round and after the nearest enemy within 7
121
+ id: 'abilitiesFoxFire',
122
+ name: 'Fox-Fire',
123
+ description: 'Three wisps that find the nearest enemies by themselves',
124
+ hotkey: 'Z',
125
+ target: 'none',
126
+ cooldown: 1,
127
+ effects: [{ type: 'missile', count: 3, spread: 2 * Math.PI, speed: 14, distance: 9, seek: 7, effects: [{ type: 'damage', amount: 50 }] }]
128
+ },
129
+ {
130
+ // where it ends it splits in two, each going on aside
131
+ id: 'abilitiesFission',
132
+ name: 'Plasma Fission',
133
+ description: 'A bolt that splits in two where it stops, each half flying on aside',
134
+ hotkey: 'X',
135
+ target: 'direction',
136
+ range: 8,
137
+ cooldown: 1,
138
+ effects: [
139
+ {
140
+ type: 'missile',
141
+ speed: 14,
142
+ distance: 7,
143
+ pierce: false,
144
+ effects: [{ type: 'damage', amount: 60 }],
145
+ split: [
146
+ { type: 'missile', count: 2, spread: Math.PI / 1.2, speed: 14, distance: 7, look: 'halves', effects: [{ type: 'damage', amount: 60 }] }
147
+ ]
148
+ }
149
+ ]
150
+ },
151
+ {
152
+ // a chain written flat: the missile, then a pull on what it hits, then damage as it arrives
153
+ id: 'abilitiesHook',
154
+ name: 'Rocket Grab',
155
+ description: 'A hook to the first enemy it meets, dragged back and hurt as it arrives',
156
+ hotkey: 'C',
157
+ target: 'direction',
158
+ range: 12,
159
+ cooldown: 1,
160
+ effects: [
161
+ { type: 'missile', speed: 28, distance: 12, radius: 0.7, pierce: false, returns: true },
162
+ { type: 'pull', seconds: 0.6, when: 'hit' },
163
+ { type: 'damage', amount: 90, when: 'arrived' },
164
+ { type: 'buff', buff: 'abilitiesHooked', when: 'hit' }
165
+ ]
166
+ }
167
+ ]
168
+
169
+ export const missiles: Family = {
170
+ abilities,
171
+ buffs,
172
+ units: [
173
+ caster(
174
+ 'abilitiesArcher',
175
+ 'Missile mage',
176
+ abilities.map((each) => each.id)
177
+ ),
178
+ sparring('abilitiesStrafer', 'Sparring footman')
179
+ ],
180
+ example: {
181
+ id: 'abilities-missiles',
182
+ category: 'abilities',
183
+ title: 'Missiles',
184
+ summary:
185
+ 'Every kind of missile: homing, through a line, stopped by the first, to a spot, thrown, bouncing, in a fan, out and back, seeking, splitting and a hook. Press a button to fire it at the footmen across the field.',
186
+ tags: ['missile', 'projectile', 'skillshot', 'homing', 'bounce', 'chain', 'hook', 'arc', 'boomerang', 'split', 'seek'],
187
+ source: ['examples/abilities/missiles.ts', 'examples/abilities/kit.ts'],
188
+ map: () => exampleField({ name: 'Missiles', players: 2 }),
189
+ start: (world) => {
190
+ put(world, 'abilitiesArcher', PLAYER, { x: 24, z: 15 }, 'caster')
191
+ // a loose crowd across the field: one in front, others behind and aside, for lines, fans and bounces to find
192
+ const spots = [
193
+ [24, 23],
194
+ [22, 25.5],
195
+ [26.5, 25],
196
+ [24, 27.5],
197
+ [20, 28],
198
+ [28.5, 28.5]
199
+ ]
200
+ spots.forEach(([x, z], i) => put(world, 'abilitiesStrafer', ENEMY, { x, z }, `enemy${i + 1}`, Math.PI))
201
+ }
202
+ }
203
+ }
@@ -0,0 +1,38 @@
1
+ /** How the missiles example looks, and its card: a button for each missile (`./missiles.ts`) */
2
+ import { sharedLooks } from '../common/looks'
3
+ import type { FamilyLooks } from './looks'
4
+ import { castButton, look, readyOn, resetButton } from './lookKit'
5
+ import { missiles } from './missiles'
6
+
7
+ /** A missile's look: the missile style, flying as `shape` (an arrow, a rock, a bird of light or a ball of fire) */
8
+ const flying = (color: [number, number, number], shape: 'arrow' | 'rock' | 'hawk' | 'ball', more = {}) =>
9
+ look(color, 'missile', { options: { shape }, ...more })
10
+
11
+ export const missilesLooks: FamilyLooks = {
12
+ units: {
13
+ abilitiesArcher: sharedLooks.mage,
14
+ abilitiesStrafer: sharedLooks.footman
15
+ },
16
+ abilities: {
17
+ abilitiesStormBolt: flying([0.6, 0.8, 1], 'rock'),
18
+ abilitiesShockwave: flying([1, 0.75, 0.35], 'hawk'),
19
+ abilitiesLightBinding: flying([1, 0.95, 0.6], 'hawk'),
20
+ abilitiesRocket: flying([1, 0.45, 0.15], 'ball'),
21
+ abilitiesGrenade: flying([0.7, 0.6, 0.5], 'rock'),
22
+ abilitiesChainLightning: flying([0.55, 0.75, 1], 'hawk', { link: 'chain' }),
23
+ abilitiesVolley: flying([0.85, 0.75, 0.55], 'arrow'),
24
+ abilitiesOrb: flying([0.9, 0.45, 1], 'ball'),
25
+ abilitiesFoxFire: flying([0.4, 0.7, 1], 'hawk'),
26
+ // the halves a colour of their own (`look: 'halves'` on the split's missile)
27
+ abilitiesFission: flying([0.6, 0.35, 1], 'ball', { effects: { halves: { color: [1, 0.6, 0.15] } } }),
28
+ abilitiesHook: flying([0.75, 0.75, 0.8], 'rock'),
29
+ abilitiesBoltStun: look([1, 1, 0.6], undefined, { aura: 'glow', options: { shape: 'ring', at: 'head' } }),
30
+ abilitiesBound: look([1, 0.95, 0.6], undefined, { aura: 'glow', options: { shape: 'column' } })
31
+ },
32
+ examples: {
33
+ 'abilities-missiles': {
34
+ ready: (client) => readyOn(client, 'abilitiesArcher', { minX: 16, minZ: 13, maxX: 32, maxZ: 30 }),
35
+ controls: [...missiles.abilities.map((ability) => castButton(ability)), resetButton('abilities-missiles')]
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Moves: abilities that move units. The caster leaping, blinking, rushing to an enemy and through it, teleporting;
3
+ * enemies thrown back (into a wall, to be stunned), thrown into each other, thrown over, dragged in; two units trading
4
+ * places; the caster riding an ally. Most need the moves pack (`movesPack`, added in `./kit.ts`)
5
+ */
6
+ import type { AbilityDef, BuffDef } from '@v12-engine/engine'
7
+ import { exampleField } from '../define'
8
+ import { ENEMY, PLAYER } from '../common/units'
9
+ import type { Family } from './family'
10
+ import { caster, put } from './kit'
11
+
12
+ const buffs: BuffDef[] = [{ id: 'abilitiesWallStun', name: 'Slammed', harmful: true, duration: 1.5, stun: true }]
13
+
14
+ const abilities: AbilityDef[] = [
15
+ {
16
+ // a dash with a leap flies over what is between, and hurts round where it lands
17
+ id: 'abilitiesLeap',
18
+ name: 'Leap',
19
+ description: 'Jumps toward the spot and strikes where it lands',
20
+ hotkey: 'Q',
21
+ target: 'point',
22
+ range: 7,
23
+ cooldown: 0.5,
24
+ effects: [{ type: 'dash', distance: 7, speed: 16, leap: 2, effects: [{ type: 'damage', amount: 80, radius: 2 }] }]
25
+ },
26
+ {
27
+ // at once, up to its distance
28
+ id: 'abilitiesBlink',
29
+ name: 'Blink',
30
+ description: 'At once to the spot, up to 8 cells',
31
+ hotkey: 'W',
32
+ target: 'point',
33
+ range: 8,
34
+ cooldown: 0.5,
35
+ effects: [{ type: 'blink', distance: 8 }]
36
+ },
37
+ {
38
+ // home: where the side's home is (here its start)
39
+ id: 'abilitiesRecall',
40
+ name: 'Teleport home',
41
+ description: 'Back to where it started, however far',
42
+ hotkey: 'E',
43
+ target: 'none',
44
+ cooldown: 0.5,
45
+ castTime: 0.6,
46
+ effects: [{ type: 'teleport', to: 'home' }]
47
+ },
48
+ {
49
+ // rushes to the unit, to its edge, following it
50
+ id: 'abilitiesCharge',
51
+ name: 'Heroic Charge',
52
+ description: 'Rushes to an enemy and strikes it',
53
+ hotkey: 'R',
54
+ target: 'unit',
55
+ targets: { enemies: true },
56
+ range: 10,
57
+ cooldown: 0.5,
58
+ effects: [{ type: 'dash', to: 'target', speed: 22, effects: [{ type: 'damage', amount: 60 }] }]
59
+ },
60
+ {
61
+ // through the unit, 3 cells past its middle
62
+ id: 'abilitiesSweep',
63
+ name: 'Sweeping Blade',
64
+ description: 'Dashes through an enemy, cutting it',
65
+ hotkey: 'T',
66
+ target: 'unit',
67
+ targets: { enemies: true },
68
+ range: 8,
69
+ cooldown: 0.5,
70
+ effects: [{ type: 'dash', to: 'target', through: 3, speed: 20, effects: [{ type: 'damage', amount: 50 }] }]
71
+ },
72
+ {
73
+ // thrown back from the caster; a wall on the way stuns it
74
+ id: 'abilitiesCondemn',
75
+ name: 'Condemn',
76
+ description: 'Throws an enemy back: into the pillar behind it, it is stunned',
77
+ hotkey: 'D',
78
+ target: 'unit',
79
+ targets: { enemies: true },
80
+ range: 10,
81
+ cooldown: 0.5,
82
+ effects: [{ type: 'knockback', distance: 5, speed: 20, onWall: [{ type: 'buff', buff: 'abilitiesWallStun' }] }]
83
+ },
84
+ {
85
+ // what it is thrown into is hit and thrown aside too
86
+ id: 'abilitiesDragonsRage',
87
+ name: 'Dragon’s Rage',
88
+ description: 'Kicks an enemy into the others: each it meets is hurt and knocked up',
89
+ hotkey: 'F',
90
+ target: 'unit',
91
+ targets: { enemies: true },
92
+ range: 10,
93
+ cooldown: 0.5,
94
+ effects: [
95
+ {
96
+ type: 'knockback',
97
+ distance: 8,
98
+ speed: 16,
99
+ collide: [
100
+ { type: 'damage', amount: 70 },
101
+ { type: 'knockback', distance: 1.5, leap: 2, speed: 4 }
102
+ ]
103
+ }
104
+ ]
105
+ },
106
+ {
107
+ // every enemy round the caster, back 4 cells
108
+ id: 'abilitiesShove',
109
+ name: 'Shove',
110
+ description: 'Throws every enemy within 4 back, through the air',
111
+ hotkey: 'G',
112
+ target: 'none',
113
+ cooldown: 0.5,
114
+ effects: [{ type: 'knockback', distance: 4, speed: 12, radius: 4, leap: 1 }]
115
+ },
116
+ {
117
+ // over the caster, to land on its far side
118
+ id: 'abilitiesFlay',
119
+ name: 'Flay',
120
+ description: 'Throws the enemies near over the caster, to land behind it',
121
+ hotkey: 'Z',
122
+ target: 'none',
123
+ cooldown: 0.5,
124
+ effects: [{ type: 'knockback', direction: 'over', distance: 2.5, leap: 2, speed: 10, radius: 4 }]
125
+ },
126
+ {
127
+ // dragged in to where it landed: a vacuum
128
+ id: 'abilitiesVacuum',
129
+ name: 'Vacuum',
130
+ description: 'Drags every enemy within 4 of the spot into its middle',
131
+ hotkey: 'X',
132
+ target: 'point',
133
+ range: 10,
134
+ cooldown: 0.5,
135
+ effects: [{ type: 'pull', seconds: 0.5, radius: 4, toPoint: true, effects: [{ type: 'damage', amount: 40 }] }]
136
+ },
137
+ {
138
+ // the caster and the target trade places
139
+ id: 'abilitiesSwap',
140
+ name: 'Nether Swap',
141
+ description: 'The caster and an enemy trade places',
142
+ hotkey: 'C',
143
+ target: 'unit',
144
+ targets: { allies: true, enemies: true },
145
+ range: 12,
146
+ cooldown: 0.5,
147
+ effects: [{ type: 'swap' }]
148
+ },
149
+ {
150
+ // out of the world, riding the ally until it ends; healing round as it lets go
151
+ id: 'abilitiesAttach',
152
+ name: 'Ride an ally',
153
+ description: 'Rides the ally for 6 seconds, then heals round it as it lets go',
154
+ hotkey: 'V',
155
+ target: 'unit',
156
+ targets: { allies: true },
157
+ range: 10,
158
+ cooldown: 0.5,
159
+ effects: [{ type: 'attach', duration: 6, onEnd: [{ type: 'heal', amount: 100, radius: 3 }] }]
160
+ }
161
+ ]
162
+
163
+ export const moves: Family = {
164
+ abilities,
165
+ buffs,
166
+ units: [
167
+ caster(
168
+ 'abilitiesMover',
169
+ 'Duelist',
170
+ abilities.map((each) => each.id)
171
+ ),
172
+ // a wall to throw things into: a pillar that blocks the ground
173
+ {
174
+ id: 'abilitiesPillar',
175
+ name: 'Pillar',
176
+ kind: 'building',
177
+ radius: 1,
178
+ footprint: { w: 2, d: 2 },
179
+ stats: { health: 5000, armor: 50 },
180
+ invulnerable: true,
181
+ sight: 2
182
+ }
183
+ ],
184
+ example: {
185
+ id: 'abilities-moves',
186
+ category: 'abilities',
187
+ title: 'Dash, blink, knockback, pull',
188
+ summary:
189
+ 'Leap, blink, teleport home, rush to an enemy and through it; throw enemies back into a pillar, into each other, over you; drag them in; trade places; ride an ally. Press a button to see each.',
190
+ tags: ['dash', 'blink', 'leap', 'teleport', 'knockback', 'pull', 'swap', 'attach', 'collide', 'wall stun', 'movement'],
191
+ source: ['examples/abilities/moves.ts', 'examples/abilities/kit.ts'],
192
+ map: () => exampleField({ name: 'Moves', players: 2 }),
193
+ start: (world) => {
194
+ put(world, 'abilitiesMover', PLAYER, { x: 24, z: 16 }, 'caster')
195
+ put(world, 'abilitiesWounded', PLAYER, { x: 20, z: 16 }, 'ally')
196
+ put(world, 'abilitiesTarget', ENEMY, { x: 24, z: 22 }, 'enemy1', Math.PI)
197
+ put(world, 'abilitiesTarget', ENEMY, { x: 22.5, z: 25 }, 'enemy2', Math.PI)
198
+ put(world, 'abilitiesTarget', ENEMY, { x: 25.5, z: 25.5 }, 'enemy3', Math.PI)
199
+ put(world, 'abilitiesTarget', ENEMY, { x: 24, z: 28 }, 'enemy4', Math.PI)
200
+ // pillars behind them, to be thrown into
201
+ put(world, 'abilitiesPillar', world.neutral, { x: 24, z: 31 })
202
+ put(world, 'abilitiesPillar', world.neutral, { x: 20, z: 30 })
203
+ put(world, 'abilitiesPillar', world.neutral, { x: 28, z: 30 })
204
+ }
205
+ }
206
+ }
@@ -0,0 +1,52 @@
1
+ /** How the moves example looks, and its card: a button for each move (`./moves.ts`) */
2
+ import { character, hold, person } from '../common/looks'
3
+ import type { FamilyLooks } from './looks'
4
+ import { castButton, look, readyOn, resetButton } from './lookKit'
5
+ import { moves } from './moves'
6
+
7
+ /** Where each is aimed, where the nearest enemy is not it */
8
+ const aims: Record<string, { at?: string; point?: { x: number; z: number } }> = {
9
+ abilitiesLeap: { point: { x: 21, z: 22 } },
10
+ abilitiesBlink: { point: { x: 28, z: 20 } },
11
+ abilitiesSweep: { at: 'enemy2' },
12
+ abilitiesCondemn: { at: 'enemy4' },
13
+ abilitiesVacuum: { point: { x: 24, z: 25 } },
14
+ abilitiesSwap: { at: 'enemy4' },
15
+ abilitiesAttach: { at: 'ally' }
16
+ }
17
+
18
+ export const movesLooks: FamilyLooks = {
19
+ units: {
20
+ abilitiesMover: { height: 1.8, model: character('rogue', 0.72, { hold: [hold('dagger'), hold('dagger', 'l')] }), shapes: person(1.8) },
21
+ // the dungeon kit's pillar; its parts show while it loads
22
+ abilitiesPillar: {
23
+ height: 3.6,
24
+ model: { src: 'dungeon-kit/pillar.glb', scale: 0.9 },
25
+ shapes: [
26
+ { shape: 'cylinder', size: [1.6, 3, 1.6], offset: [0, 1.5, 0], color: [0.55, 0.52, 0.48] },
27
+ { shape: 'box', size: [2, 0.4, 2], offset: [0, 3.1, 0], color: [0.45, 0.42, 0.4] }
28
+ ]
29
+ }
30
+ },
31
+ abilities: {
32
+ abilitiesLeap: look([1, 0.7, 0.3], 'slam'),
33
+ abilitiesBlink: look([0.6, 0.4, 1], 'burst'),
34
+ abilitiesRecall: look([0.5, 0.8, 1], 'tornado'),
35
+ abilitiesCharge: look([1, 0.6, 0.2], 'slam'),
36
+ abilitiesSweep: look([0.8, 0.9, 1], 'burst'),
37
+ abilitiesCondemn: look([0.85, 0.75, 1], 'burst'),
38
+ abilitiesDragonsRage: look([1, 0.5, 0.1], 'smash'),
39
+ abilitiesShove: look([0.9, 0.85, 0.6], 'slam'),
40
+ abilitiesFlay: look([0.4, 1, 0.6], 'slam'),
41
+ abilitiesVacuum: look([0.6, 0.4, 1], 'tornado'),
42
+ abilitiesSwap: look([0.7, 0.3, 1], 'tether'),
43
+ abilitiesAttach: look([0.5, 1, 0.8], 'tether'),
44
+ abilitiesWallStun: look([1, 1, 0.6], undefined, { aura: 'glow', options: { shape: 'ring', at: 'head' } })
45
+ },
46
+ examples: {
47
+ 'abilities-moves': {
48
+ ready: (client) => readyOn(client, 'abilitiesMover', { minX: 16, minZ: 13, maxX: 32, maxZ: 32 }),
49
+ controls: [...moves.abilities.map((ability) => castButton(ability, aims[ability.id])), resetButton('abilities-moves')]
50
+ }
51
+ }
52
+ }