@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,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A shot drawn your own way, on the client's side: the thrower's `shot.draw`, called as each glaive leaves with what
|
|
3
|
+
* the shot is (`ShotContext`: the shot, its shooter, where it left from, the render kit). It makes the glaive (three
|
|
4
|
+
* blades of meshes and a ribbon behind them, `TrailMesh`), is told where it is every frame (`move`, with the way it
|
|
5
|
+
* goes) and spins it, and as it lands (`dispose`) throws sparks there and lets the ribbon fade before it goes
|
|
6
|
+
*/
|
|
7
|
+
import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial.js'
|
|
8
|
+
import { Color3 } from '@babylonjs/core/Maths/math.color.js'
|
|
9
|
+
import { CreateBox } from '@babylonjs/core/Meshes/Builders/boxBuilder.js'
|
|
10
|
+
import { CreateCylinder } from '@babylonjs/core/Meshes/Builders/cylinderBuilder.js'
|
|
11
|
+
import { TrailMesh } from '@babylonjs/core/Meshes/trailMesh.js'
|
|
12
|
+
import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js'
|
|
13
|
+
import type { ShotContext, ShotDrawing, UnitLook } from '@v12-engine/client'
|
|
14
|
+
import { type ExampleLook, select } from '../../shell/look'
|
|
15
|
+
import { character, person } from '../common/looks'
|
|
16
|
+
import { aim } from '../fx/stage'
|
|
17
|
+
import { GLAIVE_AT } from './glaive'
|
|
18
|
+
|
|
19
|
+
/** The blades' glow, as the card sets it */
|
|
20
|
+
const glow = new Color3(0.3, 1, 0.75)
|
|
21
|
+
|
|
22
|
+
/** One glaive on its way */
|
|
23
|
+
function drawGlaive({ kit, from }: ShotContext): ShotDrawing {
|
|
24
|
+
const { scene } = kit
|
|
25
|
+
const root = new TransformNode('customFx.glaive', scene)
|
|
26
|
+
root.position.set(from.x, from.y, from.z)
|
|
27
|
+
const spinner = new TransformNode('customFx.glaiveSpin', scene)
|
|
28
|
+
spinner.parent = root
|
|
29
|
+
const steel = new StandardMaterial('customFx.glaiveSteel', scene)
|
|
30
|
+
steel.diffuseColor = new Color3(0.2, 0.22, 0.25)
|
|
31
|
+
steel.emissiveColor = glow.clone()
|
|
32
|
+
steel.specularColor = new Color3(1, 1, 1)
|
|
33
|
+
// three curved-looking blades round a hub, lying flat
|
|
34
|
+
const hub = CreateCylinder('customFx.glaiveHub', { height: 0.08, diameter: 0.25, tessellation: 12 }, scene)
|
|
35
|
+
hub.parent = spinner
|
|
36
|
+
hub.material = steel
|
|
37
|
+
for (let i = 0; i < 3; i++) {
|
|
38
|
+
const blade = CreateBox('customFx.glaiveBlade', { width: 0.7, height: 0.04, depth: 0.12 }, scene)
|
|
39
|
+
blade.parent = spinner
|
|
40
|
+
blade.material = steel
|
|
41
|
+
blade.rotation.y = (i / 3) * Math.PI * 2
|
|
42
|
+
blade.position.set(Math.cos(-blade.rotation.y) * 0.32, 0, Math.sin(-blade.rotation.y) * 0.32)
|
|
43
|
+
blade.rotation.y += 0.5
|
|
44
|
+
}
|
|
45
|
+
// the ribbon: a point at the tip of one blade, sweeping as it spins
|
|
46
|
+
const tip = new TransformNode('customFx.glaiveTip', scene)
|
|
47
|
+
tip.parent = spinner
|
|
48
|
+
tip.position.x = 0.62
|
|
49
|
+
const trail = new TrailMesh('customFx.glaiveTrail', tip, scene, 0.12, 40, true)
|
|
50
|
+
const ribbon = new StandardMaterial('customFx.glaiveRibbon', scene)
|
|
51
|
+
ribbon.emissiveColor = glow.clone()
|
|
52
|
+
ribbon.disableLighting = true
|
|
53
|
+
ribbon.alpha = 0.55
|
|
54
|
+
trail.material = ribbon
|
|
55
|
+
for (const mesh of [hub, trail, ...spinner.getChildMeshes()]) mesh.isPickable = false
|
|
56
|
+
let spin = 0
|
|
57
|
+
return {
|
|
58
|
+
move(at, heading) {
|
|
59
|
+
root.position.set(at.x, at.y, at.z)
|
|
60
|
+
root.rotation.set(0, heading.yaw, 0)
|
|
61
|
+
// tipped a little, spinning fast
|
|
62
|
+
spin += 0.45
|
|
63
|
+
spinner.rotation.set(0.25, spin, 0)
|
|
64
|
+
},
|
|
65
|
+
dispose() {
|
|
66
|
+
kit.fx.play('spark-burst', {
|
|
67
|
+
at: { x: root.position.x, y: root.position.y, z: root.position.z },
|
|
68
|
+
params: { count: 20, color: glow.toHexString(), size: 0.7 }
|
|
69
|
+
})
|
|
70
|
+
root.dispose(false, true)
|
|
71
|
+
steel.dispose()
|
|
72
|
+
// the ribbon fades where it was, then goes
|
|
73
|
+
let left = 0.35
|
|
74
|
+
const fade = scene.onBeforeRenderObservable.add(() => {
|
|
75
|
+
left -= scene.getEngine().getDeltaTime() / 1000
|
|
76
|
+
ribbon.alpha = Math.max(left / 0.35, 0) * 0.55
|
|
77
|
+
if (left > 0) return
|
|
78
|
+
scene.onBeforeRenderObservable.remove(fade)
|
|
79
|
+
trail.dispose()
|
|
80
|
+
ribbon.dispose()
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The thrower: the rogue, a glaive drawn the game's own way */
|
|
87
|
+
export const glaiverLook: UnitLook = {
|
|
88
|
+
height: 1.8,
|
|
89
|
+
model: character('rogue_hooded', 0.72, { animations: { attack: 'Ranged_1H_Shoot' } }),
|
|
90
|
+
shot: { draw: drawGlaive },
|
|
91
|
+
shapes: person(1.8, [0.2, 0.5, 0.4])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const COLORS: Record<string, [number, number, number]> = { Jade: [0.3, 1, 0.75], Ember: [1, 0.5, 0.15], Frost: [0.5, 0.8, 1], Blood: [1, 0.15, 0.2] }
|
|
95
|
+
|
|
96
|
+
export const glaiveLook: ExampleLook = {
|
|
97
|
+
controls: [
|
|
98
|
+
select(
|
|
99
|
+
'Glow',
|
|
100
|
+
Object.keys(COLORS),
|
|
101
|
+
'Jade',
|
|
102
|
+
(_client, name) => glow.set(...(COLORS[name] ?? COLORS.Jade!)),
|
|
103
|
+
'The colour the blades and the ribbon behind them glow, on every glaive in the air'
|
|
104
|
+
)
|
|
105
|
+
],
|
|
106
|
+
ready: (client) => {
|
|
107
|
+
glow.set(...COLORS.Jade!)
|
|
108
|
+
aim(client, GLAIVE_AT, 15, 0.6)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A shot drawn your own way, on the rules' side: two glaive throwers and the dummies they throw at. To the rules a
|
|
3
|
+
* glaive is a shot like any; how it flies on the screen is its look's `draw` (`./glaive.look.ts`)
|
|
4
|
+
*/
|
|
5
|
+
import type { Game } from '@v12-engine/engine'
|
|
6
|
+
import { ENEMY, PLAYER, UNIT } from '../common/units'
|
|
7
|
+
import { type ExampleDef, exampleField } from '../define'
|
|
8
|
+
|
|
9
|
+
export const GLAIVER = 'customFxGlaiver'
|
|
10
|
+
export const GLAIVE_AT = { x: 24, z: 24 }
|
|
11
|
+
|
|
12
|
+
export const glaive: ExampleDef = {
|
|
13
|
+
id: 'custom-fx-glaive',
|
|
14
|
+
category: 'custom-fx',
|
|
15
|
+
title: 'A shot of your own',
|
|
16
|
+
summary:
|
|
17
|
+
"A shot drawn in code (`ShotDef.draw`): a spinning three-bladed glaive made of meshes, a glowing ribbon behind it (Babylon's `TrailMesh`), sparks where it lands. The throwers keep throwing; turn the blades' colour.",
|
|
18
|
+
tags: ['ShotDef.draw', 'projectile', 'missile', 'TrailMesh', 'ribbon', 'meshes', 'custom shot'],
|
|
19
|
+
environment: 'dusk',
|
|
20
|
+
map: () => exampleField({ name: 'Range', players: 2 }),
|
|
21
|
+
start: (world) => {
|
|
22
|
+
world.spawn(GLAIVER, { at: { x: GLAIVE_AT.x - 5, z: GLAIVE_AT.z - 2 }, owner: PLAYER })
|
|
23
|
+
world.spawn(GLAIVER, { at: { x: GLAIVE_AT.x - 5, z: GLAIVE_AT.z + 2 }, owner: PLAYER })
|
|
24
|
+
for (const dz of [-3, 0, 3]) world.spawn(UNIT.dummy, { at: { x: GLAIVE_AT.x + 5, z: GLAIVE_AT.z + dz }, owner: ENEMY })
|
|
25
|
+
},
|
|
26
|
+
source: ['examples/custom-fx/glaive.ts', 'examples/custom-fx/glaive.look.ts']
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Registers the thrower: slow glaives, from 11 cells away */
|
|
30
|
+
export function glaiveRules(game: Game<any>): void {
|
|
31
|
+
game.unit({
|
|
32
|
+
id: GLAIVER,
|
|
33
|
+
name: 'Glaive thrower',
|
|
34
|
+
kind: 'unit',
|
|
35
|
+
radius: 0.42,
|
|
36
|
+
stats: { health: 400, attackDamage: 25, attackSpeed: 0.8, attackRange: 11, moveSpeed: 4 },
|
|
37
|
+
sight: 13,
|
|
38
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 12, projectileSpeed: 9 }]
|
|
39
|
+
})
|
|
40
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Particles on the graphics card, on the client's side: `customFx.fountain` is made in code (`FxDef.make`), with
|
|
3
|
+
* Babylon's `GPUParticleSystem` where the browser can move particles on the graphics card, its `ParticleSystem`
|
|
4
|
+
* otherwise. `make` is handed the scene, a node that moves with the effect (`anchor`) and the knobs; it gives back what
|
|
5
|
+
* runs every frame, what changes with the knobs, and what takes it all away. A light and the glow of the scene make
|
|
6
|
+
* it shine on what is round it
|
|
7
|
+
*/
|
|
8
|
+
import { DynamicTexture } from '@babylonjs/core/Materials/Textures/dynamicTexture.js'
|
|
9
|
+
import { NoiseProceduralTexture } from '@babylonjs/core/Materials/Textures/Procedurals/noiseProceduralTexture.js'
|
|
10
|
+
import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js'
|
|
11
|
+
import { Vector3 } from '@babylonjs/core/Maths/math.vector.js'
|
|
12
|
+
import { PointLight } from '@babylonjs/core/Lights/pointLight.js'
|
|
13
|
+
import { GPUParticleSystem } from '@babylonjs/core/Particles/gpuParticleSystem.js'
|
|
14
|
+
import { ParticleSystem } from '@babylonjs/core/Particles/particleSystem.js'
|
|
15
|
+
import type { Scene } from '@babylonjs/core/scene.js'
|
|
16
|
+
import type { FxDef, FxHandle, GameClientApi } from '@v12-engine/client'
|
|
17
|
+
import { type ExampleLook, readout, slider, toggle } from '../../shell/look'
|
|
18
|
+
import { aim } from '../fx/stage'
|
|
19
|
+
import { FOUNTAIN_AT } from './gpu'
|
|
20
|
+
|
|
21
|
+
/** A soft round spark, drawn once in a canvas: white, so the system's colours colour it */
|
|
22
|
+
function sparkTexture(scene: Scene): DynamicTexture {
|
|
23
|
+
const texture = new DynamicTexture('customFx.spark', 64, scene, false)
|
|
24
|
+
const context = texture.getContext()
|
|
25
|
+
const glow = context.createRadialGradient(32, 32, 0, 32, 32, 32)
|
|
26
|
+
glow.addColorStop(0, 'rgba(255,255,255,1)')
|
|
27
|
+
glow.addColorStop(0.25, 'rgba(255,255,255,0.8)')
|
|
28
|
+
glow.addColorStop(1, 'rgba(255,255,255,0)')
|
|
29
|
+
context.fillStyle = glow
|
|
30
|
+
context.fillRect(0, 0, 64, 64)
|
|
31
|
+
texture.update()
|
|
32
|
+
texture.hasAlpha = true
|
|
33
|
+
return texture
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The fountain: sparks thrown up from a ring, turning from white to cyan to violet as they fall, thrown about by noise */
|
|
37
|
+
const fountain: FxDef = {
|
|
38
|
+
params: { count: 100_000, gpu: true },
|
|
39
|
+
make: ({ scene, anchor, params }) => {
|
|
40
|
+
const count = Number(params.count)
|
|
41
|
+
const onCard = params.gpu !== false && GPUParticleSystem.IsSupported
|
|
42
|
+
const system = onCard
|
|
43
|
+
? new GPUParticleSystem('customFx.fountain', { capacity: count }, scene)
|
|
44
|
+
: new ParticleSystem('customFx.fountain', count, scene)
|
|
45
|
+
system.particleTexture = sparkTexture(scene)
|
|
46
|
+
system.emitter = anchor as never
|
|
47
|
+
// a ring at the foot, each spark sent up and a little out
|
|
48
|
+
system.createDirectedCylinderEmitter(0.6, 0.1, 0.2, new Vector3(-0.12, 1, -0.12), new Vector3(0.12, 1, 0.12))
|
|
49
|
+
system.emitRate = count / 3.2
|
|
50
|
+
system.minLifeTime = 2.4
|
|
51
|
+
system.maxLifeTime = 3.2
|
|
52
|
+
system.minEmitPower = 9
|
|
53
|
+
system.maxEmitPower = 13
|
|
54
|
+
system.gravity = new Vector3(0, -7, 0)
|
|
55
|
+
system.minSize = 0.04
|
|
56
|
+
system.maxSize = 0.11
|
|
57
|
+
system.addColorGradient(0, new Color4(1, 1, 1, 0.45))
|
|
58
|
+
system.addColorGradient(0.25, new Color4(0.3, 0.85, 1, 0.4))
|
|
59
|
+
system.addColorGradient(0.7, new Color4(0.55, 0.25, 1, 0.3))
|
|
60
|
+
system.addColorGradient(1, new Color4(0.3, 0.1, 0.8, 0))
|
|
61
|
+
system.blendMode = ParticleSystem.BLENDMODE_ADD
|
|
62
|
+
// noise: each spark pushed about by where it is in a field that moves, so the jet frays as it falls
|
|
63
|
+
const noise = new NoiseProceduralTexture('customFx.noise', 256, scene)
|
|
64
|
+
noise.animationSpeedFactor = 4
|
|
65
|
+
noise.persistence = 1.6
|
|
66
|
+
noise.brightness = 0.5
|
|
67
|
+
system.noiseTexture = noise
|
|
68
|
+
system.noiseStrength = new Vector3(4, 1, 4)
|
|
69
|
+
system.start()
|
|
70
|
+
// its light, breathing
|
|
71
|
+
const light = new PointLight('customFx.fountainLight', new Vector3(0, 3, 0), scene)
|
|
72
|
+
light.parent = anchor
|
|
73
|
+
light.diffuse = new Color3(0.45, 0.6, 1)
|
|
74
|
+
light.range = 16
|
|
75
|
+
let time = 0
|
|
76
|
+
let stoppedAt: number | undefined
|
|
77
|
+
return {
|
|
78
|
+
update: (dt) => {
|
|
79
|
+
time += dt
|
|
80
|
+
light.intensity = (2.2 + Math.sin(time * 3) * 0.4) * (stoppedAt === undefined ? 1 : Math.max(1 - (time - stoppedAt) / 3, 0))
|
|
81
|
+
// over once the last sparks let go have fallen
|
|
82
|
+
return stoppedAt === undefined || time - stoppedAt < 3.3
|
|
83
|
+
},
|
|
84
|
+
stop: () => {
|
|
85
|
+
system.stop()
|
|
86
|
+
stoppedAt = time
|
|
87
|
+
},
|
|
88
|
+
dispose: () => {
|
|
89
|
+
system.dispose()
|
|
90
|
+
noise.dispose()
|
|
91
|
+
light.dispose()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The fountain of this example, taken into the game's view */
|
|
98
|
+
export const gpuFx: Record<string, FxDef> = { 'customFx.fountain': fountain }
|
|
99
|
+
|
|
100
|
+
const chosen = { count: 100_000, gpu: true }
|
|
101
|
+
let playing: FxHandle | undefined
|
|
102
|
+
|
|
103
|
+
/** Plays it again with the card's count and where it is moved */
|
|
104
|
+
function replay(client: GameClientApi): void {
|
|
105
|
+
playing?.stop({ mode: 'at-once' })
|
|
106
|
+
playing = client.fx.play('customFx.fountain', { at: FOUNTAIN_AT, params: { ...chosen } })
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const gpuLook: ExampleLook = {
|
|
110
|
+
controls: [
|
|
111
|
+
slider(
|
|
112
|
+
'Sparks',
|
|
113
|
+
{ min: 5000, max: 200_000, step: 5000, value: chosen.count },
|
|
114
|
+
(client, count) => {
|
|
115
|
+
chosen.count = count
|
|
116
|
+
replay(client)
|
|
117
|
+
},
|
|
118
|
+
'How many sparks the fountain keeps in the air: watch the frames a second as it climbs'
|
|
119
|
+
),
|
|
120
|
+
toggle(
|
|
121
|
+
'On the graphics card',
|
|
122
|
+
true,
|
|
123
|
+
(client, on) => {
|
|
124
|
+
chosen.gpu = on
|
|
125
|
+
replay(client)
|
|
126
|
+
},
|
|
127
|
+
'Off: the same fountain moved by the CPU frame by frame, which keeps far fewer sparks a second'
|
|
128
|
+
),
|
|
129
|
+
readout(
|
|
130
|
+
'Frames a second',
|
|
131
|
+
(client) => `${Math.round(client.scene.getEngine().getFps())}${GPUParticleSystem.IsSupported ? '' : ' (no GPU particles here)'}`,
|
|
132
|
+
'How fast the page draws now: what the sparks cost'
|
|
133
|
+
)
|
|
134
|
+
],
|
|
135
|
+
ready: (client) => {
|
|
136
|
+
chosen.count = 100_000
|
|
137
|
+
chosen.gpu = true
|
|
138
|
+
aim(client, { x: FOUNTAIN_AT.x, z: FOUNTAIN_AT.z }, 34, 0.3)
|
|
139
|
+
replay(client)
|
|
140
|
+
return () => {
|
|
141
|
+
playing?.stop({ mode: 'at-once' })
|
|
142
|
+
playing = undefined
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Particles on the graphics card, on the rules' side: nothing but a mage to stand by the fountain. The fountain is the
|
|
3
|
+
* view's effect, made of Babylon's own particle system (`./gpu.look.ts`)
|
|
4
|
+
*/
|
|
5
|
+
import { PLAYER, UNIT } from '../common/units'
|
|
6
|
+
import type { ExampleDef } from '../define'
|
|
7
|
+
|
|
8
|
+
export const FOUNTAIN_AT = { x: 24, z: 24 }
|
|
9
|
+
|
|
10
|
+
export const gpu: ExampleDef = {
|
|
11
|
+
id: 'custom-fx-gpu-particles',
|
|
12
|
+
category: 'custom-fx',
|
|
13
|
+
title: "Babylon's particles, a hundred thousand",
|
|
14
|
+
summary:
|
|
15
|
+
'An effect that makes its own Babylon `GPUParticleSystem` (`make`): a fountain of up to 200 000 sparks moved on the graphics card, thrown about by noise, falling back. Turn the count up; turn the graphics card off to see what the CPU manages.',
|
|
16
|
+
tags: ['GPUParticleSystem', 'ParticleSystem', 'Babylon', 'make', 'gpu', 'noise', 'particles', 'performance'],
|
|
17
|
+
environment: 'night',
|
|
18
|
+
start: (world) => {
|
|
19
|
+
world.spawn(UNIT.mage, { at: { x: FOUNTAIN_AT.x - 4, z: FOUNTAIN_AT.z - 2 }, owner: PLAYER })
|
|
20
|
+
},
|
|
21
|
+
source: ['examples/custom-fx/gpu.ts', 'examples/custom-fx/gpu.look.ts']
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The examples of effects of your own, on the rules' side (`ExampleCategory`): their list entries, maps and starts,
|
|
3
|
+
* and what they add to the game. Most effects of one's own are the screen's alone, so most examples here put a unit or
|
|
4
|
+
* two down and leave the rest to their look (`<name>.look.ts` beside each, gathered in `./looks.ts`)
|
|
5
|
+
*/
|
|
6
|
+
import type { Game } from '@v12-engine/engine'
|
|
7
|
+
import type { ExampleCategory } from '../define'
|
|
8
|
+
import { glaive, glaiveRules } from './glaive'
|
|
9
|
+
import { gpu } from './gpu'
|
|
10
|
+
import { lights } from './lights'
|
|
11
|
+
import { meteor } from './meteor'
|
|
12
|
+
import { parts } from './parts'
|
|
13
|
+
import { portal, portalRules } from './portal'
|
|
14
|
+
import { renderer } from './renderer'
|
|
15
|
+
import { ribbons } from './ribbons'
|
|
16
|
+
import { shield, shieldRules } from './shield'
|
|
17
|
+
|
|
18
|
+
export const category: ExampleCategory = {
|
|
19
|
+
rules: (game: Game<any>) => {
|
|
20
|
+
shieldRules(game)
|
|
21
|
+
portalRules(game)
|
|
22
|
+
glaiveRules(game)
|
|
23
|
+
},
|
|
24
|
+
examples: [parts, ribbons, meteor, gpu, shield, portal, glaive, renderer, lights]
|
|
25
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lights that move, on the client's side: lights of the world added in code (`client.lights.add(LightDef)`), on
|
|
3
|
+
* always (`when: 'always'`; a lamp's are at night alone), each with a handle to move it and change it
|
|
4
|
+
* (`handle.set({ x, y, z, color })`) and take it away. A `halo` draws the light itself as a glowing ball; a `spot`
|
|
5
|
+
* shines one way. The quality's `lights` says how many are drawn at once
|
|
6
|
+
*/
|
|
7
|
+
import type { GameClientApi, LightHandle, Rgb } from '@v12-engine/client'
|
|
8
|
+
import { type ExampleLook, select, slider, toggle } from '../../shell/look'
|
|
9
|
+
import { UNIT } from '../common/units'
|
|
10
|
+
import { aim, everyFrame, idsOf, where } from '../fx/stage'
|
|
11
|
+
import { LIGHTS_AT } from './lights'
|
|
12
|
+
|
|
13
|
+
const PALETTES: Record<string, Rgb[]> = {
|
|
14
|
+
Spirits: [
|
|
15
|
+
[0.4, 0.8, 1],
|
|
16
|
+
[0.7, 0.5, 1],
|
|
17
|
+
[0.4, 1, 0.75]
|
|
18
|
+
],
|
|
19
|
+
Embers: [
|
|
20
|
+
[1, 0.55, 0.15],
|
|
21
|
+
[1, 0.8, 0.3],
|
|
22
|
+
[1, 0.3, 0.1]
|
|
23
|
+
],
|
|
24
|
+
Rainbow: [
|
|
25
|
+
[1, 0.25, 0.25],
|
|
26
|
+
[1, 0.8, 0.2],
|
|
27
|
+
[0.3, 1, 0.4],
|
|
28
|
+
[0.3, 0.6, 1],
|
|
29
|
+
[0.8, 0.3, 1]
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const chosen = { count: 6, palette: 'Spirits', flicker: false }
|
|
34
|
+
let wisps: LightHandle[] = []
|
|
35
|
+
let lantern: LightHandle | undefined
|
|
36
|
+
let beam: LightHandle | undefined
|
|
37
|
+
|
|
38
|
+
/** The wisps made again, as many as the card says, in its colours */
|
|
39
|
+
function makeWisps(client: GameClientApi): void {
|
|
40
|
+
for (const wisp of wisps) wisp.dispose()
|
|
41
|
+
const colors = PALETTES[chosen.palette]!
|
|
42
|
+
wisps = Array.from({ length: chosen.count }, (_, i) =>
|
|
43
|
+
client.lights.add({
|
|
44
|
+
name: `wisp ${i + 1}`,
|
|
45
|
+
...LIGHTS_AT,
|
|
46
|
+
y: 1.5,
|
|
47
|
+
color: colors[i % colors.length],
|
|
48
|
+
intensity: 1.6,
|
|
49
|
+
range: 5,
|
|
50
|
+
halo: 0.35,
|
|
51
|
+
when: 'always',
|
|
52
|
+
...(chosen.flicker && { flicker: { amount: 0.4, speed: 2 } })
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const lightsLook: ExampleLook = {
|
|
58
|
+
controls: [
|
|
59
|
+
slider(
|
|
60
|
+
'Wisps',
|
|
61
|
+
{ min: 0, max: 16, step: 1, value: chosen.count },
|
|
62
|
+
(client, count) => {
|
|
63
|
+
chosen.count = count
|
|
64
|
+
makeWisps(client)
|
|
65
|
+
},
|
|
66
|
+
'How many wisps circle the mage, each a light of its own on the ground under it'
|
|
67
|
+
),
|
|
68
|
+
select(
|
|
69
|
+
'Colours',
|
|
70
|
+
Object.keys(PALETTES),
|
|
71
|
+
chosen.palette,
|
|
72
|
+
(client, name) => {
|
|
73
|
+
chosen.palette = name
|
|
74
|
+
makeWisps(client)
|
|
75
|
+
},
|
|
76
|
+
'The colours the wisps take, in turn'
|
|
77
|
+
),
|
|
78
|
+
toggle(
|
|
79
|
+
'Flicker',
|
|
80
|
+
false,
|
|
81
|
+
(client, on) => {
|
|
82
|
+
chosen.flicker = on
|
|
83
|
+
makeWisps(client)
|
|
84
|
+
},
|
|
85
|
+
'The wisps waver in brightness, as a flame does'
|
|
86
|
+
),
|
|
87
|
+
toggle('Lighthouse beam', true, (_client, on) => beam?.set({ on }), 'The spot light sweeping round the field, on or off')
|
|
88
|
+
],
|
|
89
|
+
ready: (client) => {
|
|
90
|
+
Object.assign(chosen, { count: 6, palette: 'Spirits', flicker: false })
|
|
91
|
+
aim(client, LIGHTS_AT, 22, 0.75)
|
|
92
|
+
makeWisps(client)
|
|
93
|
+
lantern = client.lights.add({
|
|
94
|
+
name: 'lantern',
|
|
95
|
+
...LIGHTS_AT,
|
|
96
|
+
y: 1.6,
|
|
97
|
+
color: [1, 0.75, 0.4],
|
|
98
|
+
intensity: 1.8,
|
|
99
|
+
range: 7,
|
|
100
|
+
halo: 0.18,
|
|
101
|
+
when: 'always',
|
|
102
|
+
flicker: 0.15
|
|
103
|
+
})
|
|
104
|
+
beam = client.lights.add({
|
|
105
|
+
name: 'beam',
|
|
106
|
+
x: LIGHTS_AT.x - 9,
|
|
107
|
+
y: 6,
|
|
108
|
+
z: LIGHTS_AT.z - 7,
|
|
109
|
+
kind: 'spot',
|
|
110
|
+
angle: 0.35,
|
|
111
|
+
focus: 4,
|
|
112
|
+
color: [1, 0.95, 0.8],
|
|
113
|
+
intensity: 3,
|
|
114
|
+
range: 30,
|
|
115
|
+
when: 'always'
|
|
116
|
+
})
|
|
117
|
+
let time = 0
|
|
118
|
+
const stop = everyFrame(client, (dt) => {
|
|
119
|
+
time += dt
|
|
120
|
+
// the wisps on circles of their own about the mage, rising and falling
|
|
121
|
+
const center = where(client, idsOf(client, UNIT.mage)[0] ?? -1) ?? LIGHTS_AT
|
|
122
|
+
wisps.forEach((wisp, i) => {
|
|
123
|
+
const turn = time * (0.5 + (i % 3) * 0.15) + (i / wisps.length) * Math.PI * 2
|
|
124
|
+
const radius = 2.2 + (i % 2) * 1.4
|
|
125
|
+
wisp.set({ x: center.x + Math.cos(turn) * radius, z: center.z + Math.sin(turn) * radius, y: 1.3 + Math.sin(time * 2 + i) * 0.5 })
|
|
126
|
+
})
|
|
127
|
+
// the lantern in the footman's hand, wherever he walks
|
|
128
|
+
const [footman] = idsOf(client, UNIT.footman)
|
|
129
|
+
const hand = footman !== undefined ? client.renderer.kit.point(footman, 'handslot.l') : undefined
|
|
130
|
+
const feet = footman !== undefined ? where(client, footman) : undefined
|
|
131
|
+
if (hand) lantern?.set({ x: hand.x, y: hand.y, z: hand.z })
|
|
132
|
+
else if (feet) lantern?.set({ x: feet.x, y: 1.4, z: feet.z })
|
|
133
|
+
// the beam sweeping round over the field
|
|
134
|
+
const sweep = time * 0.6
|
|
135
|
+
beam?.set({ direction: [Math.cos(sweep), -0.45, Math.sin(sweep)] })
|
|
136
|
+
})
|
|
137
|
+
return () => {
|
|
138
|
+
stop()
|
|
139
|
+
for (const light of [...wisps, lantern, beam]) light?.dispose()
|
|
140
|
+
wisps = []
|
|
141
|
+
lantern = beam = undefined
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lights that move, on the rules' side: a night, a mage in the middle, a footman walking his round. The lights are
|
|
3
|
+
* the screen's (`./lights.look.ts`)
|
|
4
|
+
*/
|
|
5
|
+
import { PLAYER, UNIT } from '../common/units'
|
|
6
|
+
import type { ExampleDef } from '../define'
|
|
7
|
+
|
|
8
|
+
export const LIGHTS_AT = { x: 24, z: 24 }
|
|
9
|
+
|
|
10
|
+
export const lights: ExampleDef = {
|
|
11
|
+
id: 'custom-fx-lights',
|
|
12
|
+
category: 'custom-fx',
|
|
13
|
+
title: 'Lights that move',
|
|
14
|
+
summary:
|
|
15
|
+
"Lights added in code (`client.lights.add`), moved every frame: wisps circling the mage, each a glowing ball (`halo`) lighting the ground under it; a lantern carried by a walking footman; a beam sweeping round like a lighthouse's. Change how many wisps, their colours, the flicker.",
|
|
16
|
+
tags: ['lights', 'dynamic lights', 'point light', 'spot light', 'halo', 'night', 'WorldLights', 'LightDef'],
|
|
17
|
+
environment: 'night',
|
|
18
|
+
start: (world) => {
|
|
19
|
+
world.spawn(UNIT.mage, { at: LIGHTS_AT, owner: PLAYER })
|
|
20
|
+
world
|
|
21
|
+
.spawn(UNIT.footman, { at: { x: LIGHTS_AT.x - 8, z: LIGHTS_AT.z + 5 }, owner: PLAYER })
|
|
22
|
+
.order('patrol', { x: LIGHTS_AT.x + 8, z: LIGHTS_AT.z + 5 })
|
|
23
|
+
},
|
|
24
|
+
source: ['examples/custom-fx/lights.ts', 'examples/custom-fx/lights.look.ts']
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The examples of effects of your own, on the client's side (`CategoryLooks`): each example's look and controls by its
|
|
3
|
+
* id, and what they add to the game's look: their effects (`fx.defs`, every name starting `customFx.`) and the looks of
|
|
4
|
+
* the units they add
|
|
5
|
+
*/
|
|
6
|
+
import type { CategoryLooks } from '../../shell/look'
|
|
7
|
+
import { GLAIVER } from './glaive'
|
|
8
|
+
import { glaiveLook, glaiverLook } from './glaive.look'
|
|
9
|
+
import { gpuFx, gpuLook } from './gpu.look'
|
|
10
|
+
import { lightsLook } from './lights.look'
|
|
11
|
+
import { meteorFx, meteorLook } from './meteor.look'
|
|
12
|
+
import { partsFx, partsLook } from './parts.look'
|
|
13
|
+
import { portalFx, portalLook } from './portal.look'
|
|
14
|
+
import { rendererLook } from './renderer.look'
|
|
15
|
+
import { ribbonsFx, ribbonsLook } from './ribbons.look'
|
|
16
|
+
import { WARDEN } from './shield'
|
|
17
|
+
import { shieldLook, wardenLook } from './shield.look'
|
|
18
|
+
|
|
19
|
+
export const category: CategoryLooks = {
|
|
20
|
+
view: (view) => ({
|
|
21
|
+
...view,
|
|
22
|
+
units: { ...view.units, looks: { ...view.units.looks, [WARDEN]: wardenLook, [GLAIVER]: glaiverLook } },
|
|
23
|
+
fx: { ...view.fx, defs: { ...view.fx?.defs, ...partsFx, ...ribbonsFx, ...meteorFx, ...gpuFx, ...portalFx } }
|
|
24
|
+
}),
|
|
25
|
+
looks: {
|
|
26
|
+
'custom-fx-parts': partsLook,
|
|
27
|
+
'custom-fx-ribbons': ribbonsLook,
|
|
28
|
+
'custom-fx-meteor': meteorLook,
|
|
29
|
+
'custom-fx-gpu-particles': gpuLook,
|
|
30
|
+
'custom-fx-shield': shieldLook,
|
|
31
|
+
'custom-fx-portal': portalLook,
|
|
32
|
+
'custom-fx-glaive': glaiveLook,
|
|
33
|
+
'custom-fx-renderer': rendererLook,
|
|
34
|
+
'custom-fx-lights': lightsLook
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A meteor, on the client's side: `customFx.meteor`, its fall and its landing in one timeline. The fall is parts on
|
|
3
|
+
* a `path` (a function of the part's age: where it is, from where the effect plays), so the flames, the glowing rock,
|
|
4
|
+
* the ribbon behind it and its light come down together. At `FALL` seconds the landing: ready-made effects as parts
|
|
5
|
+
* (`explosion`, `impact-ring`, `burning-ground`), rocks flung that each trail smoke all their life (`sub` on `birth`),
|
|
6
|
+
* a shake and a crack laid on the ground
|
|
7
|
+
*/
|
|
8
|
+
import type { FxDef, FxPath, GameClientApi } from '@v12-engine/client'
|
|
9
|
+
import { button, type ExampleLook, slider } from '../../shell/look'
|
|
10
|
+
import { aim } from '../fx/stage'
|
|
11
|
+
import { METEOR_AT } from './meteor'
|
|
12
|
+
|
|
13
|
+
/** Seconds it falls */
|
|
14
|
+
const FALL = 1.1
|
|
15
|
+
|
|
16
|
+
/** Down from high up and off to one side, faster as it comes: where it is, from where it lands */
|
|
17
|
+
const fall: FxPath = (age, _life, p) => {
|
|
18
|
+
const t = Math.min(age / FALL, 1)
|
|
19
|
+
const left = 1 - t * t
|
|
20
|
+
const size = Number(p.size)
|
|
21
|
+
return [-9 * left * size, 0.4 + 26 * left * size, -6 * left * size]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const meteor: FxDef = {
|
|
25
|
+
params: { size: 1 },
|
|
26
|
+
knobs: { size: { min: 0.4, max: 2.5, step: 0.1, about: 'How big the rock, and all it does' } },
|
|
27
|
+
parts: [
|
|
28
|
+
// the fall: a white-hot rock, flames streaming off it, a ribbon of fire, its light
|
|
29
|
+
{
|
|
30
|
+
for: FALL,
|
|
31
|
+
path: fall,
|
|
32
|
+
particles: { rate: 40, lifetime: 0.12, size: (p) => 1.6 * Number(p.size), color: ['#ffffff', '#ffd27a'], texture: 'flare', local: true }
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
for: FALL,
|
|
36
|
+
path: fall,
|
|
37
|
+
particles: {
|
|
38
|
+
rate: 260,
|
|
39
|
+
shape: { type: 'sphere', radius: 0.4 },
|
|
40
|
+
speed: [0.5, 2],
|
|
41
|
+
lifetime: [0.3, 0.7],
|
|
42
|
+
size: (p) => [
|
|
43
|
+
[0, 1.2 * Number(p.size)],
|
|
44
|
+
[1, 0.2]
|
|
45
|
+
],
|
|
46
|
+
color: [
|
|
47
|
+
[0, '#fff0b0'],
|
|
48
|
+
[0.4, '#ff7a1a'],
|
|
49
|
+
[1, '#3a1a0a']
|
|
50
|
+
],
|
|
51
|
+
texture: 'puff'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{ for: FALL, path: fall, trail: { color: '#ff8a2a', width: (p) => 0.9 * Number(p.size), time: 0.4, soft: true, blend: 'add', taper: true } },
|
|
55
|
+
{ for: FALL, path: fall, light: { color: '#ffa040', intensity: 4, range: 14 } },
|
|
56
|
+
// the landing
|
|
57
|
+
{ at: FALL, fx: 'explosion', params: (p) => ({ size: 1.6 * Number(p.size), shake: 0 }) },
|
|
58
|
+
{ at: FALL, fx: 'impact-ring', params: (p) => ({ size: 1.5 * Number(p.size), shake: 0 }) },
|
|
59
|
+
{ at: FALL, shake: (p) => ({ strength: 0.9 * Number(p.size), time: 0.6 }) },
|
|
60
|
+
{ at: FALL, decal: { texture: 'crack', size: (p) => 6 * Number(p.size), time: 40, color: '#1a120c' } },
|
|
61
|
+
// rocks flung out on high arcs, each trailing smoke for as long as it flies
|
|
62
|
+
{
|
|
63
|
+
at: FALL,
|
|
64
|
+
particles: {
|
|
65
|
+
bursts: [{ at: 0, count: 26 }],
|
|
66
|
+
shape: 'hemisphere',
|
|
67
|
+
spread: 0.7,
|
|
68
|
+
speed: (p) => [7 * Number(p.size), 13 * Number(p.size)],
|
|
69
|
+
gravity: 16,
|
|
70
|
+
lifetime: [0.8, 1.4],
|
|
71
|
+
size: [0.35, 0.25],
|
|
72
|
+
color: '#ff9a4a',
|
|
73
|
+
texture: 'dot',
|
|
74
|
+
sub: [
|
|
75
|
+
{
|
|
76
|
+
on: 'birth',
|
|
77
|
+
particles: {
|
|
78
|
+
rate: 30,
|
|
79
|
+
lifetime: [0.4, 0.8],
|
|
80
|
+
size: [0.3, 1],
|
|
81
|
+
color: ['#5a4a40', '#2a2420'],
|
|
82
|
+
alpha: [0.7, 0],
|
|
83
|
+
texture: 'puff',
|
|
84
|
+
blend: 'alpha'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{ at: FALL + 0.1, for: 6, fx: 'burning-ground', params: (p) => ({ radius: 1.6 * Number(p.size), scorch: false }) }
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The meteor of this example, taken into the game's view */
|
|
95
|
+
export const meteorFx: Record<string, FxDef> = { 'customFx.meteor': meteor }
|
|
96
|
+
|
|
97
|
+
let size = 1
|
|
98
|
+
let shower: number[] = []
|
|
99
|
+
|
|
100
|
+
/** Calls one down a little way from the middle, or (`exact`) on it */
|
|
101
|
+
const call = (client: GameClientApi, exact = false) =>
|
|
102
|
+
client.fx.play('customFx.meteor', {
|
|
103
|
+
at: exact ? METEOR_AT : { x: METEOR_AT.x + (Math.random() - 0.5) * 9, z: METEOR_AT.z + (Math.random() - 0.5) * 7 },
|
|
104
|
+
params: { size: exact ? size : size * (0.5 + Math.random() * 0.6) }
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
export const meteorLook: ExampleLook = {
|
|
108
|
+
controls: [
|
|
109
|
+
button('Call a meteor', (client) => call(client, true), 'One meteor falls on the middle of the field, at the size below'),
|
|
110
|
+
button(
|
|
111
|
+
'A shower',
|
|
112
|
+
(client) => {
|
|
113
|
+
shower = Array.from({ length: 7 }, (_, i) => window.setTimeout(() => call(client), i * 450 + Math.random() * 200))
|
|
114
|
+
},
|
|
115
|
+
'Seven meteors, one after another, at spots round the middle'
|
|
116
|
+
),
|
|
117
|
+
slider(
|
|
118
|
+
'Size',
|
|
119
|
+
{ min: 0.4, max: 2.5, step: 0.1, value: 1 },
|
|
120
|
+
(_client, value) => (size = value),
|
|
121
|
+
'How big the next meteor is, its blast and crack too (the knob `size`): call one to see it'
|
|
122
|
+
)
|
|
123
|
+
],
|
|
124
|
+
ready: (client) => {
|
|
125
|
+
size = 1
|
|
126
|
+
aim(client, METEOR_AT, 26, 0.55)
|
|
127
|
+
const first = window.setTimeout(() => call(client, true), 500)
|
|
128
|
+
return () => {
|
|
129
|
+
window.clearTimeout(first)
|
|
130
|
+
for (const one of shower) window.clearTimeout(one)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|