@v12-engine/create 0.1.1 → 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 +1 -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,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The destruction examples on the client's side: how what they break looks, and how it comes apart (`UnitLook.death`:
|
|
3
|
+
* a building crumbling, blown apart or toppled, trees and rocks shattered into pieces of their own models, flung with
|
|
4
|
+
* the client's physics), the grass they burn, and the buttons. The meteor's fall is drawn here; where it lands is the
|
|
5
|
+
* rules' (`destruction.meteor`)
|
|
6
|
+
*/
|
|
7
|
+
import type { DeathDef, DebrisPiece, GameClientApi, GameView, ModelDef, UnitLook } from '@v12-engine/client'
|
|
8
|
+
import { foliage, presetFoliage, presetFx } from '@v12-engine/client'
|
|
9
|
+
import { button, type CategoryLooks, type ExampleLook, readout, slider, toggle } from '../../shell/look'
|
|
10
|
+
import { bombard, D, fuse, GRASS, HOW, hurt, meteor, raze, relay, shockwave, smash } from './index'
|
|
11
|
+
|
|
12
|
+
/** A model of the shared art made `width` across: its size at scale 1 (across, up, deep) is in the assets' `manifest.json` */
|
|
13
|
+
const sized = (src: string, size: [number, number, number], width: number, own: Partial<ModelDef> = {}): ModelDef => ({
|
|
14
|
+
src,
|
|
15
|
+
scale: width / Math.max(size[0], size[2]),
|
|
16
|
+
...own
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const STONE: [number, number, number] = [0.6, 0.58, 0.55]
|
|
20
|
+
const WOOD: [number, number, number] = [0.45, 0.31, 0.18]
|
|
21
|
+
const LEAF: [number, number, number] = [0.22, 0.45, 0.18]
|
|
22
|
+
|
|
23
|
+
/** Planks, stones and straw: what a house throws down when it is told to (`death.debris`) */
|
|
24
|
+
const PLANKS: DebrisPiece[] = [
|
|
25
|
+
{ shape: 'box', size: [1.1, 0.12, 0.28], color: [0.45, 0.3, 0.18], count: 14 },
|
|
26
|
+
{ shape: 'box', size: [0.4, 0.3, 0.35], color: [0.55, 0.53, 0.5], count: 10 },
|
|
27
|
+
{ shape: 'box', size: [0.35, 0.05, 0.35], color: [0.85, 0.72, 0.38], count: 8 }
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
/** Blown apart: pieces of itself flung hard, an explosion, smoke after, smoke trailing a third of the pieces */
|
|
31
|
+
const EXPLODE: DeathDef = {
|
|
32
|
+
collapse: { look: 'explode', pieces: 36, dust: true },
|
|
33
|
+
strength: 3.2,
|
|
34
|
+
fx: [
|
|
35
|
+
{ fx: 'explosion', params: { size: 1.4 } },
|
|
36
|
+
{ fx: 'smoke', delay: 0.4 }
|
|
37
|
+
],
|
|
38
|
+
pieces: { fx: 'smoke-trail', share: 0.3, landed: 'dust', params: { size: 0.5 } }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** How a building comes down, by how the rules said it died */
|
|
42
|
+
const BUILDING_DEATH: DeathDef = {
|
|
43
|
+
collapse: { look: 'crumble', pieces: 26, dust: true },
|
|
44
|
+
fx: 'rubble-fall',
|
|
45
|
+
how: {
|
|
46
|
+
[HOW.crumble]: { collapse: { look: 'crumble', pieces: 30, dust: true }, fx: 'rubble-fall', pieces: { landed: 'dust', share: 0.2 } },
|
|
47
|
+
[HOW.explode]: EXPLODE,
|
|
48
|
+
[HOW.blasted]: { ...EXPLODE, strength: 4 },
|
|
49
|
+
[HOW.swept]: { ...EXPLODE, fx: 'dust' },
|
|
50
|
+
[HOW.topple]: { collapse: { look: 'topple', time: 2.4, dust: true }, fx: 'rubble-fall' },
|
|
51
|
+
[HOW.sink]: {
|
|
52
|
+
collapse: { look: 'sink', time: 3, dust: true },
|
|
53
|
+
fx: [
|
|
54
|
+
{ fx: 'dust', params: { size: 2 } },
|
|
55
|
+
{ fx: 'ground-crack', params: { size: 1.4 } }
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
[HOW.shatter]: { shatter: 40, strength: 1.4, fx: 'dust', pieces: { landed: 'dust', share: 0.15, params: { size: 0.4 } } },
|
|
59
|
+
[HOW.planks]: { debris: PLANKS, strength: 1.6, fx: 'rubble-fall' },
|
|
60
|
+
[HOW.crushed]: { collapse: { look: 'crumble', pieces: 30, dust: true }, strength: 1.2, fx: 'rubble-fall' }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Smoke under half its health, fire under a quarter; both go as it is mended */
|
|
65
|
+
const HURT: UnitLook['damaged'] = [
|
|
66
|
+
{ below: 0.5, fx: 'smoke', params: { size: 0.9 } },
|
|
67
|
+
{ below: 0.25, fx: 'fire', params: { size: 0.9 } }
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
/** A building of the shared art: its model, the box drawn while it loads, how it comes down and how it looks hurt */
|
|
71
|
+
const house = (height: number, model: ModelDef, box: [number, number, number], own: Partial<UnitLook> = {}): UnitLook => ({
|
|
72
|
+
height,
|
|
73
|
+
model,
|
|
74
|
+
shapes: [{ shape: 'box', size: box, offset: [0, box[1] / 2, 0], color: WOOD }],
|
|
75
|
+
death: BUILDING_DEATH,
|
|
76
|
+
damaged: HURT,
|
|
77
|
+
// put down again (Lay it out again), it rises out of the ground
|
|
78
|
+
construction: { motion: 'rise', done: { fx: 'dust' } },
|
|
79
|
+
...own
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
/** Trees of the shared art's forest a tree is drawn as, one picked for each */
|
|
83
|
+
const TREES = ['common_tree_1', 'common_tree_2', 'common_tree_3', 'common_tree_5', 'pine_1', 'pine_2', 'pine_3', 'pine_5']
|
|
84
|
+
const ROCKS = ['rock_medium_1', 'rock_medium_2', 'rock_medium_3']
|
|
85
|
+
|
|
86
|
+
/** Scenery broken: into pieces of its own model, flung away from what broke it */
|
|
87
|
+
const shattered = (pieces: number, strength: number, fx: string): DeathDef => ({
|
|
88
|
+
shatter: pieces,
|
|
89
|
+
strength,
|
|
90
|
+
fx,
|
|
91
|
+
pieces: { landed: 'dust', share: 0.1, params: { size: 0.3 } }
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const looks: Record<string, UnitLook> = {
|
|
95
|
+
[D.house]: house(2.6, sized('rts/house.glb', [2.6, 2.3, 3.5], 3), [2.6, 2, 2.6], {
|
|
96
|
+
// one of three houses for each, picked by its id
|
|
97
|
+
looks: {
|
|
98
|
+
house: sized('rts/house.glb', [2.6, 2.3, 3.5], 3),
|
|
99
|
+
house_3: sized('rts/house_3.glb', [2.7, 2.8, 2.4], 3),
|
|
100
|
+
house_4: sized('rts/house_4.glb', [2.7, 2.3, 2.6], 3)
|
|
101
|
+
},
|
|
102
|
+
mix: ['house', 'house_3', 'house_4']
|
|
103
|
+
}),
|
|
104
|
+
[D.hut]: house(2.2, sized('rts/hut.glb', [2.6, 2.3, 2.3], 2.1), [1.8, 1.6, 1.8]),
|
|
105
|
+
[D.tower]: house(4.6, sized('rts/watch_tower.glb', [1.5, 4.4, 1.6], 1.9), [1.4, 4, 1.4], {
|
|
106
|
+
// a tower falls over rather than in on itself, when nothing else is said
|
|
107
|
+
death: { ...BUILDING_DEATH, collapse: { look: 'topple', time: 2, dust: true } }
|
|
108
|
+
}),
|
|
109
|
+
[D.wall]: house(1.4, sized('rts/stone_wall.glb', [4.7, 1.3, 0.7], 3.1), [3, 1.2, 0.7], {
|
|
110
|
+
shapes: [{ shape: 'box', size: [3, 1.2, 0.7], offset: [0, 0.6, 0], color: STONE }],
|
|
111
|
+
death: { ...BUILDING_DEATH, collapse: { look: 'crumble', pieces: 16, dust: true } }
|
|
112
|
+
}),
|
|
113
|
+
[D.fence]: house(1.4, sized('rts/wooden_wall.glb', [4.1, 1.6, 0.2], 3.1), [3, 1.1, 0.2], {
|
|
114
|
+
death: { shatter: 10, strength: 2, fx: 'dust', how: { [HOW.planks]: { debris: PLANKS.slice(0, 1), strength: 1.5 } } },
|
|
115
|
+
damaged: []
|
|
116
|
+
}),
|
|
117
|
+
[D.keg]: {
|
|
118
|
+
height: 1,
|
|
119
|
+
model: sized('dungeon/barrel.glb', [1, 1.3, 1], 0.8),
|
|
120
|
+
shapes: [{ shape: 'cylinder', size: [0.7, 0.9, 0.7], offset: [0, 0.45, 0], color: [0.5, 0.33, 0.18] }],
|
|
121
|
+
// the blast is the rules' (an explosion where it stood): the keg itself goes to splinters
|
|
122
|
+
death: { shatter: 10, strength: 3.5, pieces: { fx: 'fire-trail', share: 0.4, params: { size: 0.3 } } }
|
|
123
|
+
},
|
|
124
|
+
[D.tree]: {
|
|
125
|
+
height: 3,
|
|
126
|
+
looks: Object.fromEntries(TREES.map((name) => [name, { src: `forest/${name}.glb`, scale: 0.8 }])),
|
|
127
|
+
mix: TREES,
|
|
128
|
+
vary: { scale: 0.25, lean: 0.06 },
|
|
129
|
+
shapes: [
|
|
130
|
+
{ shape: 'cylinder', size: [0.3, 1, 0.3], offset: [0, 0.5, 0], color: WOOD },
|
|
131
|
+
{ shape: 'cone', size: [1.5, 1.8, 1.5], offset: [0, 1.7, 0], color: LEAF }
|
|
132
|
+
],
|
|
133
|
+
death: {
|
|
134
|
+
...shattered(12, 1.6, 'leaves'),
|
|
135
|
+
how: {
|
|
136
|
+
[HOW.blasted]: shattered(14, 3.4, 'leaves'),
|
|
137
|
+
// a wood broken at once: fewer pieces each, so the physics keeps up
|
|
138
|
+
[HOW.swept]: shattered(6, 4, 'leaves'),
|
|
139
|
+
[HOW.crushed]: { clip: undefined, fx: 'leaves', shatter: 10, strength: 1.4 }
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
// grown back: up out of the ground, leaves shaken off it as it is whole
|
|
143
|
+
construction: { motion: 'grow', done: { fx: 'leaves' } }
|
|
144
|
+
},
|
|
145
|
+
[D.rock]: {
|
|
146
|
+
height: 1.3,
|
|
147
|
+
looks: Object.fromEntries(ROCKS.map((name) => [name, { src: `forest/${name}.glb`, scale: 1.25 }])),
|
|
148
|
+
mix: ROCKS,
|
|
149
|
+
vary: { scale: 0.2 },
|
|
150
|
+
shapes: [{ shape: 'sphere', size: [1.8, 1.2, 1.8], offset: [0, 0.5, 0], color: STONE }],
|
|
151
|
+
death: { ...shattered(18, 1.6, 'dust'), how: { [HOW.blasted]: shattered(20, 3.2, 'dust'), [HOW.swept]: shattered(18, 2.8, 'dust') } },
|
|
152
|
+
construction: { motion: 'rise', done: { fx: 'dust' } }
|
|
153
|
+
},
|
|
154
|
+
// the shared art has no catapult: a cart of the village's, its shafts to the back, a boulder of the RTS kit's loaded
|
|
155
|
+
// on it, throwing boulders like it. The parts below are drawn while it loads
|
|
156
|
+
[D.catapult]: {
|
|
157
|
+
height: 2,
|
|
158
|
+
model: {
|
|
159
|
+
...sized('village/cart.glb', [0.693, 1.194, 1.391], 2.1, { turn: Math.PI }),
|
|
160
|
+
points: { load: { offset: [0, 0.75, -0.2] } },
|
|
161
|
+
hold: [{ src: 'rts/rock.glb', at: 'load' }]
|
|
162
|
+
},
|
|
163
|
+
shapes: [
|
|
164
|
+
{ shape: 'box', size: [1.4, 0.4, 2], offset: [0, 0.5, 0], color: WOOD },
|
|
165
|
+
{ shape: 'cylinder', size: [0.7, 0.25, 0.7], offset: [-0.8, 0.35, 0.6], color: [0.3, 0.2, 0.12] },
|
|
166
|
+
{ shape: 'cylinder', size: [0.7, 0.25, 0.7], offset: [0.8, 0.35, 0.6], color: [0.3, 0.2, 0.12] },
|
|
167
|
+
{ shape: 'cylinder', size: [0.7, 0.25, 0.7], offset: [-0.8, 0.35, -0.6], color: [0.3, 0.2, 0.12] },
|
|
168
|
+
{ shape: 'cylinder', size: [0.7, 0.25, 0.7], offset: [0.8, 0.35, -0.6], color: [0.3, 0.2, 0.12] },
|
|
169
|
+
{ shape: 'box', size: [0.2, 1.8, 0.2], offset: [0, 1.4, -0.3], color: [0.55, 0.38, 0.22] },
|
|
170
|
+
{ shape: 'box', size: [0.6, 0.25, 0.6], offset: [0, 2.3, -0.3], color: 'team' }
|
|
171
|
+
],
|
|
172
|
+
shot: { model: 'rts/rock.glb', color: [0.5, 0.48, 0.45], size: 0.55 },
|
|
173
|
+
death: { debris: PLANKS.slice(0, 1), strength: 1.2, fx: 'dust' }
|
|
174
|
+
},
|
|
175
|
+
// nothing to see: where a blast comes from
|
|
176
|
+
[D.blast]: { height: 0.1, shapes: [], death: 'none' }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** The ground's grass of these examples: a meadow, drawn black and short where it burnt, flat where it was laid down */
|
|
180
|
+
const grass = foliage({ ...presetFoliage.meadow, ruined: { stubble: 0.12 } })
|
|
181
|
+
|
|
182
|
+
// ---- the meteor's fall, drawn
|
|
183
|
+
|
|
184
|
+
/** How long a meteor takes to come down, in seconds */
|
|
185
|
+
const FALL = 0.9
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A meteor falls on a point: a fireball from high in the sky, its trail behind it, and when it reaches the ground the
|
|
189
|
+
* rules are told (`destruction.meteor`): what breaks is theirs. The fall is the screen's alone
|
|
190
|
+
*/
|
|
191
|
+
function callMeteor(client: GameClientApi, at: { x: number; z: number }, size = 1): void {
|
|
192
|
+
const from = { x: at.x - 14, y: 34, z: at.z + 10 }
|
|
193
|
+
const ball = client.fx.play('fireball', { at: from, params: { size: 2.4 * size, light: 4, impact: '' } })
|
|
194
|
+
client.timers.tween(
|
|
195
|
+
FALL,
|
|
196
|
+
(t) => {
|
|
197
|
+
const ease = t * t
|
|
198
|
+
ball.move({ at: { x: from.x + (at.x - from.x) * ease, y: from.y * (1 - ease) + 0.5, z: from.z + (at.z - from.z) * ease } })
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
onEnd: () => {
|
|
202
|
+
ball.stop()
|
|
203
|
+
client.session.send(meteor.make({ x: at.x, z: at.z, size }))
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Where on the ground a click of the pointer is, or nothing over the sky */
|
|
210
|
+
function groundUnder(client: GameClientApi, event: PointerEvent): { x: number; z: number } | null {
|
|
211
|
+
const canvas = client.scene.getEngine().getRenderingCanvas()
|
|
212
|
+
if (!canvas) return null
|
|
213
|
+
const rect = canvas.getBoundingClientRect()
|
|
214
|
+
// the client's own (`GameClient.groundAt`): what a click picks; not in `GameClientApi`, so asked for by name
|
|
215
|
+
const picker = client as GameClientApi & { groundAt?: (x: number, y: number) => { x: number; z: number } | null }
|
|
216
|
+
return picker.groundAt?.(event.clientX - rect.left, event.clientY - rect.top) ?? null
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The camera over a point: how far back and how steeply, turned as it is. Farther than the player may zoom out (48
|
|
221
|
+
* cells), the limit is let out for this example: what it gives back puts it back
|
|
222
|
+
*/
|
|
223
|
+
function look(client: GameClientApi, x: number, z: number, distance: number, pitch = 50): () => void {
|
|
224
|
+
if (distance > client.camera.farthest) client.camera.setLimits({ max: distance })
|
|
225
|
+
client.camera.setPose({ x, z, distance, yaw: client.camera.pose().yaw, pitch: (pitch * Math.PI) / 180 })
|
|
226
|
+
return () => client.camera.setLimits({ max: 48 })
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** A count of what still stands of some types */
|
|
230
|
+
const standing = (client: GameClientApi, types: readonly string[]) =>
|
|
231
|
+
client.session.snapshot().entities.filter((each) => types.includes(each.type) && each.health > 0).length
|
|
232
|
+
|
|
233
|
+
const again = (id: string) =>
|
|
234
|
+
button(
|
|
235
|
+
'Lay it out again',
|
|
236
|
+
(client) => client.session.send(relay.make({ example: id })),
|
|
237
|
+
'Clears what is left and puts the scene back as it started: buildings rise out of the ground, the grass whole again.'
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
/** Whether a click calls a meteor down where it lands: the meteor example's, off as it is left */
|
|
241
|
+
let clickToStrike = false
|
|
242
|
+
|
|
243
|
+
const examples: Record<string, ExampleLook> = {
|
|
244
|
+
'destruction-collapse': {
|
|
245
|
+
controls: [
|
|
246
|
+
button(
|
|
247
|
+
'Crumble',
|
|
248
|
+
(client) => client.session.send(raze.make({ how: HOW.crumble })),
|
|
249
|
+
'Brings down the first building of the row still standing: pieces of its own model slump where it stood, in a cloud of dust.'
|
|
250
|
+
),
|
|
251
|
+
button(
|
|
252
|
+
'Blow apart',
|
|
253
|
+
(client) => client.session.send(raze.make({ how: HOW.explode })),
|
|
254
|
+
'Blows up the next building: an explosion flings pieces of its model away, a third of them trailing smoke.'
|
|
255
|
+
),
|
|
256
|
+
button(
|
|
257
|
+
'Topple',
|
|
258
|
+
(client) => client.session.send(raze.make({ how: HOW.topple })),
|
|
259
|
+
'The next building tips over, away from the camera, in one piece and sinks into the ground.'
|
|
260
|
+
),
|
|
261
|
+
button(
|
|
262
|
+
'Sink',
|
|
263
|
+
(client) => client.session.send(raze.make({ how: HOW.sink })),
|
|
264
|
+
'The next building sinks straight down into the ground in one piece, the ground cracking round it.'
|
|
265
|
+
),
|
|
266
|
+
button(
|
|
267
|
+
'Shatter',
|
|
268
|
+
(client) => client.session.send(raze.make({ how: HOW.shatter })),
|
|
269
|
+
'The next building is split into 40 pieces of its own model that tumble with physics and settle on the ground.'
|
|
270
|
+
),
|
|
271
|
+
button(
|
|
272
|
+
'Planks',
|
|
273
|
+
(client) => client.session.send(raze.make({ how: HOW.planks })),
|
|
274
|
+
'The next building falls as pieces described in code, planks, stones and straw, rather than pieces of its model.'
|
|
275
|
+
),
|
|
276
|
+
button(
|
|
277
|
+
'Hurt them',
|
|
278
|
+
(client) => client.session.send(hurt.make({ share: 0.2 })),
|
|
279
|
+
'Takes every building down to a fifth of its health: each smokes under half and burns under a quarter.'
|
|
280
|
+
),
|
|
281
|
+
again('destruction-collapse'),
|
|
282
|
+
readout('Standing', (client) => `${standing(client, [D.house, D.hut, D.tower])} of 6`, 'How many of the six buildings of the row still stand.')
|
|
283
|
+
],
|
|
284
|
+
// the row a little left of the middle: the card stands over the right of the picture
|
|
285
|
+
ready: (client) => look(client, 31, 22, 60, 44)
|
|
286
|
+
},
|
|
287
|
+
'destruction-meteor': {
|
|
288
|
+
controls: [
|
|
289
|
+
button(
|
|
290
|
+
'Meteor',
|
|
291
|
+
(client) => callMeteor(client, { x: 24, z: 24 }, 1),
|
|
292
|
+
'Drops a meteor on the middle of the village: it digs a crater and blows apart what stands near.'
|
|
293
|
+
),
|
|
294
|
+
button(
|
|
295
|
+
'Meteor shower',
|
|
296
|
+
(client) => {
|
|
297
|
+
const spots = [
|
|
298
|
+
[17, 29],
|
|
299
|
+
[31, 27],
|
|
300
|
+
[24, 17],
|
|
301
|
+
[14, 18],
|
|
302
|
+
[34, 18],
|
|
303
|
+
[24, 34]
|
|
304
|
+
]
|
|
305
|
+
spots.forEach(([x, z], i) => client.timers.after(i * 0.45, () => callMeteor(client, { x, z }, 0.6)))
|
|
306
|
+
},
|
|
307
|
+
'Six smaller meteors, one after another round the village: each digs its own crater and burns the grass.'
|
|
308
|
+
),
|
|
309
|
+
toggle(
|
|
310
|
+
'Click to strike',
|
|
311
|
+
false,
|
|
312
|
+
(_client, on) => (clickToStrike = on),
|
|
313
|
+
'While on, a click on the ground calls a meteor down where you clicked, instead of selecting.'
|
|
314
|
+
),
|
|
315
|
+
again('destruction-meteor'),
|
|
316
|
+
readout('Still standing', (client) => `${standing(client, [D.house, D.hut, D.tower, D.fence])} buildings · ${standing(client, [D.tree])} trees`)
|
|
317
|
+
],
|
|
318
|
+
ready: (client) => {
|
|
319
|
+
const camera = look(client, 24, 22, 58, 48)
|
|
320
|
+
const canvas = client.scene.getEngine().getRenderingCanvas()
|
|
321
|
+
const strike = (event: PointerEvent) => {
|
|
322
|
+
if (!clickToStrike || event.button !== 0) return
|
|
323
|
+
const at = groundUnder(client, event)
|
|
324
|
+
if (!at) return
|
|
325
|
+
// the click is the meteor's: nothing is selected by it
|
|
326
|
+
event.stopImmediatePropagation()
|
|
327
|
+
callMeteor(client, at, 0.8)
|
|
328
|
+
}
|
|
329
|
+
canvas?.addEventListener('pointerdown', strike, { capture: true })
|
|
330
|
+
return () => {
|
|
331
|
+
clickToStrike = false
|
|
332
|
+
canvas?.removeEventListener('pointerdown', strike, { capture: true })
|
|
333
|
+
camera()
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
'destruction-shockwave': {
|
|
338
|
+
controls: [
|
|
339
|
+
button(
|
|
340
|
+
'Shockwave',
|
|
341
|
+
(client) => client.session.send(shockwave.make({})),
|
|
342
|
+
'The knight sends a ring out round him: every tree, rock and fence it passes is broken and flung away, the grass laid flat.'
|
|
343
|
+
),
|
|
344
|
+
again('destruction-shockwave'),
|
|
345
|
+
readout('Trees standing', (client) => String(standing(client, [D.tree])))
|
|
346
|
+
],
|
|
347
|
+
ready: (client) => {
|
|
348
|
+
const camera = look(client, 24, 24, 60, 55)
|
|
349
|
+
// a whole wood broken at once: room for many more pieces than the player's Debris setting keeps
|
|
350
|
+
const was = client.debrisMost
|
|
351
|
+
client.debrisMost = 1500
|
|
352
|
+
return () => {
|
|
353
|
+
client.debrisMost = was
|
|
354
|
+
camera()
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
'destruction-chain': {
|
|
359
|
+
controls: [
|
|
360
|
+
button(
|
|
361
|
+
'Light the fuse',
|
|
362
|
+
(client) => client.session.send(fuse.make({})),
|
|
363
|
+
'Sets off the first keg of powder: its blast sets off the kegs near it, and the fort goes up one keg after another.'
|
|
364
|
+
),
|
|
365
|
+
again('destruction-chain'),
|
|
366
|
+
readout('Kegs left', (client) => String(standing(client, [D.keg])))
|
|
367
|
+
],
|
|
368
|
+
ready: (client) => look(client, 23, 24, 62, 55)
|
|
369
|
+
},
|
|
370
|
+
'destruction-regrow': {
|
|
371
|
+
controls: [
|
|
372
|
+
button(
|
|
373
|
+
'Smash it all',
|
|
374
|
+
(client) => client.session.send(smash.make({})),
|
|
375
|
+
'Breaks every tree and rock of the meadow at once: a few seconds later each grows back out of the ground where it stood, and the grass comes back.'
|
|
376
|
+
),
|
|
377
|
+
readout('Standing', (client) => `${standing(client, [D.tree])} trees · ${standing(client, [D.rock])} rocks`)
|
|
378
|
+
],
|
|
379
|
+
ready: (client) => look(client, 24, 25, 52, 48)
|
|
380
|
+
},
|
|
381
|
+
'destruction-siege': {
|
|
382
|
+
controls: [
|
|
383
|
+
button(
|
|
384
|
+
'Fire!',
|
|
385
|
+
(client) => client.session.send(bombard.make({})),
|
|
386
|
+
'Each catapult throws boulders at what of the fort is nearest it, and at the next as that falls.'
|
|
387
|
+
),
|
|
388
|
+
slider(
|
|
389
|
+
'Debris pieces',
|
|
390
|
+
{ min: 0, max: 600, step: 20, value: 240 },
|
|
391
|
+
(client, value) => (client.debrisMost = value),
|
|
392
|
+
'The most broken pieces drawn at once (the Debris setting): at 0 nothing breaks apart into pieces, buildings fall without them; fire again to see the difference.'
|
|
393
|
+
),
|
|
394
|
+
again('destruction-siege'),
|
|
395
|
+
readout('Fort standing', (client) => `${standing(client, [D.wall, D.tower, D.house, D.hut])} of 10`)
|
|
396
|
+
],
|
|
397
|
+
ready: (client) => look(client, 24, 23, 56, 48)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** The ready-made effects the destruction examples play (`presetFx`): a view plays none it did not take */
|
|
402
|
+
const FX = [
|
|
403
|
+
'explosion',
|
|
404
|
+
'fireball',
|
|
405
|
+
'smoke',
|
|
406
|
+
'fire',
|
|
407
|
+
'dust',
|
|
408
|
+
'rubble-fall',
|
|
409
|
+
'ground-crack',
|
|
410
|
+
'shockwave',
|
|
411
|
+
'impact-ring',
|
|
412
|
+
'leaves',
|
|
413
|
+
'smoke-trail',
|
|
414
|
+
'fire-trail',
|
|
415
|
+
'blood-burst',
|
|
416
|
+
'spark-burst'
|
|
417
|
+
] as const
|
|
418
|
+
|
|
419
|
+
/** The meteor and the shockwave kill people too: blown off their feet (`deathFor`, for every type of the game) */
|
|
420
|
+
const blownApart = (view: GameView): GameView['units'] => ({
|
|
421
|
+
...view.units,
|
|
422
|
+
looks: { ...view.units?.looks, ...looks },
|
|
423
|
+
deathFor: (entity, death) => {
|
|
424
|
+
if (entity.def.kind === 'unit' && (death.how === HOW.blasted || death.how === HOW.swept))
|
|
425
|
+
return { fx: [{ fx: 'blood-burst' }, { fx: 'dust' }], strength: 2 }
|
|
426
|
+
return view.units?.deathFor?.(entity, death)
|
|
427
|
+
},
|
|
428
|
+
// no bars over scenery, nor over a tree growing back: only over what is fought over
|
|
429
|
+
hud: {
|
|
430
|
+
...view.units?.hud,
|
|
431
|
+
bars: (entity) => (SCENERY.has(entity.def.id) ? { hide: true } : view.units?.hud?.bars?.(entity))
|
|
432
|
+
}
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
/** What stands about to be broken: no health or progress bars over it */
|
|
436
|
+
const SCENERY = new Set<string>([D.tree, D.rock, D.keg, D.fence, D.blast])
|
|
437
|
+
|
|
438
|
+
export const category: CategoryLooks = {
|
|
439
|
+
looks: examples,
|
|
440
|
+
view: (view) => ({
|
|
441
|
+
...view,
|
|
442
|
+
units: blownApart(view),
|
|
443
|
+
// the ready-made effects these examples play, under what the game's view says of them
|
|
444
|
+
fx: { ...view.fx, defs: { ...presetFx(FX), ...view.fx?.defs } },
|
|
445
|
+
ground: { ...view.ground, cellLayers: { ...view.ground?.cellLayers, [GRASS]: grass } }
|
|
446
|
+
})
|
|
447
|
+
}
|