@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,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The construction examples on the client's side: how each building looks as it goes up (`UnitLook.construction`:
|
|
3
|
+
* `motion`, `stages`, `working`, `done`, `cancelled`, `upgrade`), smoke and fire on a hurt one, the workers, and the
|
|
4
|
+
* buttons
|
|
5
|
+
*/
|
|
6
|
+
import type { ConstructionLook, GameClientApi, ModelDef, UnitLook } from '@v12-engine/client'
|
|
7
|
+
import { presetFx } from '@v12-engine/client'
|
|
8
|
+
import { builtSoFar } from '@v12-engine/engine'
|
|
9
|
+
import { button, type CategoryLooks, type ExampleLook, readout, slider } from '../../shell/look'
|
|
10
|
+
import { character, hold, ONE_HANDED, person } from '../common/looks'
|
|
11
|
+
import { buildTown, C, callOff, hurtAll, progressAll, relay, repairAll, sendWorkers, setSpeed, stopWork, upgrade } from './index'
|
|
12
|
+
|
|
13
|
+
/** A model of the shared art made `width` across: its size at scale 1 (across, up, deep) is in the assets' `manifest.json` */
|
|
14
|
+
const sized = (src: string, size: [number, number, number], width: number, own: Partial<ModelDef> = {}): ModelDef => ({
|
|
15
|
+
src,
|
|
16
|
+
scale: width / Math.max(size[0], size[2]),
|
|
17
|
+
...own
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const WOOD: [number, number, number] = [0.5, 0.36, 0.22]
|
|
21
|
+
|
|
22
|
+
/** At each blow of a worker's axe: sparks off it, and a puff of sawdust */
|
|
23
|
+
const WORKING: ConstructionLook['working'] = { fx: 'spark-burst', at: 'handslot.r', params: { count: 8, size: 0.5 } }
|
|
24
|
+
/** Finished: a cloud of dust, sparkles over it */
|
|
25
|
+
const DONE: ConstructionLook['done'] = { fx: 'dust' }
|
|
26
|
+
/** Called off: dust, and it folds back into the ground */
|
|
27
|
+
const CANCELLED: ConstructionLook['cancelled'] = { fx: 'dust', look: 'fold' }
|
|
28
|
+
|
|
29
|
+
/** Scaffolding of the shared art (`dungeon-kit`), 4 across at scale 1, made `width` across */
|
|
30
|
+
const scaffold = (name: string, width: number): ModelDef => ({ src: `dungeon-kit/${name}.glb`, scale: width / 4 })
|
|
31
|
+
|
|
32
|
+
/** Going up in scaffolding: dug earth, low then high scaffolding, then the building itself rising inside the last of it */
|
|
33
|
+
const staged = (width: number): ConstructionLook => ({
|
|
34
|
+
stages: [
|
|
35
|
+
{ at: 0, model: sized('rts/farm_dirt.glb', [5.4, 0.2, 4.3], width), fx: 'dust' },
|
|
36
|
+
{ at: 0.15, model: scaffold('scaffold_low_railing', width), fx: 'dust' },
|
|
37
|
+
{ at: 0.35, model: scaffold('scaffold_medium_railing', width), fx: 'dust' },
|
|
38
|
+
{ at: 0.6, model: scaffold('scaffold_high_railing', width), fx: 'dust' },
|
|
39
|
+
// a stage with no model is the building itself: from 80% on, the scaffolding gone
|
|
40
|
+
{ at: 0.8, fx: 'rubble-fall' }
|
|
41
|
+
],
|
|
42
|
+
motion: 'grow',
|
|
43
|
+
working: WORKING,
|
|
44
|
+
done: DONE,
|
|
45
|
+
cancelled: CANCELLED
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
/** A building of the shared art: its model, a box while it loads, smoke and fire as it is hurt, and how it goes up */
|
|
49
|
+
const building = (height: number, model: ModelDef, box: [number, number, number], construction: ConstructionLook): UnitLook => ({
|
|
50
|
+
height,
|
|
51
|
+
model,
|
|
52
|
+
shapes: [{ shape: 'box', size: box, offset: [0, box[1] / 2, 0], color: WOOD }],
|
|
53
|
+
construction: { working: WORKING, done: DONE, cancelled: CANCELLED, ...construction },
|
|
54
|
+
damaged: [
|
|
55
|
+
{ below: 0.5, fx: 'smoke', params: { size: 1.2 } },
|
|
56
|
+
{ below: 0.25, fx: 'fire', params: { size: 1.3 } }
|
|
57
|
+
],
|
|
58
|
+
death: { collapse: { look: 'crumble', pieces: 24, dust: true } }
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const looks: Record<string, UnitLook> = {
|
|
62
|
+
[C.worker]: {
|
|
63
|
+
height: 1.6,
|
|
64
|
+
model: character('barbarian', 0.6, { hold: [hold('axe_1handed')] }),
|
|
65
|
+
attacks: [ONE_HANDED],
|
|
66
|
+
shapes: person(1.6)
|
|
67
|
+
},
|
|
68
|
+
// grows up out of the ground, from a fifth of its height
|
|
69
|
+
[C.barracks]: building(2.6, sized('rts/barracks.glb', [3, 2.2, 3.2], 3), [2.6, 2, 2.6], { motion: 'grow' }),
|
|
70
|
+
// rises out of the ground whole, as if pushed up
|
|
71
|
+
[C.tower]: building(4.6, sized('rts/watch_tower.glb', [1.5, 4.4, 1.6], 1.9), [1.4, 4, 1.4], { motion: 'rise' }),
|
|
72
|
+
// fades in, from nearly see-through
|
|
73
|
+
[C.house]: building(2.6, sized('rts/house_3.glb', [2.7, 2.8, 2.4], 2.1), [1.8, 1.8, 1.8], { motion: 'fade' }),
|
|
74
|
+
// revealed from the ground up by a cut with a glowing edge, a cold blue
|
|
75
|
+
[C.temple]: building(5, sized('rts/temple_3.glb', [5.4, 5.4, 5.4], 3), [2.6, 3, 2.6], {
|
|
76
|
+
motion: 'reveal',
|
|
77
|
+
revealColor: [0.4, 0.75, 1],
|
|
78
|
+
revealEdge: 0.4,
|
|
79
|
+
done: { fx: 'sparkle' }
|
|
80
|
+
}),
|
|
81
|
+
// in scaffolding until near the end
|
|
82
|
+
[C.fortress]: building(4, sized('rts/wooden_fortress.glb', [6.6, 4.1, 5.9], 4), [3.6, 3, 3.6], staged(4)),
|
|
83
|
+
[C.farm]: building(2.6, sized('rts/farm.glb', [5.5, 3.9, 4.3], 3), [2.6, 1.6, 2.6], { motion: 'grow' }),
|
|
84
|
+
[C.mill]: building(4.5, sized('rts/windmill.glb', [4, 4.8, 2.8], 3), [2.4, 3.5, 2.4], { motion: 'rise' }),
|
|
85
|
+
[C.hall]: {
|
|
86
|
+
...building(3.6, sized('rts/town_center_2.glb', [3.8, 3.8, 3.7], 4), [3.6, 2.8, 3.6], staged(4)),
|
|
87
|
+
// upgraded: scaffolding round it by the upgrade's progress, the keep from 90%
|
|
88
|
+
construction: {
|
|
89
|
+
...staged(4),
|
|
90
|
+
upgrade: {
|
|
91
|
+
stages: [
|
|
92
|
+
{ at: 0, model: scaffold('scaffold_high_railing', 4.4), fx: 'dust' },
|
|
93
|
+
{ at: 0.9, fx: 'rubble-fall' }
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
[C.keep]: {
|
|
99
|
+
...building(3.4, sized('rts/castle.glb', [6.3, 2.8, 5.9], 4.2), [3.6, 2.8, 3.6], { motion: 'rise' }),
|
|
100
|
+
construction: {
|
|
101
|
+
upgrade: {
|
|
102
|
+
stages: [
|
|
103
|
+
{ at: 0, model: scaffold('scaffold_high_railing', 4.6), fx: 'dust' },
|
|
104
|
+
{ at: 0.9, fx: 'rubble-fall' }
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
[C.castle]: building(4.6, sized('rts/castle_fortress.glb', [6.4, 4.1, 5.9], 4.4), [3.8, 3.6, 3.8], { motion: 'rise' })
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The camera over a point: how far back and how steeply, turned as it is. Farther than the player may zoom out (48
|
|
114
|
+
* cells), the limit is let out for this example: what it gives back puts it back
|
|
115
|
+
*/
|
|
116
|
+
function look(client: GameClientApi, x: number, z: number, distance: number, pitch = 50): () => void {
|
|
117
|
+
if (distance > client.camera.farthest) client.camera.setLimits({ max: distance })
|
|
118
|
+
client.camera.setPose({ x, z, distance, yaw: client.camera.pose().yaw, pitch: (pitch * Math.PI) / 180 })
|
|
119
|
+
return () => client.camera.setLimits({ max: 48 })
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** The buildings of the player's in the snapshot: each with how far it is built (1 for one that stands) */
|
|
123
|
+
const buildings = (client: GameClientApi) =>
|
|
124
|
+
client.session
|
|
125
|
+
.snapshot()
|
|
126
|
+
.entities.filter((each) => each.type !== C.worker && each.health > 0 && each.owner === 1)
|
|
127
|
+
.map((each) => ({ ...each, built: builtSoFar(each) ?? 1 }))
|
|
128
|
+
|
|
129
|
+
const percent = (share: number) => `${Math.round(share * 100)}%`
|
|
130
|
+
|
|
131
|
+
const speedSlider = slider(
|
|
132
|
+
'Build speed',
|
|
133
|
+
{ min: 0.25, max: 6, step: 0.25, value: 1 },
|
|
134
|
+
(client, factor) => client.session.send(setSpeed.make({ factor })),
|
|
135
|
+
'How fast every site goes up, times its own build time: at 6 a building stands in seconds, at a quarter you can watch each stage of it.'
|
|
136
|
+
)
|
|
137
|
+
const again = (id: string) =>
|
|
138
|
+
button(
|
|
139
|
+
'Start again',
|
|
140
|
+
(client) => client.session.send(relay.make({ example: id })),
|
|
141
|
+
'Takes everything away and lays the scene out again as it started, the sites back at nothing.'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
const examples: Record<string, ExampleLook> = {
|
|
145
|
+
'construction-motions': {
|
|
146
|
+
controls: [
|
|
147
|
+
speedSlider,
|
|
148
|
+
again('construction-motions'),
|
|
149
|
+
readout(
|
|
150
|
+
'Built',
|
|
151
|
+
(client) =>
|
|
152
|
+
buildings(client)
|
|
153
|
+
.sort((a, b) => a.x - b.x)
|
|
154
|
+
.map((each) => percent(each.built))
|
|
155
|
+
.join(' · '),
|
|
156
|
+
'How far each site is built, left to right: grow, rise, fade, reveal, scaffolding.'
|
|
157
|
+
)
|
|
158
|
+
],
|
|
159
|
+
ready: (client) => look(client, 24, 23, 40, 40)
|
|
160
|
+
},
|
|
161
|
+
'construction-workers': {
|
|
162
|
+
controls: [
|
|
163
|
+
button(
|
|
164
|
+
'Send a worker',
|
|
165
|
+
(client) => client.session.send(sendWorkers.make({ count: 1 })),
|
|
166
|
+
'Sends one more idle worker to the fortress: the first worker counts whole, every other one half, so it goes up faster with each.'
|
|
167
|
+
),
|
|
168
|
+
button(
|
|
169
|
+
'Send them all',
|
|
170
|
+
(client) => client.session.send(sendWorkers.make({ count: 0 })),
|
|
171
|
+
'Sends every idle worker to the fortress at once: watch the percentage climb faster.'
|
|
172
|
+
),
|
|
173
|
+
button(
|
|
174
|
+
'Stop work',
|
|
175
|
+
(client) => client.session.send(stopWork.make({})),
|
|
176
|
+
'Every worker stops: the fortress stands where it got to, in its scaffolding, until someone works on it again.'
|
|
177
|
+
),
|
|
178
|
+
button(
|
|
179
|
+
'Cancel it',
|
|
180
|
+
(client) => client.session.send(callOff.make({})),
|
|
181
|
+
'Calls the fortress off, as its Cancel button would: it folds back into the ground in a cloud of dust.'
|
|
182
|
+
),
|
|
183
|
+
speedSlider,
|
|
184
|
+
again('construction-workers'),
|
|
185
|
+
readout('Fortress', (client) => {
|
|
186
|
+
const site = buildings(client)[0]
|
|
187
|
+
return site ? (site.built < 1 ? `${percent(site.built)} built` : 'finished') : 'none'
|
|
188
|
+
})
|
|
189
|
+
],
|
|
190
|
+
ready: (client) => look(client, 24, 22, 40, 42)
|
|
191
|
+
},
|
|
192
|
+
'construction-stages': {
|
|
193
|
+
controls: [
|
|
194
|
+
slider(
|
|
195
|
+
'Progress',
|
|
196
|
+
{ min: 0, max: 0.99, step: 0.01, value: 0.5 },
|
|
197
|
+
(client, share) => client.session.send(progressAll.make({ share })),
|
|
198
|
+
'Puts every site this far along: the scaffolding changes from low to medium to high, and past 80% the building itself shows.'
|
|
199
|
+
),
|
|
200
|
+
again('construction-stages'),
|
|
201
|
+
readout(
|
|
202
|
+
'Built',
|
|
203
|
+
(client) =>
|
|
204
|
+
buildings(client)
|
|
205
|
+
.sort((a, b) => a.x - b.x)
|
|
206
|
+
.map((each) => percent(each.built))
|
|
207
|
+
.join(' · '),
|
|
208
|
+
'How far each of the six is built, left to right.'
|
|
209
|
+
)
|
|
210
|
+
],
|
|
211
|
+
ready: (client) => look(client, 24, 24, 44, 38)
|
|
212
|
+
},
|
|
213
|
+
'construction-repair': {
|
|
214
|
+
controls: [
|
|
215
|
+
button(
|
|
216
|
+
'Repair it',
|
|
217
|
+
(client) => client.session.send(repairAll.make({})),
|
|
218
|
+
'Sends the three workers to mend the barracks: its health climbs, and the fire then the smoke go out as it passes a quarter and a half.'
|
|
219
|
+
),
|
|
220
|
+
button(
|
|
221
|
+
'Set it alight again',
|
|
222
|
+
(client) => client.session.send(hurtAll.make({ share: 0.12 })),
|
|
223
|
+
'A blast takes the barracks down to an eighth of its health: it smokes and burns again.'
|
|
224
|
+
),
|
|
225
|
+
readout('Health', (client) => {
|
|
226
|
+
const barracks = buildings(client)[0]
|
|
227
|
+
return barracks ? `${Math.round(barracks.health)} of ${barracks.maxHealth ?? 1200}` : 'gone'
|
|
228
|
+
})
|
|
229
|
+
],
|
|
230
|
+
ready: (client) => look(client, 24, 24, 34, 40)
|
|
231
|
+
},
|
|
232
|
+
'construction-upgrade': {
|
|
233
|
+
controls: [
|
|
234
|
+
button(
|
|
235
|
+
'Upgrade',
|
|
236
|
+
(client) => client.session.send(upgrade.make({})),
|
|
237
|
+
'Upgrades the town hall into a keep, or the keep into a castle: scaffolding goes up round it as the upgrade runs, and the new model shows near the end.'
|
|
238
|
+
),
|
|
239
|
+
speedSlider,
|
|
240
|
+
again('construction-upgrade'),
|
|
241
|
+
readout('Now', (client) => {
|
|
242
|
+
const standing = buildings(client)[0]
|
|
243
|
+
const names: Record<string, string> = { [C.hall]: 'Town hall', [C.keep]: 'Keep', [C.castle]: 'Castle' }
|
|
244
|
+
return standing ? (names[standing.type] ?? standing.type) : 'none'
|
|
245
|
+
})
|
|
246
|
+
],
|
|
247
|
+
ready: (client) => look(client, 24, 24, 36, 38)
|
|
248
|
+
},
|
|
249
|
+
'construction-town': {
|
|
250
|
+
controls: [
|
|
251
|
+
button(
|
|
252
|
+
'Build the town',
|
|
253
|
+
(client) => client.session.send(buildTown.make({})),
|
|
254
|
+
'Gives each worker its share of a town plan, a hall in the middle and houses, farms and towers round it: they walk to each site and build one after another.'
|
|
255
|
+
),
|
|
256
|
+
speedSlider,
|
|
257
|
+
again('construction-town'),
|
|
258
|
+
readout('Standing', (client) => {
|
|
259
|
+
const all = buildings(client)
|
|
260
|
+
return `${all.filter((each) => each.built >= 1).length} built · ${all.filter((each) => each.built < 1).length} going up`
|
|
261
|
+
})
|
|
262
|
+
],
|
|
263
|
+
ready: (client) => look(client, 24, 26, 60, 52)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const category: CategoryLooks = {
|
|
268
|
+
looks: examples,
|
|
269
|
+
view: (view) => ({
|
|
270
|
+
...view,
|
|
271
|
+
units: { ...view.units, looks: { ...view.units?.looks, ...looks } },
|
|
272
|
+
// the ready-made effects these examples play, under what the game's view says of them: a view plays none it did not take
|
|
273
|
+
fx: { ...view.fx, defs: { ...presetFx(['dust', 'spark-burst', 'sparkle', 'rubble-fall', 'smoke', 'fire', 'explosion']), ...view.fx?.defs } }
|
|
274
|
+
})
|
|
275
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bones turned in code, over what the clip poses: a head that looks at a point, an arm that reaches for one. The shared
|
|
3
|
+
* characters' bones are nodes of their model (`head`, `chest`, `upperarm.r`...), which the clip sets each frame as the
|
|
4
|
+
* scene is animated; turned after that (`scene.onAfterAnimationsObservable`), before the picture is drawn, they hold
|
|
5
|
+
* the clip's pose and the turn on top of it. Babylon alone, and the unit's model as the client draws it (`kit.model`)
|
|
6
|
+
*/
|
|
7
|
+
import { Vector3 } from '@babylonjs/core/Maths/math.vector.js'
|
|
8
|
+
import { Space } from '@babylonjs/core/Maths/math.axis.js'
|
|
9
|
+
import type { Scene } from '@babylonjs/core/scene.js'
|
|
10
|
+
import type { TransformNode } from '@babylonjs/core/Meshes/transformNode.js'
|
|
11
|
+
import type { GameClientApi } from '@v12-engine/client'
|
|
12
|
+
import type { Vec2 } from '@v12-engine/engine'
|
|
13
|
+
|
|
14
|
+
/** A bone of a unit's model by its name in the file, while the unit has a skeleton of its own: nothing before its model has loaded */
|
|
15
|
+
export function boneOf(client: GameClientApi, id: number, name: string): TransformNode | undefined {
|
|
16
|
+
const model = client.renderer.kit.model(id)
|
|
17
|
+
if (!model?.animator) return undefined
|
|
18
|
+
// the model's nodes keep their names from the file, a copy's with what it was copied as before them
|
|
19
|
+
return model.root
|
|
20
|
+
.getChildTransformNodes(false)
|
|
21
|
+
.find((node) => node.name === name || node.name.endsWith(`.${name}`) || node.name.endsWith(`_${name}`))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Runs `pose` once the clips have posed the scene each frame, until what it gives back is called: where code turns
|
|
26
|
+
* bones over the clip's pose. Animations not evaluated in a frame (a frozen game) give it nothing to do but turn again
|
|
27
|
+
*/
|
|
28
|
+
export function afterClips(scene: Scene, pose: (dt: number) => void): () => void {
|
|
29
|
+
let last = performance.now()
|
|
30
|
+
const observer = scene.onAfterAnimationsObservable.add(() => {
|
|
31
|
+
const now = performance.now()
|
|
32
|
+
pose(Math.min((now - last) / 1000, 0.1))
|
|
33
|
+
last = now
|
|
34
|
+
})
|
|
35
|
+
return () => scene.onAfterAnimationsObservable.remove(observer)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Its place in the world now: its parents' moves of this frame taken in first (the clip moved them a moment ago) */
|
|
39
|
+
export function worldOf(node: TransformNode): Vector3 {
|
|
40
|
+
const chain: TransformNode[] = []
|
|
41
|
+
for (let each: TransformNode | null = node; each; each = each.parent as TransformNode | null) chain.unshift(each)
|
|
42
|
+
for (const each of chain) each.computeWorldMatrix(true)
|
|
43
|
+
return node.getAbsolutePosition().clone()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Turns a bone in the world so that the way from `from` (a point on it, or its own place) to `along` points at
|
|
48
|
+
* `target` instead, by at most `most` radians, `share` of the way there: the step of an aim, and of a reach
|
|
49
|
+
*/
|
|
50
|
+
export function aimBone(bone: TransformNode, along: Vector3, target: Vector3, share = 1, most = Math.PI): void {
|
|
51
|
+
const from = worldOf(bone)
|
|
52
|
+
const now = along.subtract(from).normalize()
|
|
53
|
+
const want = target.subtract(from).normalize()
|
|
54
|
+
const axis = Vector3.Cross(now, want)
|
|
55
|
+
const length = axis.length()
|
|
56
|
+
if (length < 1e-5) return
|
|
57
|
+
const angle = Math.min(Math.acos(Math.max(-1, Math.min(1, Vector3.Dot(now, want)))), most) * share
|
|
58
|
+
bone.rotate(axis.scaleInPlace(1 / length), angle, Space.WORLD)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Turns a bone about the world's up by `yaw` and about the line to its right by `pitch`, radians: what a look is made of */
|
|
62
|
+
export function turnBone(bone: TransformNode, yaw: number, pitch: number, facing: number): void {
|
|
63
|
+
if (yaw) bone.rotate(Vector3.Up(), yaw, Space.WORLD)
|
|
64
|
+
// to its right: across the way it faces once it has turned
|
|
65
|
+
if (pitch) bone.rotate(new Vector3(Math.cos(facing + yaw), 0, -Math.sin(facing + yaw)), pitch, Space.WORLD)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The point of the flat ground (height 0) under a pixel of the canvas, or nothing where the ray goes up into the sky */
|
|
69
|
+
export function groundUnder(scene: Scene, x: number, y: number): Vec2 | undefined {
|
|
70
|
+
const ray = scene.createPickingRay(x, y, null, scene.activeCamera)
|
|
71
|
+
if (ray.direction.y > -1e-4) return undefined
|
|
72
|
+
const t = -ray.origin.y / ray.direction.y
|
|
73
|
+
return { x: ray.origin.x + ray.direction.x * t, z: ray.origin.z + ray.direction.z * t }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Where the pointer is over the scene's canvas, in its CSS pixels, kept as it moves (over the HUD too: what is over the
|
|
78
|
+
* canvas takes the pointer's events, the window hears them all); `stop` stops listening. Nothing before it first moves
|
|
79
|
+
*/
|
|
80
|
+
export function trackPointer(scene: Scene): { at?: { x: number; y: number }; stop(): void } {
|
|
81
|
+
const canvas = scene.getEngine().getRenderingCanvas()
|
|
82
|
+
const tracked: { at?: { x: number; y: number }; stop(): void } = { stop: () => window.removeEventListener('pointermove', moved) }
|
|
83
|
+
const moved = (event: PointerEvent) => {
|
|
84
|
+
const box = canvas?.getBoundingClientRect()
|
|
85
|
+
if (box) tracked.at = { x: event.clientX - box.left, y: event.clientY - box.top }
|
|
86
|
+
}
|
|
87
|
+
window.addEventListener('pointermove', moved)
|
|
88
|
+
return tracked
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Eases `now` toward `to` at `rate` a second, the same however long the frame was */
|
|
92
|
+
export const approach = (now: number, to: number, rate: number, dt: number): number => to + (now - to) * Math.exp(-rate * dt)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The examples of moves made in code, on the rules' side (`ExampleCategory`): the units they move and what keeps those
|
|
3
|
+
* units busy. The rules only say where a unit goes; how it moves on the screen (a head that turns, a hop, a wobble,
|
|
4
|
+
* a bank into a turn) is the client's alone, in `./looks.ts`, and the same on every screen without being sent
|
|
5
|
+
*/
|
|
6
|
+
import { defineCommand, defineIds, type Game, type UnitTypeDef, type Vec2, type World, exactMath } from '@v12-engine/engine'
|
|
7
|
+
import { ENEMY, PLAYER } from '../common/units'
|
|
8
|
+
import { type ExampleCategory, exampleField } from '../define'
|
|
9
|
+
|
|
10
|
+
/** The category's units */
|
|
11
|
+
export const MOVE = defineIds(
|
|
12
|
+
'customAnimationKnight',
|
|
13
|
+
'customAnimationSlime',
|
|
14
|
+
'customAnimationWaddler',
|
|
15
|
+
'customAnimationJelly',
|
|
16
|
+
'customAnimationBrute',
|
|
17
|
+
'customAnimationGlider',
|
|
18
|
+
'customAnimationAdept'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const units: UnitTypeDef[] = [
|
|
22
|
+
// stands still: its head, its arm are the client's to turn
|
|
23
|
+
{ id: MOVE.customAnimationKnight, name: 'Knight', kind: 'unit', radius: 0.45, stats: { health: 500, moveSpeed: 0 }, sight: 6 },
|
|
24
|
+
{ id: MOVE.customAnimationSlime, name: 'Slime', kind: 'unit', radius: 0.4, stats: { health: 200, moveSpeed: 2.6 }, sight: 6 },
|
|
25
|
+
{ id: MOVE.customAnimationWaddler, name: 'Waddler', kind: 'unit', radius: 0.45, stats: { health: 200, moveSpeed: 2.2 }, sight: 6 },
|
|
26
|
+
// takes blows and heals them at once: what wobbles when it is hit
|
|
27
|
+
{ id: MOVE.customAnimationJelly, name: 'Jelly', kind: 'unit', radius: 0.6, stats: { health: 2000, healthRegen: 400, moveSpeed: 0 }, sight: 4 },
|
|
28
|
+
{
|
|
29
|
+
id: MOVE.customAnimationBrute,
|
|
30
|
+
name: 'Brute',
|
|
31
|
+
kind: 'unit',
|
|
32
|
+
radius: 0.5,
|
|
33
|
+
stats: { health: 800, attackDamage: 30, attackSpeed: 0.7, attackRange: 0.6, moveSpeed: 0 },
|
|
34
|
+
sight: 5,
|
|
35
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 3 }]
|
|
36
|
+
},
|
|
37
|
+
{ id: MOVE.customAnimationGlider, name: 'Glider', kind: 'unit', radius: 0.6, flying: true, stats: { health: 300, moveSpeed: 7 }, sight: 10 },
|
|
38
|
+
{ id: MOVE.customAnimationAdept, name: 'Adept', kind: 'unit', radius: 0.45, stats: { health: 300, moveSpeed: 3 }, sight: 6 }
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
/** The middle of the examples' fields */
|
|
42
|
+
const MIDDLE: Vec2 = { x: 24, z: 24 }
|
|
43
|
+
|
|
44
|
+
/** A point a little way from the middle, a different one for each unit and each time, the same on every screen (the world's random) */
|
|
45
|
+
const somewhere = (world: World, reach: number): Vec2 => {
|
|
46
|
+
const angle = world.rng.next() * Math.PI * 2
|
|
47
|
+
const away = Math.sqrt(world.rng.next()) * reach
|
|
48
|
+
return { x: MIDDLE.x + exactMath.cos(angle) * away, z: MIDDLE.z + exactMath.sin(angle) * away }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The way round the flight example's field: a figure of eight, which the glider follows for ever */
|
|
52
|
+
export const FIGURE_EIGHT: Vec2[] = Array.from({ length: 16 }, (_, i) => {
|
|
53
|
+
const t = (i / 16) * Math.PI * 2
|
|
54
|
+
return { x: MIDDLE.x + exactMath.sin(t) * 14, z: MIDDLE.z + exactMath.sin(t * 2) * 7 }
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* What keeps the category's walkers walking, a few times a second: the slimes and waddlers to somewhere near the
|
|
59
|
+
* middle once they stand, and the glider on to the next point of its figure of eight as it nears the one it flies to
|
|
60
|
+
*/
|
|
61
|
+
const wandering = {
|
|
62
|
+
name: 'customAnimation.wandering',
|
|
63
|
+
every: 6,
|
|
64
|
+
update: (world: World) => {
|
|
65
|
+
for (const unit of world.units.values()) {
|
|
66
|
+
if (!unit.alive) continue
|
|
67
|
+
const type = unit.def.id
|
|
68
|
+
if (type === MOVE.customAnimationGlider) {
|
|
69
|
+
// the point it is nearest, and on to the one after: it never stops, and never turns back
|
|
70
|
+
const goal = unit.orders[0]?.init.point as Vec2 | undefined
|
|
71
|
+
if (goal && exactMath.hypot(goal.x - unit.pos.x, goal.z - unit.pos.z) > 3) continue
|
|
72
|
+
let near = 0
|
|
73
|
+
FIGURE_EIGHT.forEach((each, i) => {
|
|
74
|
+
if (
|
|
75
|
+
exactMath.hypot(each.x - unit.pos.x, each.z - unit.pos.z) <
|
|
76
|
+
exactMath.hypot(FIGURE_EIGHT[near]!.x - unit.pos.x, FIGURE_EIGHT[near]!.z - unit.pos.z)
|
|
77
|
+
)
|
|
78
|
+
near = i
|
|
79
|
+
})
|
|
80
|
+
unit.order('move', FIGURE_EIGHT[(near + 1) % FIGURE_EIGHT.length]!)
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
(type === MOVE.customAnimationSlime || type === MOVE.customAnimationWaddler || type === MOVE.customAnimationAdept) &&
|
|
85
|
+
!unit.orders.length &&
|
|
86
|
+
world.rng.next() < 0.25
|
|
87
|
+
)
|
|
88
|
+
unit.order('move', somewhere(world, type === MOVE.customAnimationAdept ? 6 : 10))
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** More adepts for the renderer example, each popping in where it is put */
|
|
94
|
+
export const summon = defineCommand('customAnimation.summon', {}, (world, player) => {
|
|
95
|
+
for (let i = 0; i < 3; i++) world.spawn(MOVE.customAnimationAdept, { at: somewhere(world, 6), owner: player })
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
export const category: ExampleCategory = {
|
|
99
|
+
rules: (game: Game<any>) => {
|
|
100
|
+
for (const unit of units) game.unit(unit)
|
|
101
|
+
game.system(wandering)
|
|
102
|
+
game.command(summon)
|
|
103
|
+
},
|
|
104
|
+
examples: [
|
|
105
|
+
{
|
|
106
|
+
id: 'custom-animation-look-at',
|
|
107
|
+
category: 'custom-animation',
|
|
108
|
+
title: 'A head that follows the pointer',
|
|
109
|
+
summary:
|
|
110
|
+
'A knight whose head and chest turn to look at the ground under the pointer, over whatever clip it plays: its bones turned in code after the clip has posed them, every frame. Move the pointer about.',
|
|
111
|
+
tags: ['bones', 'look at', 'head', 'skeleton', 'pointer', 'procedural', 'onBeforeRender'],
|
|
112
|
+
start: (world) => world.spawn(MOVE.customAnimationKnight, { at: MIDDLE, owner: PLAYER, facing: Math.PI }),
|
|
113
|
+
source: ['examples/custom-animation/index.ts', 'examples/custom-animation/looks.ts', 'examples/custom-animation/bones.ts']
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'custom-animation-reach',
|
|
117
|
+
category: 'custom-animation',
|
|
118
|
+
title: 'Reaching for it',
|
|
119
|
+
summary:
|
|
120
|
+
'A knight reaches its sword arm for a glowing orb that floats about it: the upper and lower arm turned toward the orb each frame, as a simple IK does. Let the orb follow the pointer, or wander on its own.',
|
|
121
|
+
tags: ['IK', 'inverse kinematics', 'reach', 'bones', 'arm', 'procedural', 'aim'],
|
|
122
|
+
start: (world) => world.spawn(MOVE.customAnimationKnight, { at: MIDDLE, owner: PLAYER, facing: Math.PI }),
|
|
123
|
+
source: ['examples/custom-animation/index.ts', 'examples/custom-animation/looks.ts', 'examples/custom-animation/bones.ts']
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'custom-animation-hop',
|
|
127
|
+
category: 'custom-animation',
|
|
128
|
+
title: 'Hops, squash and stretch',
|
|
129
|
+
summary:
|
|
130
|
+
"Slimes with no clips at all, made of a few solids, that hop where they go: stretched as they leave the ground, squashed as they land. The look's `pose` moves them each frame; slide how squashy they are.",
|
|
131
|
+
tags: ['squash', 'stretch', 'hop', 'pose', 'procedural', 'shapes', 'no clips'],
|
|
132
|
+
start: (world) => {
|
|
133
|
+
for (let i = 0; i < 12; i++) world.spawn(MOVE.customAnimationSlime, { at: somewhere(world, 9), owner: PLAYER })
|
|
134
|
+
},
|
|
135
|
+
source: ['examples/custom-animation/index.ts', 'examples/custom-animation/looks.ts']
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'custom-animation-waddle',
|
|
139
|
+
category: 'custom-animation',
|
|
140
|
+
title: 'A walk with no clips',
|
|
141
|
+
summary:
|
|
142
|
+
'Little creatures of a few solids that waddle: rocking from foot to foot, bobbing in step, leaning into their turns, as fast as they go. No model and no clips: they are a few solids on purpose, and moved in code alone.',
|
|
143
|
+
tags: ['walk', 'bob', 'lean', 'waddle', 'pose', 'procedural', 'shapes'],
|
|
144
|
+
start: (world) => {
|
|
145
|
+
for (let i = 0; i < 10; i++) world.spawn(MOVE.customAnimationWaddler, { at: somewhere(world, 9), owner: PLAYER })
|
|
146
|
+
},
|
|
147
|
+
source: ['examples/custom-animation/index.ts', 'examples/custom-animation/looks.ts']
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'custom-animation-springs',
|
|
151
|
+
category: 'custom-animation',
|
|
152
|
+
title: 'Springs: a wobble when hit',
|
|
153
|
+
summary:
|
|
154
|
+
'Jellies of a few solids, no model, that wobble when a brute hits them: each blow pushes a spring, which sways it away and back until it settles. Make the springs stiffer or looser, and damp them more or less.',
|
|
155
|
+
tags: ['spring', 'wobble', 'jiggle', 'hit reaction', 'pose', 'damping', 'procedural'],
|
|
156
|
+
map: () => exampleField({ name: 'Springs', players: 2 }),
|
|
157
|
+
start: (world) => {
|
|
158
|
+
// side on to the camera: each brute to the west of its jelly, facing it
|
|
159
|
+
for (let i = 0; i < 3; i++) {
|
|
160
|
+
const x = 18.5 + i * 6
|
|
161
|
+
world.spawn(MOVE.customAnimationJelly, { at: { x: x + 0.9, z: 24 }, owner: ENEMY, facing: 0 })
|
|
162
|
+
world.spawn(MOVE.customAnimationBrute, { at: { x: x - 0.9, z: 24 }, owner: PLAYER, facing: Math.PI / 2 })
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
source: ['examples/custom-animation/index.ts', 'examples/custom-animation/looks.ts']
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'custom-animation-flight',
|
|
169
|
+
category: 'custom-animation',
|
|
170
|
+
title: 'Banking through a figure of eight',
|
|
171
|
+
summary:
|
|
172
|
+
"A glider of a few solids flies a figure of eight for ever, banking into each turn as hard as it turns, pitching as it climbs and falls, with a trail behind. The rules fly it from point to point; the banking is the look's `pose`.",
|
|
173
|
+
tags: ['flying', 'bank', 'path', 'pose', 'altitude', 'trail', 'procedural'],
|
|
174
|
+
start: (world) => world.spawn(MOVE.customAnimationGlider, { at: FIGURE_EIGHT[0]!, owner: PLAYER }),
|
|
175
|
+
source: ['examples/custom-animation/index.ts', 'examples/custom-animation/looks.ts']
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'custom-animation-renderer',
|
|
179
|
+
category: 'custom-animation',
|
|
180
|
+
title: 'A renderer of your own',
|
|
181
|
+
summary:
|
|
182
|
+
"Runes that orbit every adept, drawn by a renderer of the game's own (`GameView.renderers`): told of each unit as it comes and goes, moved every frame, popping in with a tween. Summon more.",
|
|
183
|
+
tags: ['renderer', 'FeatureRenderer', 'orbit', 'tween', 'meshes', 'Babylon', 'per frame'],
|
|
184
|
+
start: (world) => {
|
|
185
|
+
for (let i = 0; i < 3; i++) world.spawn(MOVE.customAnimationAdept, { at: somewhere(world, 5), owner: PLAYER })
|
|
186
|
+
},
|
|
187
|
+
source: ['examples/custom-animation/index.ts', 'examples/custom-animation/looks.ts', 'examples/custom-animation/runes.ts']
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|