@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,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The weathers of the weather example, as looks of the map (`MapEnvironment`): each the sky, the clouds, the light,
|
|
3
|
+
* the wind, the mist and what falls, for an amount of it from 0 (hardly any) to 1 (all it gets). The rules set one for
|
|
4
|
+
* every player (`world.setEnvironment(look, { over })`), and every screen eases into it
|
|
5
|
+
*/
|
|
6
|
+
import type { MapEnvironment } from '@v12-engine/engine'
|
|
7
|
+
|
|
8
|
+
/** The weathers there are */
|
|
9
|
+
export type WeatherKind = 'clear' | 'rain' | 'storm' | 'snow'
|
|
10
|
+
|
|
11
|
+
/** Between two numbers, `t` of the way */
|
|
12
|
+
const mix = (a: number, b: number, t: number) => a + (b - a) * t
|
|
13
|
+
/** Between two colours, `t` of the way */
|
|
14
|
+
const mixRgb = (a: readonly number[], b: readonly number[], t: number): [number, number, number] => [
|
|
15
|
+
mix(a[0], b[0], t),
|
|
16
|
+
mix(a[1], b[1], t),
|
|
17
|
+
mix(a[2], b[2], t)
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
/** Each weather's look, for an amount of it from 0 to 1 */
|
|
21
|
+
export const WEATHER: Record<WeatherKind, (amount: number) => Partial<MapEnvironment>> = {
|
|
22
|
+
// a clear day: a few clouds, a light breeze, nothing falling (what fell fades out)
|
|
23
|
+
clear: (amount) => ({
|
|
24
|
+
sky: [0.5, 0.7, 0.92],
|
|
25
|
+
clouds: { cover: mix(0.1, 0.4, amount), color: [1, 1, 1] },
|
|
26
|
+
cloudLayer: { height: 22, cover: mix(0.1, 0.35, amount), opacity: 0.5, shadows: 0.4 },
|
|
27
|
+
light: { sunUp: 45, sunAround: 200, sun: 1.4, sunColor: [1, 0.95, 0.85], ambient: 0.6, ambientColor: [0.9, 0.95, 1] },
|
|
28
|
+
wind: { around: 80, strength: 0.25, gusts: 0.3 },
|
|
29
|
+
weather: { kind: 'rain', amount: 0 },
|
|
30
|
+
fog: { start: 70, end: 200 },
|
|
31
|
+
mist: {}
|
|
32
|
+
}),
|
|
33
|
+
// rain from a grey sky: the more of it, the darker and windier, the more splashes
|
|
34
|
+
rain: (amount) => ({
|
|
35
|
+
sky: mixRgb([0.55, 0.62, 0.7], [0.36, 0.4, 0.46], amount),
|
|
36
|
+
clouds: { cover: mix(0.6, 0.9, amount), color: mixRgb([0.85, 0.87, 0.9], [0.6, 0.62, 0.66], amount) },
|
|
37
|
+
cloudLayer: { height: 18, cover: mix(0.5, 0.75, amount), opacity: 0.6, shadows: 0.45 },
|
|
38
|
+
light: {
|
|
39
|
+
sunUp: 40,
|
|
40
|
+
sunAround: 200,
|
|
41
|
+
sun: mix(0.9, 0.6, amount),
|
|
42
|
+
sunColor: [0.9, 0.93, 1],
|
|
43
|
+
ambient: mix(0.6, 0.5, amount),
|
|
44
|
+
ambientColor: [0.8, 0.85, 0.95]
|
|
45
|
+
},
|
|
46
|
+
wind: { around: 100, strength: mix(0.3, 0.6, amount), gusts: 0.5 },
|
|
47
|
+
weather: { kind: 'rain', amount: mix(0.25, 1, amount), splashes: mix(0.3, 0.8, amount) },
|
|
48
|
+
fog: { start: mix(60, 35, amount), end: mix(180, 120, amount) },
|
|
49
|
+
mist: { ground: { base: 0, top: 1.5, density: mix(0.1, 0.3, amount), color: [0.6, 0.64, 0.7] } }
|
|
50
|
+
}),
|
|
51
|
+
// a storm: dark, a gale slanting the rain, low cloud racing over
|
|
52
|
+
storm: (amount) => ({
|
|
53
|
+
sky: mixRgb([0.3, 0.33, 0.38], [0.16, 0.18, 0.22], amount),
|
|
54
|
+
clouds: { cover: 0.95, speed: 2.5, color: [0.42, 0.44, 0.48] },
|
|
55
|
+
cloudLayer: { height: 14, cover: mix(0.7, 0.9, amount), opacity: 0.75, shadows: 0.6, scale: 18 },
|
|
56
|
+
light: {
|
|
57
|
+
sunUp: 35,
|
|
58
|
+
sunAround: 200,
|
|
59
|
+
sun: mix(0.55, 0.35, amount),
|
|
60
|
+
sunColor: [0.75, 0.82, 1],
|
|
61
|
+
ambient: mix(0.45, 0.35, amount),
|
|
62
|
+
ambientColor: [0.6, 0.66, 0.8]
|
|
63
|
+
},
|
|
64
|
+
wind: { around: 120, strength: mix(0.75, 1, amount), gusts: 0.9 },
|
|
65
|
+
weather: { kind: 'rain', amount: 1, splashes: 1, size: 1.2, speed: 1.3 },
|
|
66
|
+
fog: { start: mix(35, 20, amount), end: mix(120, 80, amount) },
|
|
67
|
+
mist: { ground: { base: 0, top: 2.5, density: mix(0.25, 0.45, amount), color: [0.4, 0.43, 0.5] } }
|
|
68
|
+
}),
|
|
69
|
+
// snow: a white sky, the flakes drifting, the ground going white the more falls
|
|
70
|
+
snow: (amount) => ({
|
|
71
|
+
sky: mixRgb([0.78, 0.82, 0.88], [0.7, 0.73, 0.78], amount),
|
|
72
|
+
clouds: { cover: 0.85, speed: 0.4, color: [0.92, 0.93, 0.96] },
|
|
73
|
+
cloudLayer: { height: 20, cover: 0.6, opacity: 0.5, shadows: 0.3 },
|
|
74
|
+
light: { sunUp: 25, sunAround: 190, sun: 0.9, sunColor: [0.95, 0.97, 1], ambient: 0.75, ambientColor: [0.88, 0.92, 1] },
|
|
75
|
+
wind: { around: 60, strength: mix(0.15, 0.45, amount), gusts: 0.4 },
|
|
76
|
+
weather: { kind: 'snow', amount: mix(0.3, 1, amount), settle: mix(0.4, 0.9, amount) },
|
|
77
|
+
fog: { start: mix(55, 30, amount), end: mix(170, 110, amount), color: [0.85, 0.87, 0.9] },
|
|
78
|
+
mist: { ground: { base: 0, top: 1.5, density: 0.2, color: [0.9, 0.92, 0.95] } }
|
|
79
|
+
})
|
|
80
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The performance examples on the rules' side: big fields, and commands that lay out armies of any size on them, march
|
|
3
|
+
* them and set them fighting (`ExampleCategory`). The armies are the category's own footmen and archers, so their looks
|
|
4
|
+
* can be drawn as baked crowds (`./looks.ts`) without changing the shared units'. Each example starts small, so its world
|
|
5
|
+
* is quick to make with no page; the card's slider asks for thousands
|
|
6
|
+
*/
|
|
7
|
+
import {
|
|
8
|
+
defineCommand,
|
|
9
|
+
defineIds,
|
|
10
|
+
type Entity,
|
|
11
|
+
type Game,
|
|
12
|
+
number,
|
|
13
|
+
oneOf,
|
|
14
|
+
type PlayerId,
|
|
15
|
+
type UnitTypeDef,
|
|
16
|
+
type Vec2,
|
|
17
|
+
type World
|
|
18
|
+
} from '@v12-engine/engine'
|
|
19
|
+
import { ENEMY, PLAYER } from '../common/units'
|
|
20
|
+
import { type ExampleCategory, exampleField } from '../define'
|
|
21
|
+
|
|
22
|
+
/** The category's units: a footman and an archer of their own, and a post to shoot at */
|
|
23
|
+
export const PERF = defineIds('performanceFootman', 'performanceArcher', 'performanceTarget')
|
|
24
|
+
|
|
25
|
+
/** The most units a side an army command lays out */
|
|
26
|
+
export const MOST = 5000
|
|
27
|
+
|
|
28
|
+
const units: UnitTypeDef[] = [
|
|
29
|
+
{
|
|
30
|
+
id: PERF.performanceFootman,
|
|
31
|
+
name: 'Footman',
|
|
32
|
+
kind: 'unit',
|
|
33
|
+
radius: 0.45,
|
|
34
|
+
stats: { health: 420, attackDamage: 14, attackSpeed: 1.1, attackRange: 0.5, armor: 12, moveSpeed: 4 },
|
|
35
|
+
sight: 9,
|
|
36
|
+
attacks: [{ damagePoint: 0.45, acquisitionRange: 8 }]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: PERF.performanceArcher,
|
|
40
|
+
name: 'Archer',
|
|
41
|
+
kind: 'unit',
|
|
42
|
+
radius: 0.42,
|
|
43
|
+
stats: { health: 260, attackDamage: 6, attackSpeed: 1.4, attackRange: 14, moveSpeed: 4.2 },
|
|
44
|
+
sight: 15,
|
|
45
|
+
attacks: [{ damagePoint: 0.3, acquisitionRange: 15, projectileSpeed: 22 }]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
// stands and is shot at, healing as fast as it is hurt: a volley that never runs out of targets
|
|
49
|
+
id: PERF.performanceTarget,
|
|
50
|
+
name: 'Target',
|
|
51
|
+
kind: 'unit',
|
|
52
|
+
radius: 0.5,
|
|
53
|
+
stats: { health: 100000, healthRegen: 5000, moveSpeed: 0 },
|
|
54
|
+
sight: 2
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
/** A square block of `count` of a type, 1.5 apart, its middle at `at`: as many across as deep */
|
|
59
|
+
export function block(world: World, type: string, owner: PlayerId, at: Vec2, count: number, gap = 1.5): void {
|
|
60
|
+
const side = Math.ceil(Math.sqrt(count))
|
|
61
|
+
const from = -((side - 1) * gap) / 2
|
|
62
|
+
for (let i = 0; i < count; i++) {
|
|
63
|
+
const row = Math.floor(i / side)
|
|
64
|
+
const column = i % side
|
|
65
|
+
world.spawn(type, { at: { x: at.x + from + column * gap, z: at.z + from + row * gap }, owner })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A block `across` wide and as deep as it takes, its front row at `at.z`, going back by `back` (1 or -1) */
|
|
70
|
+
function ranks(world: World, type: string, owner: PlayerId, at: Vec2, count: number, across: number, back: number, gap = 1.4): void {
|
|
71
|
+
for (let i = 0; i < count; i++) {
|
|
72
|
+
const row = Math.floor(i / across)
|
|
73
|
+
const column = i % across
|
|
74
|
+
const width = Math.min(across, count - row * across)
|
|
75
|
+
world.spawn(type, { at: { x: at.x + (column - (width - 1) / 2) * gap, z: at.z + back * row * gap }, owner })
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Takes every unit of the world away, as if it had never been */
|
|
80
|
+
function clear(world: World, keep: (unit: Entity) => boolean = () => false): void {
|
|
81
|
+
for (const unit of [...world.units.values()]) if (unit.alive && !keep(unit)) world.kill(unit, undefined, false, 'removed')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The middle of the world's field */
|
|
85
|
+
const middle = (world: World): Vec2 => ({ x: world.terrain.width / 2, z: world.terrain.depth / 2 })
|
|
86
|
+
|
|
87
|
+
/** An army of `count` footmen in a square block in the middle of the field, what was there before taken away */
|
|
88
|
+
export const army = defineCommand('performance.army', { count: number }, (world, player, { count }) => {
|
|
89
|
+
clear(world)
|
|
90
|
+
block(world, PERF.performanceFootman, player, middle(world), Math.max(1, Math.min(MOST, Math.round(count))))
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
/** The player's units sent to a point together, in a formation: one order for them all, as a box drag and right-click is */
|
|
94
|
+
export const march = defineCommand(
|
|
95
|
+
'performance.march',
|
|
96
|
+
{ to: oneOf('north', 'south', 'east', 'west', 'middle'), formation: oneOf('square', 'circle', 'rows') },
|
|
97
|
+
(world, player, { to, formation }) => {
|
|
98
|
+
const { x, z } = middle(world)
|
|
99
|
+
const far = world.terrain.width * 0.3
|
|
100
|
+
const point = { north: { x, z: z + far }, south: { x, z: z - far }, east: { x: x + far, z }, west: { x: x - far, z }, middle: { x, z } }[to]
|
|
101
|
+
const entities = [...world.units.values()]
|
|
102
|
+
.filter((unit) => unit.alive && unit.owner === player && unit.def.stats.moveSpeed)
|
|
103
|
+
.map((unit) => unit.id)
|
|
104
|
+
if (entities.length) world.issue({ type: 'order', player, entities, order: { type: 'move', point, formation } })
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
/** Two armies of `count` footmen each, facing each other across the field's middle, ranks of 50 */
|
|
109
|
+
export const battle = defineCommand('performance.battle', { count: number }, (world, _player, { count }) => {
|
|
110
|
+
clear(world)
|
|
111
|
+
const each = Math.max(1, Math.min(MOST / 2, Math.round(count)))
|
|
112
|
+
const { x, z } = middle(world)
|
|
113
|
+
const across = Math.min(50, Math.ceil(Math.sqrt(each * 4)))
|
|
114
|
+
ranks(world, PERF.performanceFootman, PLAYER, { x, z: z - 8 }, each, across, -1)
|
|
115
|
+
ranks(world, PERF.performanceFootman, ENEMY, { x, z: z + 8 }, each, across, 1)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
/** Both armies sent at each other, fighting what they meet on the way (`attackMove`) */
|
|
119
|
+
export const charge = defineCommand('performance.charge', {}, (world) => {
|
|
120
|
+
const { z } = middle(world)
|
|
121
|
+
for (const side of [PLAYER, ENEMY]) {
|
|
122
|
+
const entities = [...world.units.values()].filter((unit) => unit.alive && unit.owner === side).map((unit) => unit.id)
|
|
123
|
+
// one order for each side, the way a player gives it: the side's units go together, each to its place
|
|
124
|
+
if (entities.length)
|
|
125
|
+
world.issue({
|
|
126
|
+
type: 'order',
|
|
127
|
+
player: side,
|
|
128
|
+
entities,
|
|
129
|
+
order: { type: 'attackMove', point: { x: middle(world).x, z: side === PLAYER ? z + 30 : z - 30 }, formation: 'none' }
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
/** `count` archers in ranks along the south, and rows of targets to the north that never fall: arrows always in the air */
|
|
135
|
+
export const volley = defineCommand('performance.volley', { count: number }, (world, player, { count }) => {
|
|
136
|
+
clear(world)
|
|
137
|
+
const { x, z } = middle(world)
|
|
138
|
+
const each = Math.max(1, Math.min(2000, Math.round(count)))
|
|
139
|
+
ranks(world, PERF.performanceArcher, player, { x, z: z - 6 }, each, Math.min(60, Math.ceil(Math.sqrt(each * 3))), -1, 1.2)
|
|
140
|
+
ranks(world, PERF.performanceTarget, ENEMY, { x, z: z + 6 }, Math.ceil(each / 8), 40, 1, 2)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
/** Two crowds of `count` footmen each, at the two ends of the field: the flow system sends them across */
|
|
144
|
+
export const flow = defineCommand('performance.flow', { count: number }, (world, _player, { count }) => {
|
|
145
|
+
clear(world)
|
|
146
|
+
crowds(world, Math.max(1, Math.min(MOST / 2, Math.round(count))))
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
/** The two crowds of the flow example, 30 cells either side of the middle */
|
|
150
|
+
function crowds(world: World, each: number): void {
|
|
151
|
+
const { x, z } = middle(world)
|
|
152
|
+
block(world, PERF.performanceFootman, PLAYER, { x: x - 30, z }, each, 1.3)
|
|
153
|
+
block(world, PERF.performanceFootman, ENEMY, { x: x + 30, z }, each, 1.3)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The flow example's crowds sent across again once each has got to its side: a system, twice a second, on that
|
|
158
|
+
* example's field alone (a system is the whole game's: it looks at the map's name)
|
|
159
|
+
*/
|
|
160
|
+
const flowSystem = {
|
|
161
|
+
name: 'performance.flow',
|
|
162
|
+
every: 15,
|
|
163
|
+
update: (world: World) => {
|
|
164
|
+
if (world.map?.name !== 'Crowd flow') return
|
|
165
|
+
const { x, z } = middle(world)
|
|
166
|
+
for (const side of [PLAYER, ENEMY]) {
|
|
167
|
+
// a side whose units have all stopped goes to the other end of the field, through the other crowd
|
|
168
|
+
const own = [...world.units.values()].filter((unit) => unit.alive && unit.owner === side)
|
|
169
|
+
if (!own.length || own.some((unit) => unit.orders.length)) continue
|
|
170
|
+
const there = own[0]!.pos.x > x ? -1 : 1
|
|
171
|
+
world.issue({
|
|
172
|
+
type: 'order',
|
|
173
|
+
player: side,
|
|
174
|
+
entities: own.map((unit) => unit.id),
|
|
175
|
+
order: { type: 'move', point: { x: x + there * 30, z }, formation: 'square' }
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const category: ExampleCategory = {
|
|
182
|
+
rules: (game: Game<any>) => {
|
|
183
|
+
for (const unit of units) game.unit(unit)
|
|
184
|
+
for (const command of [army, march, battle, charge, volley, flow]) game.command(command)
|
|
185
|
+
game.system(flowSystem)
|
|
186
|
+
},
|
|
187
|
+
examples: [
|
|
188
|
+
{
|
|
189
|
+
id: 'performance-army',
|
|
190
|
+
category: 'performance',
|
|
191
|
+
title: 'Thousands of footmen',
|
|
192
|
+
summary:
|
|
193
|
+
'An army of up to 5,000 footmen, each a unit of the simulation, drawn as baked crowds: one mesh, their clips in a texture. Pick the size, march them about in formation, and watch the frame time.',
|
|
194
|
+
tags: ['crowd', 'baked', 'instancing', 'army', 'fps', 'formation', 'large battles'],
|
|
195
|
+
map: () => exampleField({ name: 'Thousands', size: 128 }),
|
|
196
|
+
start: (world) => block(world, PERF.performanceFootman, PLAYER, middle(world), 400),
|
|
197
|
+
source: ['examples/performance/index.ts', 'examples/performance/looks.ts', 'examples/performance/meter.ts']
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'performance-battle',
|
|
201
|
+
category: 'performance',
|
|
202
|
+
title: 'A huge battle',
|
|
203
|
+
summary:
|
|
204
|
+
'Two armies of a thousand footmen and more crash into each other: every swing, every hit and every death simulated. Choose the size, lay them out, and charge.',
|
|
205
|
+
tags: ['battle', 'combat', 'army', 'fps', 'crowd', 'large battles'],
|
|
206
|
+
map: () => exampleField({ name: 'Huge battle', size: 128, players: 2 }),
|
|
207
|
+
start: (world) => {
|
|
208
|
+
const { x, z } = middle(world)
|
|
209
|
+
ranks(world, PERF.performanceFootman, PLAYER, { x, z: z - 8 }, 150, 30, -1)
|
|
210
|
+
ranks(world, PERF.performanceFootman, ENEMY, { x, z: z + 8 }, 150, 30, 1)
|
|
211
|
+
},
|
|
212
|
+
source: ['examples/performance/index.ts', 'examples/performance/looks.ts']
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'performance-volley',
|
|
216
|
+
category: 'performance',
|
|
217
|
+
title: 'Arrows by the thousand',
|
|
218
|
+
summary:
|
|
219
|
+
'Hundreds of archers shooting at once: every arrow is a projectile of the simulation, flying, landing and drawn. Raise the count and watch the arrows in the air.',
|
|
220
|
+
tags: ['projectiles', 'arrows', 'archers', 'missiles', 'fps'],
|
|
221
|
+
map: () => exampleField({ name: 'Volley', size: 96, players: 2 }),
|
|
222
|
+
start: (world) => {
|
|
223
|
+
const { x, z } = middle(world)
|
|
224
|
+
ranks(world, PERF.performanceArcher, PLAYER, { x, z: z - 6 }, 120, 30, -1, 1.2)
|
|
225
|
+
ranks(world, PERF.performanceTarget, ENEMY, { x, z: z + 6 }, 15, 15, 1, 2)
|
|
226
|
+
},
|
|
227
|
+
source: ['examples/performance/index.ts', 'examples/performance/looks.ts']
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: 'performance-flow',
|
|
231
|
+
category: 'performance',
|
|
232
|
+
title: 'Crowds crossing',
|
|
233
|
+
summary:
|
|
234
|
+
'Two crowds walk through each other to the far side and back, for ever: paths, steering and pushing for every unit, every tick. Make them bigger and watch them find their way.',
|
|
235
|
+
tags: ['pathing', 'steering', 'crowd', 'movement', 'collision', 'fps'],
|
|
236
|
+
map: () => exampleField({ name: 'Crowd flow', size: 112, players: 2 }),
|
|
237
|
+
start: (world) => {
|
|
238
|
+
crowds(world, 100)
|
|
239
|
+
},
|
|
240
|
+
source: ['examples/performance/index.ts', 'examples/performance/looks.ts']
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: 'performance-forest',
|
|
244
|
+
category: 'performance',
|
|
245
|
+
title: 'A forest of thousands',
|
|
246
|
+
summary:
|
|
247
|
+
'Up to 40,000 trees, each a model of the shared art, drawn as copies by chunk of the map and lighter far off. Grow the forest and fly over it.',
|
|
248
|
+
tags: ['trees', 'forest', 'doodads', 'scenery', 'instancing', 'batching', 'fps'],
|
|
249
|
+
map: () => exampleField({ name: 'Forest', size: 192 }),
|
|
250
|
+
source: ['examples/performance/looks.ts']
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: 'performance-effects',
|
|
254
|
+
category: 'performance',
|
|
255
|
+
title: 'Effects everywhere',
|
|
256
|
+
summary:
|
|
257
|
+
'Up to three hundred fires, blasts and sparkles playing at once over a field: particles, lights and flashes, kept to the effects budget. Raise the count and pick the kind.',
|
|
258
|
+
tags: ['fx', 'particles', 'fire', 'explosions', 'budget', 'fps'],
|
|
259
|
+
map: () => exampleField({ name: 'Effects', size: 96 }),
|
|
260
|
+
source: ['examples/performance/looks.ts']
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
}
|