@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.
- package/README.md +4 -1
- package/base/_gitignore +5 -0
- package/base/package.json +3 -3
- package/base/src/main.ts +36 -10
- package/base/src/navigation.worker.ts +7 -0
- package/base/vite.config.ts +24 -2
- package/cli.mjs +21 -6
- package/index.mjs +27 -7
- package/layers/desktop/electron/app.mjs +237 -0
- package/layers/desktop/electron/config.mjs +16 -0
- package/layers/desktop/electron/dev.mjs +63 -0
- package/layers/desktop/electron/files.mjs +86 -0
- package/layers/desktop/electron/icons/icon.icns +0 -0
- package/layers/desktop/electron/icons/icon.png +0 -0
- package/layers/desktop/electron/main.mjs +12 -0
- package/layers/desktop/electron/package.mjs +75 -0
- package/layers/desktop/electron/preload.cjs +41 -0
- package/layers/desktop/electron/presentation.m +87 -0
- package/layers/desktop/electron/serve.mjs +76 -0
- package/layers/desktop/electron/tools.mjs +63 -0
- package/layers/desktop/electron/updates.mjs +71 -0
- package/layers/desktop/package.json +13 -0
- package/layers/desktop/src/desktop.ts +175 -0
- package/layers/desktop/src/shell.ts +279 -0
- package/layers/desktop/tests/main.test.ts +189 -0
- package/layers/desktop/tests/shell.test.ts +152 -0
- package/layers/rooms/server.ts +13 -10
- package/layers/server/server.ts +4 -5
- package/layers/source/vite.config.ts +21 -1
- package/package.json +2 -1
- package/starters/campaign/README.md +9 -2
- package/starters/campaign/layers/rooms/server.ts +3 -3
- package/starters/campaign/layers/server/server.ts +3 -5
- package/starters/campaign/layers/tests/tests/rules.test.ts +40 -21
- package/starters/campaign/src/campaign/game.ts +26 -5
- package/starters/campaign/src/campaign/index.ts +104 -68
- package/starters/campaign/src/campaign/kills.ts +1 -1
- package/starters/campaign/src/campaign/missions/homecoming.ts +7 -7
- package/starters/campaign/src/campaign/missions/siege.ts +6 -6
- package/starters/campaign/src/campaign/missions/warlord.ts +13 -14
- package/starters/campaign/src/campaign/scenes.ts +8 -7
- package/starters/campaign/src/campaign/units.ts +59 -20
- package/starters/campaign/src/campaign/view/attacks.ts +19 -0
- package/starters/campaign/src/campaign/view/fire.ts +2 -2
- package/starters/campaign/src/campaign/view/index.ts +54 -43
- package/starters/campaign/src/campaign/waves.ts +18 -17
- package/starters/campaign/src/game.ts +8 -1
- package/starters/campaign/src/main.ts +38 -19
- package/starters/campaign/src/maps/fort.names.ts +2 -2
- package/starters/campaign/src/maps/fort.vgmap.json +102 -2
- package/starters/campaign/src/maps/homestead.names.ts +2 -2
- package/starters/campaign/src/maps/homestead.vgmap.json +85 -1
- package/starters/campaign/src/maps/pass.names.ts +2 -2
- package/starters/campaign/src/maps/pass.vgmap.json +69 -1
- package/starters/campaign/src/waves-panel.ts +2 -1
- package/starters/empty/README.md +18 -10
- package/starters/empty/layers/tests/tests/rules.test.ts +16 -17
- package/starters/empty/src/game.ts +11 -75
- package/starters/empty/src/generate.worker.ts +5 -5
- package/starters/empty/src/rules.ts +83 -25
- package/starters/empty/src/view.ts +82 -0
- package/starters/examples/README.md +83 -0
- package/starters/examples/layers/tests/tests/examples.test.ts +30 -0
- package/starters/examples/package.json +5 -0
- package/starters/examples/src/examples/abilities/areas.ts +165 -0
- package/starters/examples/src/examples/abilities/areasLooks.ts +30 -0
- package/starters/examples/src/examples/abilities/buffs.ts +287 -0
- package/starters/examples/src/examples/abilities/buffsLooks.ts +46 -0
- package/starters/examples/src/examples/abilities/changing.ts +228 -0
- package/starters/examples/src/examples/abilities/changingLooks.ts +61 -0
- package/starters/examples/src/examples/abilities/control.ts +158 -0
- package/starters/examples/src/examples/abilities/controlLooks.ts +78 -0
- package/starters/examples/src/examples/abilities/damage.ts +156 -0
- package/starters/examples/src/examples/abilities/damageLooks.ts +37 -0
- package/starters/examples/src/examples/abilities/family.ts +29 -0
- package/starters/examples/src/examples/abilities/index.ts +32 -0
- package/starters/examples/src/examples/abilities/kit.ts +179 -0
- package/starters/examples/src/examples/abilities/lookKit.ts +71 -0
- package/starters/examples/src/examples/abilities/looks.ts +47 -0
- package/starters/examples/src/examples/abilities/missiles.ts +203 -0
- package/starters/examples/src/examples/abilities/missilesLooks.ts +38 -0
- package/starters/examples/src/examples/abilities/moves.ts +206 -0
- package/starters/examples/src/examples/abilities/movesLooks.ts +52 -0
- package/starters/examples/src/examples/abilities/recasts.ts +181 -0
- package/starters/examples/src/examples/abilities/recastsLooks.ts +41 -0
- package/starters/examples/src/examples/abilities/resources.ts +166 -0
- package/starters/examples/src/examples/abilities/resourcesLooks.ts +42 -0
- package/starters/examples/src/examples/abilities/summons.ts +296 -0
- package/starters/examples/src/examples/abilities/summonsLooks.ts +115 -0
- package/starters/examples/src/examples/abilities/triggers.ts +204 -0
- package/starters/examples/src/examples/abilities/triggersLooks.ts +53 -0
- package/starters/examples/src/examples/abilities/vision.ts +137 -0
- package/starters/examples/src/examples/abilities/visionLooks.ts +50 -0
- package/starters/examples/src/examples/animation/index.ts +235 -0
- package/starters/examples/src/examples/animation/looks.ts +444 -0
- package/starters/examples/src/examples/catalog.ts +52 -0
- package/starters/examples/src/examples/cinematics/index.ts +217 -0
- package/starters/examples/src/examples/cinematics/looks.ts +476 -0
- package/starters/examples/src/examples/cinematics/tour.json +43 -0
- package/starters/examples/src/examples/cinematics/village.ts +88 -0
- package/starters/examples/src/examples/common/looks.ts +65 -0
- package/starters/examples/src/examples/common/units.ts +70 -0
- package/starters/examples/src/examples/construction/index.ts +329 -0
- package/starters/examples/src/examples/construction/looks.ts +275 -0
- package/starters/examples/src/examples/custom-animation/bones.ts +92 -0
- package/starters/examples/src/examples/custom-animation/index.ts +190 -0
- package/starters/examples/src/examples/custom-animation/looks.ts +523 -0
- package/starters/examples/src/examples/custom-animation/runes.ts +82 -0
- package/starters/examples/src/examples/custom-fx/glaive.look.ts +110 -0
- package/starters/examples/src/examples/custom-fx/glaive.ts +40 -0
- package/starters/examples/src/examples/custom-fx/gpu.look.ts +145 -0
- package/starters/examples/src/examples/custom-fx/gpu.ts +22 -0
- package/starters/examples/src/examples/custom-fx/index.ts +25 -0
- package/starters/examples/src/examples/custom-fx/lights.look.ts +144 -0
- package/starters/examples/src/examples/custom-fx/lights.ts +25 -0
- package/starters/examples/src/examples/custom-fx/looks.ts +36 -0
- package/starters/examples/src/examples/custom-fx/meteor.look.ts +133 -0
- package/starters/examples/src/examples/custom-fx/meteor.ts +26 -0
- package/starters/examples/src/examples/custom-fx/parts.look.ts +193 -0
- package/starters/examples/src/examples/custom-fx/parts.ts +21 -0
- package/starters/examples/src/examples/custom-fx/portal.look.ts +193 -0
- package/starters/examples/src/examples/custom-fx/portal.ts +48 -0
- package/starters/examples/src/examples/custom-fx/renderer.look.ts +139 -0
- package/starters/examples/src/examples/custom-fx/renderer.ts +18 -0
- package/starters/examples/src/examples/custom-fx/ribbons.look.ts +151 -0
- package/starters/examples/src/examples/custom-fx/ribbons.ts +22 -0
- package/starters/examples/src/examples/custom-fx/shader.ts +42 -0
- package/starters/examples/src/examples/custom-fx/shield.look.ts +226 -0
- package/starters/examples/src/examples/custom-fx/shield.ts +43 -0
- package/starters/examples/src/examples/define.ts +86 -0
- package/starters/examples/src/examples/destruction/index.ts +570 -0
- package/starters/examples/src/examples/destruction/looks.ts +447 -0
- package/starters/examples/src/examples/dialogs/index.ts +383 -0
- package/starters/examples/src/examples/dialogs/looks.ts +546 -0
- package/starters/examples/src/examples/dynamic-map/index.ts +603 -0
- package/starters/examples/src/examples/dynamic-map/looks.ts +301 -0
- package/starters/examples/src/examples/fx/blood.look.ts +62 -0
- package/starters/examples/src/examples/fx/blood.ts +59 -0
- package/starters/examples/src/examples/fx/feel.look.ts +86 -0
- package/starters/examples/src/examples/fx/feel.ts +57 -0
- package/starters/examples/src/examples/fx/fight.ts +28 -0
- package/starters/examples/src/examples/fx/floats.look.ts +108 -0
- package/starters/examples/src/examples/fx/floats.ts +24 -0
- package/starters/examples/src/examples/fx/gallery.look.ts +168 -0
- package/starters/examples/src/examples/fx/gallery.ts +23 -0
- package/starters/examples/src/examples/fx/index.ts +30 -0
- package/starters/examples/src/examples/fx/links.look.ts +83 -0
- package/starters/examples/src/examples/fx/links.ts +49 -0
- package/starters/examples/src/examples/fx/looks.ts +55 -0
- package/starters/examples/src/examples/fx/map.look.ts +93 -0
- package/starters/examples/src/examples/fx/map.ts +33 -0
- package/starters/examples/src/examples/fx/marks.look.ts +100 -0
- package/starters/examples/src/examples/fx/marks.ts +36 -0
- package/starters/examples/src/examples/fx/shots.look.ts +92 -0
- package/starters/examples/src/examples/fx/shots.ts +71 -0
- package/starters/examples/src/examples/fx/spells.look.ts +87 -0
- package/starters/examples/src/examples/fx/spells.ts +122 -0
- package/starters/examples/src/examples/fx/stage.ts +31 -0
- package/starters/examples/src/examples/gui/babylonScreen.ts +167 -0
- package/starters/examples/src/examples/gui/babylonWorld.ts +191 -0
- package/starters/examples/src/examples/gui/htmlPart.ts +95 -0
- package/starters/examples/src/examples/gui/index.ts +143 -0
- package/starters/examples/src/examples/gui/keys.ts +91 -0
- package/starters/examples/src/examples/gui/layout.ts +42 -0
- package/starters/examples/src/examples/gui/looks.ts +270 -0
- package/starters/examples/src/examples/gui/minimap.ts +127 -0
- package/starters/examples/src/examples/gui/ownInterface.ts +129 -0
- package/starters/examples/src/examples/gui/plates.ts +87 -0
- package/starters/examples/src/examples/gui/radial.ts +104 -0
- package/starters/examples/src/examples/gui/solidPart.ts +107 -0
- package/starters/examples/src/examples/looks.ts +45 -0
- package/starters/examples/src/examples/mechanics/battle.ts +51 -0
- package/starters/examples/src/examples/mechanics/damage.ts +80 -0
- package/starters/examples/src/examples/mechanics/flying.ts +93 -0
- package/starters/examples/src/examples/mechanics/formations.ts +58 -0
- package/starters/examples/src/examples/mechanics/highGround.ts +82 -0
- package/starters/examples/src/examples/mechanics/index.ts +44 -0
- package/starters/examples/src/examples/mechanics/looks.ts +289 -0
- package/starters/examples/src/examples/mechanics/maze.ts +119 -0
- package/starters/examples/src/examples/mechanics/orders.ts +68 -0
- package/starters/examples/src/examples/mechanics/selection.ts +30 -0
- package/starters/examples/src/examples/moba/index.ts +377 -0
- package/starters/examples/src/examples/moba/looks.ts +349 -0
- package/starters/examples/src/examples/moba/maps.ts +69 -0
- package/starters/examples/src/examples/moba/spells.ts +34 -0
- package/starters/examples/src/examples/moba/units.ts +196 -0
- package/starters/examples/src/examples/nature/camera.ts +15 -0
- package/starters/examples/src/examples/nature/grass.ts +64 -0
- package/starters/examples/src/examples/nature/index.ts +327 -0
- package/starters/examples/src/examples/nature/land.ts +173 -0
- package/starters/examples/src/examples/nature/looks.ts +577 -0
- package/starters/examples/src/examples/nature/maps.ts +370 -0
- package/starters/examples/src/examples/nature/skies.ts +324 -0
- package/starters/examples/src/examples/nature/weather.ts +80 -0
- package/starters/examples/src/examples/performance/index.ts +263 -0
- package/starters/examples/src/examples/performance/looks.ts +372 -0
- package/starters/examples/src/examples/performance/meter.ts +158 -0
- package/starters/examples/src/examples/post/grades.ts +86 -0
- package/starters/examples/src/examples/post/index.ts +202 -0
- package/starters/examples/src/examples/post/looks.ts +884 -0
- package/starters/examples/src/examples/post/passes.ts +213 -0
- package/starters/examples/src/examples/rts/ai.ts +49 -0
- package/starters/examples/src/examples/rts/airlift.ts +43 -0
- package/starters/examples/src/examples/rts/base.ts +129 -0
- package/starters/examples/src/examples/rts/build.ts +65 -0
- package/starters/examples/src/examples/rts/camps.ts +36 -0
- package/starters/examples/src/examples/rts/carry.ts +103 -0
- package/starters/examples/src/examples/rts/creeps.ts +61 -0
- package/starters/examples/src/examples/rts/data.ts +406 -0
- package/starters/examples/src/examples/rts/ferry.ts +41 -0
- package/starters/examples/src/examples/rts/garrison.ts +50 -0
- package/starters/examples/src/examples/rts/harvest.ts +29 -0
- package/starters/examples/src/examples/rts/hero.ts +67 -0
- package/starters/examples/src/examples/rts/index.ts +77 -0
- package/starters/examples/src/examples/rts/looks.ts +721 -0
- package/starters/examples/src/examples/rts/shop.ts +46 -0
- package/starters/examples/src/examples/rts/tech.ts +39 -0
- package/starters/examples/src/examples/rts/train.ts +48 -0
- package/starters/examples/src/examples/rts/waterside.ts +185 -0
- package/starters/examples/src/examples/showcase/index.ts +714 -0
- package/starters/examples/src/examples/showcase/looks.ts +238 -0
- package/starters/examples/src/game.ts +17 -0
- package/starters/examples/src/main.ts +82 -0
- package/starters/examples/src/rules.ts +31 -0
- package/starters/examples/src/shell/look.ts +79 -0
- package/starters/examples/src/shell/registry.ts +7 -0
- package/starters/examples/src/shell/scenes.ts +178 -0
- package/starters/examples/src/shell/shell.ts +290 -0
- package/starters/examples/src/shell/style.ts +202 -0
- package/starters/examples/src/view.ts +50 -0
- package/starters/melee/README.md +12 -3
- package/starters/melee/layers/tests/tests/heroes.test.ts +402 -0
- package/starters/melee/layers/tests/tests/rules.test.ts +10 -8
- package/starters/melee/src/game.ts +32 -2
- package/starters/melee/src/maps/bay.names.ts +2 -2
- package/starters/melee/src/maps/bay.vgmap.json +198 -1
- package/starters/melee/src/maps.ts +11 -9
- package/starters/melee/src/melee/abilities.ts +1 -194
- package/starters/melee/src/melee/blight.ts +7 -7
- package/starters/melee/src/melee/cellLayers.json +3 -3
- package/starters/melee/src/melee/data.json +1 -18
- package/starters/melee/src/melee/game.ts +6 -4
- package/starters/melee/src/melee/heroes.ts +1025 -0
- package/starters/melee/src/melee/index.ts +64 -61
- package/starters/melee/src/melee/items.ts +15 -362
- package/starters/melee/src/melee/languages/he.json +14 -2
- package/starters/melee/src/melee/pings.ts +5 -6
- package/starters/melee/src/melee/saves.ts +1 -1
- package/starters/melee/src/melee/undead.ts +15 -230
- package/starters/melee/src/melee/units.ts +37 -171
- package/starters/melee/src/melee/view/attacks.ts +19 -0
- package/starters/melee/src/melee/view/heroSpellLooks.ts +39 -8
- package/starters/melee/src/melee/view/index.ts +56 -52
- package/starters/melee/src/melee/view/looks.ts +86 -63
- package/starters/melee/src/melee/view/undeadLooks.ts +70 -31
- package/starters/melee/src/melee/view/walls.ts +1 -1
- package/starters.mjs +6 -0
- package/v12.mjs +14 -15
- package/starters/campaign/src/rules.ts +0 -20
- package/starters/empty/src/maps.ts +0 -44
- package/starters/empty/src/tiles.ts +0 -12
- package/starters/melee/src/melee/heroSpells.ts +0 -38
- package/starters/melee/src/rules.ts +0 -24
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vision and stealth, played in the fog of war: see a far spot, find what hides, go unseen; and with the vision pack
|
|
3
|
+
* (`visionPack`), mark an enemy so it is seen wherever it runs, be seen only up close, and see the hidden with true
|
|
4
|
+
* sight. A sentry far to the north is in the fog, an assassin near is invisible, a ranger camouflaged
|
|
5
|
+
*/
|
|
6
|
+
import { type AbilityDef, applyBuff, type 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, sparring } from './kit'
|
|
11
|
+
|
|
12
|
+
const buffs: BuffDef[] = [
|
|
13
|
+
// unseen by enemies unless found, until it attacks or casts
|
|
14
|
+
{
|
|
15
|
+
id: 'abilitiesWindWalk',
|
|
16
|
+
name: 'Wind Walk',
|
|
17
|
+
description: 'Unseen, 30% faster, until it attacks or casts',
|
|
18
|
+
duration: 10,
|
|
19
|
+
invisible: true,
|
|
20
|
+
breaksOn: ['attack', 'cast'],
|
|
21
|
+
percent: { moveSpeed: 0.3 }
|
|
22
|
+
},
|
|
23
|
+
// seen by the team of who put it on, wherever it stands: through fog and invisibility
|
|
24
|
+
{ id: 'abilitiesHunted', name: 'Hunted', description: 'Seen wherever it runs', harmful: true, duration: 12, revealed: true },
|
|
25
|
+
// seen only by an enemy within 4 cells
|
|
26
|
+
{ id: 'abilitiesCamouflage', name: 'Camouflage', description: 'Seen only from within 4 cells', camouflage: 4 },
|
|
27
|
+
{ id: 'abilitiesGhostwater', name: 'Ghostwater', description: 'Seen only from within 3 cells', duration: 8, camouflage: 3 },
|
|
28
|
+
// finds the invisible and camouflaged within 10, for as long as they stay near
|
|
29
|
+
{ id: 'abilitiesOracle', name: 'Oracle Lens', description: 'Finds what hides within 10', duration: 10, trueSight: 10 }
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
const abilities: AbilityDef[] = [
|
|
33
|
+
{
|
|
34
|
+
id: 'abilitiesFarSight',
|
|
35
|
+
name: 'Far Sight',
|
|
36
|
+
description: 'Lights an 8-cell circle in the north for 8 seconds: the sentry there',
|
|
37
|
+
hotkey: 'Q',
|
|
38
|
+
target: 'point',
|
|
39
|
+
range: 99,
|
|
40
|
+
cooldown: 1,
|
|
41
|
+
effects: [{ type: 'reveal', radius: 8, duration: 8 }]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
// finds the invisible around it for a while: the assassin
|
|
45
|
+
id: 'abilitiesDust',
|
|
46
|
+
name: 'Dust of Appearance',
|
|
47
|
+
description: 'Finds invisible enemies within 9 of the caster for 8 seconds',
|
|
48
|
+
hotkey: 'W',
|
|
49
|
+
target: 'none',
|
|
50
|
+
cooldown: 1,
|
|
51
|
+
effects: [{ type: 'detect', radius: 9, duration: 8 }]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'abilitiesCastWindWalk',
|
|
55
|
+
name: 'Wind Walk',
|
|
56
|
+
description: 'The caster is unseen by enemies for 10 seconds, until it attacks or casts',
|
|
57
|
+
hotkey: 'E',
|
|
58
|
+
target: 'none',
|
|
59
|
+
cooldown: 1,
|
|
60
|
+
effects: [{ type: 'buff', buff: 'abilitiesWindWalk', on: 'caster' }]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
// every enemy within 30 seen for 6 seconds, wherever it goes
|
|
64
|
+
id: 'abilitiesThrill',
|
|
65
|
+
name: 'Thrill of the Hunt',
|
|
66
|
+
description: 'Every enemy within 30 is seen for 6 seconds, wherever it goes, hidden or not',
|
|
67
|
+
hotkey: 'R',
|
|
68
|
+
target: 'none',
|
|
69
|
+
cooldown: 1,
|
|
70
|
+
effects: [{ type: 'revealUnit', radius: 30, duration: 6 }]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'abilitiesMark',
|
|
74
|
+
name: 'Hunter’s Mark',
|
|
75
|
+
description: 'Marks the sentry (once seen): seen wherever it runs for 12 seconds',
|
|
76
|
+
hotkey: 'T',
|
|
77
|
+
target: 'unit',
|
|
78
|
+
targets: { enemies: true },
|
|
79
|
+
range: 99,
|
|
80
|
+
cooldown: 1,
|
|
81
|
+
effects: [{ type: 'buff', buff: 'abilitiesHunted' }]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'abilitiesGhostwaterDive',
|
|
85
|
+
name: 'Ghostwater Dive',
|
|
86
|
+
description: 'The caster is seen only from within 3 cells, for 8 seconds',
|
|
87
|
+
hotkey: 'D',
|
|
88
|
+
target: 'none',
|
|
89
|
+
cooldown: 1,
|
|
90
|
+
effects: [{ type: 'buff', buff: 'abilitiesGhostwater', on: 'caster' }]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'abilitiesOracleLens',
|
|
94
|
+
name: 'Oracle Lens',
|
|
95
|
+
description: 'True sight within 10 for 10 seconds: the assassin and the ranger',
|
|
96
|
+
hotkey: 'F',
|
|
97
|
+
target: 'none',
|
|
98
|
+
cooldown: 1,
|
|
99
|
+
effects: [{ type: 'buff', buff: 'abilitiesOracle', on: 'caster' }]
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
export const vision: Family = {
|
|
104
|
+
abilities,
|
|
105
|
+
buffs,
|
|
106
|
+
units: [
|
|
107
|
+
caster(
|
|
108
|
+
'abilitiesSeer',
|
|
109
|
+
'Seer',
|
|
110
|
+
abilities.map((each) => each.id),
|
|
111
|
+
{ sight: 9 }
|
|
112
|
+
),
|
|
113
|
+
// invisible as it is: found only by detection or true sight
|
|
114
|
+
sparring('abilitiesAssassin', 'Assassin', { invisible: { after: 0 } }),
|
|
115
|
+
sparring('abilitiesSentry', 'Sentry', { sight: 6 })
|
|
116
|
+
],
|
|
117
|
+
example: {
|
|
118
|
+
id: 'abilities-vision',
|
|
119
|
+
category: 'abilities',
|
|
120
|
+
title: 'Vision and stealth',
|
|
121
|
+
summary:
|
|
122
|
+
'In the fog of war: an invisible assassin stands near you, a camouflaged ranger to the right, a sentry far north. Light the north, find the hidden, mark the sentry so it is seen as it walks, go unseen yourself.',
|
|
123
|
+
tags: ['vision', 'fog', 'reveal', 'invisible', 'stealth', 'detect', 'true sight', 'camouflage', 'far sight', 'mark'],
|
|
124
|
+
source: ['examples/abilities/vision.ts', 'examples/abilities/kit.ts'],
|
|
125
|
+
map: () => exampleField({ name: 'Vision', players: 2 }),
|
|
126
|
+
// the fog of war, for once
|
|
127
|
+
reveal: false,
|
|
128
|
+
start: (world) => {
|
|
129
|
+
put(world, 'abilitiesSeer', PLAYER, { x: 24, z: 14 }, 'caster')
|
|
130
|
+
put(world, 'abilitiesAssassin', ENEMY, { x: 22, z: 18.5 }, 'assassin', Math.PI)
|
|
131
|
+
const ranger = put(world, 'abilitiesTarget', ENEMY, { x: 28.5, z: 18 }, 'ranger', Math.PI)
|
|
132
|
+
applyBuff(world, ranger, 'abilitiesCamouflage')
|
|
133
|
+
// the sentry, in the north out of sight: set walking as the page starts (`walkAgain`)
|
|
134
|
+
put(world, 'abilitiesSentry', ENEMY, { x: 22, z: 36 }, 'sentry', Math.PI)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** How the vision example looks, and its card: a button for each, and what the player sees of the enemies */
|
|
2
|
+
import { readout } from '../../shell/look'
|
|
3
|
+
import { character, hold, person, sharedLooks } from '../common/looks'
|
|
4
|
+
import { ENEMY } from '../common/units'
|
|
5
|
+
import type { FamilyLooks } from './looks'
|
|
6
|
+
import { castButton, look, readyOn, resetButton } from './lookKit'
|
|
7
|
+
import { walkAgain } from './control'
|
|
8
|
+
import { vision } from './vision'
|
|
9
|
+
|
|
10
|
+
/** Where each is aimed, where the nearest enemy is not it */
|
|
11
|
+
const aims: Record<string, { at?: string; point?: { x: number; z: number } }> = {
|
|
12
|
+
abilitiesFarSight: { point: { x: 26, z: 37 } },
|
|
13
|
+
abilitiesMark: { at: 'sentry' }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const visionLooks: FamilyLooks = {
|
|
17
|
+
units: {
|
|
18
|
+
abilitiesSeer: { ...sharedLooks.mage, model: character('mage', 0.72, { hold: [hold('wand'), hold('spellbook_open', 'l')] }) },
|
|
19
|
+
abilitiesAssassin: { height: 1.8, model: character('rogue_hooded', 0.72, { hold: [hold('dagger'), hold('dagger', 'l')] }), shapes: person(1.8) },
|
|
20
|
+
abilitiesSentry: sharedLooks.archer
|
|
21
|
+
},
|
|
22
|
+
abilities: {
|
|
23
|
+
abilitiesFarSight: look([0.5, 0.8, 1], 'mark', { options: { column: true, fill: true } }),
|
|
24
|
+
abilitiesDust: look([1, 0.9, 0.6], 'sonar'),
|
|
25
|
+
abilitiesThrill: look([1, 0.4, 0.3], 'sonar'),
|
|
26
|
+
abilitiesMark: look([1, 0.3, 0.2], 'burst'),
|
|
27
|
+
abilitiesWindWalk: look([0.8, 0.9, 1], undefined, { aura: 'glow', options: { shape: 'dust' } }),
|
|
28
|
+
abilitiesHunted: look([1, 0.3, 0.2], undefined, { aura: 'glow', options: { shape: 'ring', at: 'head' } }),
|
|
29
|
+
abilitiesGhostwater: look([0.4, 0.6, 1], undefined, { aura: 'glow', options: { shape: 'fall' } }),
|
|
30
|
+
abilitiesOracle: look([1, 0.85, 0.3], undefined, { aura: 'glow', options: { shape: 'ring' } })
|
|
31
|
+
},
|
|
32
|
+
examples: {
|
|
33
|
+
'abilities-vision': {
|
|
34
|
+
ready: (client) => {
|
|
35
|
+
readyOn(client, 'abilitiesSeer', { minX: 15, minZ: 5, maxX: 33, maxZ: 40 })
|
|
36
|
+
client.session.send(walkAgain.make({}))
|
|
37
|
+
},
|
|
38
|
+
controls: [
|
|
39
|
+
...vision.abilities.map((ability) => castButton(ability, aims[ability.id])),
|
|
40
|
+
resetButton('abilities-vision'),
|
|
41
|
+
readout('Enemies you see', (client) => {
|
|
42
|
+
const seen = client.session.snapshot().entities.filter((each) => each.owner === ENEMY && each.visible && each.health > 0)
|
|
43
|
+
return seen.length
|
|
44
|
+
? seen.map((each) => (each.type === 'abilitiesTarget' ? 'ranger' : each.type.replace('abilities', '').toLowerCase())).join(', ')
|
|
45
|
+
: 'none'
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The animation examples on the rules' side (`ExampleCategory`): the shared art's characters as unit types of their
|
|
3
|
+
* own, each to be drawn with its model and its clips (`./looks.ts`), and what the examples set them doing. The rules
|
|
4
|
+
* know nothing of clips: a unit stands, walks, swings and dies, and the client plays the clip for it
|
|
5
|
+
*/
|
|
6
|
+
import { defineCommand, defineIds, type Game, type UnitTypeDef, type Vec2, type World, exactMath } from '@v12-engine/engine'
|
|
7
|
+
import { ENEMY, PLAYER, UNIT } from '../common/units'
|
|
8
|
+
import { type ExampleCategory, exampleField } from '../define'
|
|
9
|
+
|
|
10
|
+
/** The characters of the shared art (`characters/<name>.glb`), one unit type each */
|
|
11
|
+
export const CHARACTERS = [
|
|
12
|
+
'knight',
|
|
13
|
+
'barbarian',
|
|
14
|
+
'mage',
|
|
15
|
+
'ranger',
|
|
16
|
+
'rogue',
|
|
17
|
+
'rogue_hooded',
|
|
18
|
+
'skeleton_warrior',
|
|
19
|
+
'skeleton_mage',
|
|
20
|
+
'skeleton_rogue',
|
|
21
|
+
'skeleton_minion'
|
|
22
|
+
] as const
|
|
23
|
+
export type Character = (typeof CHARACTERS)[number]
|
|
24
|
+
|
|
25
|
+
/** A unit type's id for each character: `animationKnight` */
|
|
26
|
+
export const characterType = (name: Character): string => `animation${name.replace(/(^|_)(\w)/g, (_, _sep: string, c: string) => c.toUpperCase())}`
|
|
27
|
+
|
|
28
|
+
/** The category's own units besides: swordsmen that swing in different ways, walkers at three paces, and the fallen */
|
|
29
|
+
export const ANIM = defineIds(
|
|
30
|
+
'animationTurns',
|
|
31
|
+
'animationRandom',
|
|
32
|
+
'animationTwoHanded',
|
|
33
|
+
'animationSlow',
|
|
34
|
+
'animationJog',
|
|
35
|
+
'animationRun',
|
|
36
|
+
'animationWielder',
|
|
37
|
+
'animationVictim',
|
|
38
|
+
'animationWaver'
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
/** A character that stands where it is put: it does nothing of its own, the card plays its clips */
|
|
42
|
+
const standing = (id: string, name: string): UnitTypeDef => ({ id, name, kind: 'unit', radius: 0.45, stats: { health: 500, moveSpeed: 0 }, sight: 6 })
|
|
43
|
+
|
|
44
|
+
/** A swordsman that hits the dummy in front of it for ever */
|
|
45
|
+
const swordsman = (id: string, name: string, attackSpeed: number): UnitTypeDef => ({
|
|
46
|
+
id,
|
|
47
|
+
name,
|
|
48
|
+
kind: 'unit',
|
|
49
|
+
radius: 0.45,
|
|
50
|
+
stats: { health: 500, attackDamage: 10, attackSpeed, attackRange: 0.6, moveSpeed: 4 },
|
|
51
|
+
sight: 6,
|
|
52
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 4 }]
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/** A walker of a pace: its clips are matched to how fast it goes (`./looks.ts`) */
|
|
56
|
+
const walker = (id: string, name: string, moveSpeed: number): UnitTypeDef => ({
|
|
57
|
+
id,
|
|
58
|
+
name,
|
|
59
|
+
kind: 'unit',
|
|
60
|
+
radius: 0.45,
|
|
61
|
+
stats: { health: 500, moveSpeed },
|
|
62
|
+
sight: 6
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const units: UnitTypeDef[] = [
|
|
66
|
+
...CHARACTERS.map((name) => standing(characterType(name), name.replace('_', ' '))),
|
|
67
|
+
swordsman(ANIM.animationTurns, 'Swings in turn', 1),
|
|
68
|
+
swordsman(ANIM.animationRandom, 'Swings at random', 1),
|
|
69
|
+
swordsman(ANIM.animationTwoHanded, 'Two-handed', 0.8),
|
|
70
|
+
walker(ANIM.animationSlow, 'Stroller', 2),
|
|
71
|
+
walker(ANIM.animationJog, 'Walker', 3.2),
|
|
72
|
+
walker(ANIM.animationRun, 'Runner', 6.5),
|
|
73
|
+
swordsman(ANIM.animationWielder, 'Wielder', 1),
|
|
74
|
+
{ ...standing(ANIM.animationVictim, 'Fallen'), stats: { health: 300, moveSpeed: 0 } },
|
|
75
|
+
walker(ANIM.animationWaver, 'Waver', 3)
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
/** `count` of a type in a row across x, `gap` apart, round `at`, facing the camera (south) */
|
|
79
|
+
function row(world: World, types: readonly string[], at: Vec2, gap: number, owner = PLAYER): void {
|
|
80
|
+
types.forEach((type, i) => world.spawn(type, { at: { x: at.x + (i - (types.length - 1) / 2) * gap, z: at.z }, owner, facing: Math.PI }))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The fallen of the deaths example stood up again: those dead taken away, a row of five laid out anew */
|
|
84
|
+
export const raise = defineCommand('animation.raise', {}, (world) => {
|
|
85
|
+
for (const unit of [...world.units.values()]) if (unit.alive && unit.def.id === ANIM.animationVictim) world.kill(unit, undefined, false, 'removed')
|
|
86
|
+
row(world, Array(5).fill(ANIM.animationVictim), { x: 24, z: 24 }, 2.2)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
/** One of the fallen struck down, the one at `index` along the row (or the first left standing) */
|
|
90
|
+
export const strike = defineCommand('animation.strike', {}, (world) => {
|
|
91
|
+
const left = [...world.units.values()].filter((unit) => unit.alive && unit.def.id === ANIM.animationVictim).sort((a, b) => a.pos.x - b.pos.x)
|
|
92
|
+
const unit = left[0]
|
|
93
|
+
if (unit) world.kill(unit)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
/** Walkers that go to the far side of a track and back for ever: a system on the paces example's field */
|
|
97
|
+
const pacing = {
|
|
98
|
+
name: 'animation.pacing',
|
|
99
|
+
every: 10,
|
|
100
|
+
update: (world: World) => {
|
|
101
|
+
if (world.map?.name !== 'Paces') return
|
|
102
|
+
for (const unit of world.units.values()) {
|
|
103
|
+
if (!unit.alive || unit.orders.length || !unit.def.stats.moveSpeed) continue
|
|
104
|
+
unit.order('move', { x: unit.pos.x < 24 ? 36 : 12, z: unit.pos.z })
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The waver of the layered example walking round a square for ever */
|
|
110
|
+
const circling = {
|
|
111
|
+
name: 'animation.circling',
|
|
112
|
+
every: 5,
|
|
113
|
+
update: (world: World) => {
|
|
114
|
+
if (world.map?.name !== 'Layers') return
|
|
115
|
+
const corners = [
|
|
116
|
+
{ x: 18, z: 18 },
|
|
117
|
+
{ x: 30, z: 18 },
|
|
118
|
+
{ x: 30, z: 30 },
|
|
119
|
+
{ x: 18, z: 30 }
|
|
120
|
+
]
|
|
121
|
+
for (const unit of world.units.values()) {
|
|
122
|
+
if (!unit.alive || unit.def.id !== ANIM.animationWaver || unit.orders.length) continue
|
|
123
|
+
// the corner after the one it stands nearest
|
|
124
|
+
const near = corners.reduce(
|
|
125
|
+
(best, each, i) =>
|
|
126
|
+
exactMath.hypot(each.x - unit.pos.x, each.z - unit.pos.z) < exactMath.hypot(corners[best]!.x - unit.pos.x, corners[best]!.z - unit.pos.z)
|
|
127
|
+
? i
|
|
128
|
+
: best,
|
|
129
|
+
0
|
|
130
|
+
)
|
|
131
|
+
unit.order('move', corners[(near + 1) % corners.length]!)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const category: ExampleCategory = {
|
|
137
|
+
rules: (game: Game<any>) => {
|
|
138
|
+
for (const unit of units) game.unit(unit)
|
|
139
|
+
game.command(raise)
|
|
140
|
+
game.command(strike)
|
|
141
|
+
game.system(pacing)
|
|
142
|
+
game.system(circling)
|
|
143
|
+
},
|
|
144
|
+
examples: [
|
|
145
|
+
{
|
|
146
|
+
id: 'animation-clips',
|
|
147
|
+
category: 'animation',
|
|
148
|
+
title: 'Every clip of every character',
|
|
149
|
+
summary:
|
|
150
|
+
"The shared art's ten characters in a row. Pick a clip and they all play it at once (the skeletons have six of their own); slow it down, speed it up, or loop it.",
|
|
151
|
+
tags: ['clips', 'playClip', 'characters', 'models', 'KayKit', 'skeletons', 'browser'],
|
|
152
|
+
start: (world) => row(world, CHARACTERS.map(characterType), { x: 24, z: 24 }, 2.1),
|
|
153
|
+
source: ['examples/animation/index.ts', 'examples/animation/looks.ts']
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: 'animation-attacks',
|
|
157
|
+
category: 'animation',
|
|
158
|
+
title: 'Swings in turn, at random, two-handed',
|
|
159
|
+
summary:
|
|
160
|
+
"Three swordsmen at dummies, each with an attack look of its own: three swings in turn, the same three at random, and a two-handed chop and slice. The blow lands on the clip's hit, at any attack speed.",
|
|
161
|
+
tags: ['attack', 'clips', 'AttackLook', 'order', 'random', 'damagePoint', 'hit mark', 'swing'],
|
|
162
|
+
map: () => exampleField({ name: 'Swings', players: 2 }),
|
|
163
|
+
start: (world) => {
|
|
164
|
+
const types = [ANIM.animationTurns, ANIM.animationRandom, ANIM.animationTwoHanded]
|
|
165
|
+
types.forEach((type, i) => {
|
|
166
|
+
const x = 18 + i * 6
|
|
167
|
+
// side on to the camera: the swordsman to the west, its dummy to the east
|
|
168
|
+
world.spawn(type, { at: { x: x - 0.7, z: 24 }, owner: PLAYER, facing: Math.PI / 2 })
|
|
169
|
+
world.spawn(UNIT.dummy, { at: { x: x + 0.7, z: 24 }, owner: ENEMY, facing: -Math.PI / 2 })
|
|
170
|
+
})
|
|
171
|
+
},
|
|
172
|
+
source: ['examples/animation/index.ts', 'examples/animation/looks.ts']
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: 'animation-blending',
|
|
176
|
+
category: 'animation',
|
|
177
|
+
title: 'Blending one clip into the next',
|
|
178
|
+
summary:
|
|
179
|
+
'A knight and a barbarian switch between the same clips, the knight blending each into the next, the barbarian snapping. Slide the blend time and pick the clips.',
|
|
180
|
+
tags: ['blend', 'blends', 'BlendDef', 'transition', 'crossfade', 'clips'],
|
|
181
|
+
start: (world) => row(world, [characterType('knight'), characterType('barbarian')], { x: 24, z: 24 }, 3),
|
|
182
|
+
source: ['examples/animation/index.ts', 'examples/animation/looks.ts']
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'animation-paces',
|
|
186
|
+
category: 'animation',
|
|
187
|
+
title: 'Feet on the ground at any pace',
|
|
188
|
+
summary:
|
|
189
|
+
'Three walkers at three speeds: a stroll, a walk and a run, each clip played as fast as the unit goes, so the feet keep to the ground. Turn the matching off to see them slide.',
|
|
190
|
+
tags: ['walk', 'run', 'stride', 'speed', 'loop', 'foot sliding', 'moveSpeed'],
|
|
191
|
+
map: () => exampleField({ name: 'Paces' }),
|
|
192
|
+
start: (world) => {
|
|
193
|
+
world.spawn(ANIM.animationSlow, { at: { x: 12, z: 21.5 }, owner: PLAYER })
|
|
194
|
+
world.spawn(ANIM.animationJog, { at: { x: 12, z: 24 }, owner: PLAYER })
|
|
195
|
+
world.spawn(ANIM.animationRun, { at: { x: 12, z: 26.5 }, owner: PLAYER })
|
|
196
|
+
},
|
|
197
|
+
source: ['examples/animation/index.ts', 'examples/animation/looks.ts']
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'animation-weapons',
|
|
201
|
+
category: 'animation',
|
|
202
|
+
title: 'Gear in hand',
|
|
203
|
+
summary:
|
|
204
|
+
'A knight at a dummy, with what it holds in each hand chosen on the card: the weapon is hung from the hand bone and moves with every clip, and the swing changes with it.',
|
|
205
|
+
tags: ['hold', 'weapons', 'gear', 'handslot', 'swap', 'equipment', 'looks.set'],
|
|
206
|
+
map: () => exampleField({ name: 'Gear', players: 2 }),
|
|
207
|
+
start: (world) => {
|
|
208
|
+
world.spawn(ANIM.animationWielder, { at: { x: 23.3, z: 24 }, owner: PLAYER, facing: Math.PI / 2 })
|
|
209
|
+
world.spawn(UNIT.dummy, { at: { x: 24.7, z: 24 }, owner: ENEMY, facing: -Math.PI / 2 })
|
|
210
|
+
},
|
|
211
|
+
source: ['examples/animation/index.ts', 'examples/animation/looks.ts']
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: 'animation-deaths',
|
|
215
|
+
category: 'animation',
|
|
216
|
+
title: 'Deaths and what is left',
|
|
217
|
+
summary:
|
|
218
|
+
'Five skeletons in a row, each with a death of its own: a fall, another fall, one that keeps its body, one that shatters to pieces, one that sinks away. Strike them down one by one, and raise them again.',
|
|
219
|
+
tags: ['death', 'die', 'DeathLook', 'corpse', 'shatter', 'clip', 'remains'],
|
|
220
|
+
start: (world) => row(world, Array(5).fill(ANIM.animationVictim), { x: 24, z: 24 }, 2.2),
|
|
221
|
+
source: ['examples/animation/index.ts', 'examples/animation/looks.ts']
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
id: 'animation-layers',
|
|
225
|
+
category: 'animation',
|
|
226
|
+
title: 'Upper body, lower body',
|
|
227
|
+
summary:
|
|
228
|
+
'A mage walks round a square while the card plays a clip on its upper body alone: the legs keep walking under a cast, a throw or a wave. Or play it on the whole body to see the difference.',
|
|
229
|
+
tags: ['mask', 'layer', 'upper body', 'ClipMask', 'playClip', 'additive', 'walk'],
|
|
230
|
+
map: () => exampleField({ name: 'Layers' }),
|
|
231
|
+
start: (world) => world.spawn(ANIM.animationWaver, { at: { x: 18, z: 18 }, owner: PLAYER }),
|
|
232
|
+
source: ['examples/animation/index.ts', 'examples/animation/looks.ts']
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|