@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,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summons and ground objects: units an ability brings into the world. Summons that fight beside the caster, a ward,
|
|
3
|
+
* the dead raised and brought back, illusions; and with the ground objects pack (`groundObjectsPack`), a trap, walls,
|
|
4
|
+
* a wall that eats missiles, a lantern an ally uses, kegs that set each other off, and a shadow and a ball the caster
|
|
5
|
+
* casts from
|
|
6
|
+
*/
|
|
7
|
+
import { type AbilityDef, defineCommand, type UnitTypeDef } from '@v12-engine/engine'
|
|
8
|
+
import { exampleField } from '../define'
|
|
9
|
+
import { ENEMY, PLAYER } from '../common/units'
|
|
10
|
+
import type { Family } from './family'
|
|
11
|
+
import { caster, put, sparring } from './kit'
|
|
12
|
+
|
|
13
|
+
/** Something put on the ground: small, one hit, out of the way */
|
|
14
|
+
const placed = (id: string, name: string, own: Partial<UnitTypeDef>): UnitTypeDef => ({
|
|
15
|
+
id,
|
|
16
|
+
name,
|
|
17
|
+
kind: 'unit',
|
|
18
|
+
radius: 0.3,
|
|
19
|
+
stats: { health: 1 },
|
|
20
|
+
sight: 2,
|
|
21
|
+
...own
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const units: UnitTypeDef[] = [
|
|
25
|
+
sparring('abilitiesElemental', 'Water Elemental', {
|
|
26
|
+
radius: 0.5,
|
|
27
|
+
stats: { health: 500, attackDamage: 20, attackSpeed: 1, attackRange: 4, moveSpeed: 3.5 },
|
|
28
|
+
attacks: [{ damagePoint: 0.3, acquisitionRange: 7, projectileSpeed: 12 }]
|
|
29
|
+
}),
|
|
30
|
+
sparring('abilitiesVoidling', 'Voidling', {
|
|
31
|
+
radius: 0.3,
|
|
32
|
+
stats: { health: 60, attackDamage: 9, attackSpeed: 1.4, attackRange: 0.5, moveSpeed: 5 },
|
|
33
|
+
attacks: [{ damagePoint: 0.25, acquisitionRange: 7 }]
|
|
34
|
+
}),
|
|
35
|
+
sparring('abilitiesSkeleton', 'Skeleton', {
|
|
36
|
+
stats: { health: 200, attackDamage: 12, attackSpeed: 1, attackRange: 0.5, moveSpeed: 3.6 },
|
|
37
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 7 }]
|
|
38
|
+
}),
|
|
39
|
+
// sees round it, unseen by enemies after a second, gone after two hits whatever they deal
|
|
40
|
+
placed('abilitiesWard', 'Sentry Ward', { sight: 10, lifetime: 60, invisible: { after: 1 }, uses: 2 }),
|
|
41
|
+
// goes off as an enemy comes within 0.9, armed a second after it is put down
|
|
42
|
+
placed('abilitiesShroom', 'Noxious Trap', { invisible: { after: 1 }, trigger: { ability: 'abilitiesShroomBurst', radius: 0.9, arm: 1 } }),
|
|
43
|
+
placed('abilitiesIceWall', 'Ice Wall', { radius: 0.55, invulnerable: true, stats: { health: 100 } }),
|
|
44
|
+
// eats every missile that meets it, the caster's own too
|
|
45
|
+
placed('abilitiesWindWall', 'Wind Wall', { radius: 0.6, invulnerable: true, lifetime: 6, blocksMissiles: 'all' }),
|
|
46
|
+
// allies use it: walk up and right-click
|
|
47
|
+
placed('abilitiesLantern', 'Lantern', { lifetime: 20, use: { ability: 'abilitiesLanternLight', range: 1.5, times: 1 } }),
|
|
48
|
+
// where one dies its ability goes off, and those of its kind within 4 die too
|
|
49
|
+
placed('abilitiesKeg', 'Powder Keg', { radius: 0.4, stats: { health: 50 }, burst: { ability: 'abilitiesKegBlast', chain: 4 } }),
|
|
50
|
+
placed('abilitiesShadow', 'Living Shadow', { radius: 0.4, invulnerable: true }),
|
|
51
|
+
placed('abilitiesBall', 'The Ball', { radius: 0.3, invulnerable: true, sight: 4 })
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const abilities: AbilityDef[] = [
|
|
55
|
+
{
|
|
56
|
+
// at a spot near the caster (summoned where the caster stands, it would stand inside it); follows its master,
|
|
57
|
+
// fights who fights it, dies with it
|
|
58
|
+
id: 'abilitiesSummonElemental',
|
|
59
|
+
name: 'Water Elemental',
|
|
60
|
+
description: 'An elemental beside the caster for 30 seconds',
|
|
61
|
+
hotkey: 'Q',
|
|
62
|
+
target: 'point',
|
|
63
|
+
range: 4,
|
|
64
|
+
cooldown: 1,
|
|
65
|
+
effects: [{ type: 'summon', unit: 'abilitiesElemental', lifetime: 30 }]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
// four that go for the nearest enemy at once
|
|
69
|
+
id: 'abilitiesVoidSwarm',
|
|
70
|
+
name: 'Void Swarm',
|
|
71
|
+
description: 'Four voidlings for 8 seconds, going for the nearest enemy',
|
|
72
|
+
hotkey: 'W',
|
|
73
|
+
target: 'none',
|
|
74
|
+
cooldown: 1,
|
|
75
|
+
effects: [{ type: 'summon', unit: 'abilitiesVoidling', count: 4, lifetime: 8, spread: 1.5, aggro: 'nearest' }]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'abilitiesPlaceWard',
|
|
79
|
+
name: 'Sentry Ward',
|
|
80
|
+
description: 'A ward at the spot: it sees, and is unseen after a second',
|
|
81
|
+
hotkey: 'E',
|
|
82
|
+
target: 'point',
|
|
83
|
+
range: 10,
|
|
84
|
+
cooldown: 1,
|
|
85
|
+
effects: [{ type: 'spawn', unit: 'abilitiesWard' }]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
// corpses round the spot stand up again, the caster's for 30 seconds
|
|
89
|
+
id: 'abilitiesRaiseDead',
|
|
90
|
+
name: 'Raise Dead',
|
|
91
|
+
description: 'The fallen enemies on the left rise as skeletons of yours',
|
|
92
|
+
hotkey: 'R',
|
|
93
|
+
target: 'point',
|
|
94
|
+
range: 12,
|
|
95
|
+
cooldown: 1,
|
|
96
|
+
effects: [{ type: 'raiseDead', unit: 'abilitiesSkeleton', count: 3, duration: 30, radius: 4 }]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
// the side's own dead back as they were, for good
|
|
100
|
+
id: 'abilitiesResurrection',
|
|
101
|
+
name: 'Resurrection',
|
|
102
|
+
description: 'Your fallen footmen on the right come back as they were',
|
|
103
|
+
hotkey: 'T',
|
|
104
|
+
target: 'none',
|
|
105
|
+
cooldown: 1,
|
|
106
|
+
effects: [{ type: 'revive', count: 6, radius: 12 }]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
// copies of the caster that deal a third and take double
|
|
110
|
+
id: 'abilitiesMirrorImage',
|
|
111
|
+
name: 'Mirror Image',
|
|
112
|
+
description: 'Two copies of the caster for 15 seconds, weaker than it',
|
|
113
|
+
hotkey: 'D',
|
|
114
|
+
target: 'none',
|
|
115
|
+
cooldown: 1,
|
|
116
|
+
effects: [{ type: 'illusion', count: 2, duration: 15, dealt: 0.33, taken: 2, on: 'caster' }]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'abilitiesNoxiousTrap',
|
|
120
|
+
name: 'Noxious Trap',
|
|
121
|
+
description: 'A hidden mushroom under the nearest enemy: it goes off a second later',
|
|
122
|
+
hotkey: 'F',
|
|
123
|
+
target: 'point',
|
|
124
|
+
range: 12,
|
|
125
|
+
cooldown: 1,
|
|
126
|
+
effects: [{ type: 'spawn', unit: 'abilitiesShroom' }]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
// several along a line laid across the aim: a wall in front
|
|
130
|
+
id: 'abilitiesCrystallize',
|
|
131
|
+
name: 'Crystallize',
|
|
132
|
+
description: 'A wall of ice across the way for 5 seconds',
|
|
133
|
+
hotkey: 'G',
|
|
134
|
+
target: 'point',
|
|
135
|
+
range: 10,
|
|
136
|
+
cooldown: 1,
|
|
137
|
+
effects: [{ type: 'spawn', unit: 'abilitiesIceWall', duration: 5, line: { count: 5, length: 4, across: true } }]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'abilitiesCastWindWall',
|
|
141
|
+
name: 'Wind Wall',
|
|
142
|
+
description: 'A wall of wind in front that eats missiles: fire a bolt through it',
|
|
143
|
+
hotkey: 'Z',
|
|
144
|
+
target: 'point',
|
|
145
|
+
range: 3,
|
|
146
|
+
cooldown: 1,
|
|
147
|
+
effects: [{ type: 'spawn', unit: 'abilitiesWindWall', line: { count: 4, length: 3, across: true } }]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'abilitiesProbe',
|
|
151
|
+
name: 'Bolt',
|
|
152
|
+
description: 'A bolt at the nearest enemy, to see a wind wall eat it',
|
|
153
|
+
hotkey: 'X',
|
|
154
|
+
target: 'direction',
|
|
155
|
+
range: 12,
|
|
156
|
+
cooldown: 0.3,
|
|
157
|
+
effects: [{ type: 'missile', speed: 14, distance: 12, radius: 0.5, pierce: false, effects: [{ type: 'damage', amount: 60 }] }]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: 'abilitiesPlaceLantern',
|
|
161
|
+
name: 'Lantern',
|
|
162
|
+
description: 'A lantern by your wounded footman: it walks up and uses it',
|
|
163
|
+
hotkey: 'C',
|
|
164
|
+
target: 'point',
|
|
165
|
+
range: 12,
|
|
166
|
+
cooldown: 1,
|
|
167
|
+
effects: [{ type: 'spawn', unit: 'abilitiesLantern' }]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'abilitiesPlaceKegs',
|
|
171
|
+
name: 'Powder Kegs',
|
|
172
|
+
description: 'Three kegs by the enemies: a fuse sets one off, and it sets off the rest',
|
|
173
|
+
hotkey: 'V',
|
|
174
|
+
target: 'point',
|
|
175
|
+
range: 12,
|
|
176
|
+
cooldown: 1,
|
|
177
|
+
effects: [{ type: 'spawn', unit: 'abilitiesKeg', line: { count: 3, length: 6, across: true } }]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
// a keg with a short life: dying, it bursts, and the others within 4 burst after it
|
|
181
|
+
id: 'abilitiesFuse',
|
|
182
|
+
name: 'Light a fuse',
|
|
183
|
+
description: 'A keg that bursts in a second, setting off every keg within 4',
|
|
184
|
+
hotkey: 'B',
|
|
185
|
+
target: 'point',
|
|
186
|
+
range: 12,
|
|
187
|
+
cooldown: 1,
|
|
188
|
+
effects: [{ type: 'spawn', unit: 'abilitiesKeg', duration: 1 }]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'abilitiesPlaceShadow',
|
|
192
|
+
name: 'Living Shadow',
|
|
193
|
+
description: 'A shadow to the side for 8 seconds: the shuriken flies from it too',
|
|
194
|
+
hotkey: 'N',
|
|
195
|
+
target: 'point',
|
|
196
|
+
range: 10,
|
|
197
|
+
cooldown: 1,
|
|
198
|
+
effects: [{ type: 'spawn', unit: 'abilitiesShadow', duration: 8 }]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
// goes off from the caster and from every shadow of its own (`origin`); the caster's damage still
|
|
202
|
+
id: 'abilitiesShuriken',
|
|
203
|
+
name: 'Razor Shuriken',
|
|
204
|
+
description: 'A shuriken from the caster and from each shadow, at the enemies',
|
|
205
|
+
hotkey: 'Y',
|
|
206
|
+
target: 'point',
|
|
207
|
+
range: 10,
|
|
208
|
+
cooldown: 0.5,
|
|
209
|
+
effects: [
|
|
210
|
+
{
|
|
211
|
+
type: 'missile',
|
|
212
|
+
speed: 17,
|
|
213
|
+
distance: 10,
|
|
214
|
+
radius: 0.6,
|
|
215
|
+
origin: { proxy: 'abilitiesShadow', pick: 'all', caster: true },
|
|
216
|
+
effects: [{ type: 'damage', amount: 70 }]
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
// thrown from wherever the ball is, then the ball sent there
|
|
222
|
+
id: 'abilitiesCommandAttack',
|
|
223
|
+
name: 'Command: Attack',
|
|
224
|
+
description: 'A shot from the ball to the spot, and the ball goes there',
|
|
225
|
+
hotkey: 'U',
|
|
226
|
+
target: 'point',
|
|
227
|
+
range: 12,
|
|
228
|
+
cooldown: 0.5,
|
|
229
|
+
effects: [
|
|
230
|
+
{
|
|
231
|
+
type: 'missile',
|
|
232
|
+
speed: 12,
|
|
233
|
+
toPoint: true,
|
|
234
|
+
distance: 12,
|
|
235
|
+
radius: 0.8,
|
|
236
|
+
origin: { proxy: 'abilitiesBall', caster: 'otherwise' },
|
|
237
|
+
effects: [{ type: 'damage', amount: 60 }]
|
|
238
|
+
},
|
|
239
|
+
{ type: 'sendProxy', speed: 12, origin: { proxy: 'abilitiesBall' } }
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
/** What the placed things set off: not on the card */
|
|
245
|
+
const more: AbilityDef[] = [
|
|
246
|
+
{ id: 'abilitiesShroomBurst', name: 'Noxious Burst', target: 'none', effects: [{ type: 'damage', amount: 120, radius: 2 }] },
|
|
247
|
+
{ id: 'abilitiesLanternLight', name: 'Lantern Light', target: 'none', effects: [{ type: 'heal', amount: 400, radius: 2.5 }] },
|
|
248
|
+
{ id: 'abilitiesKegBlast', name: 'Keg Blast', target: 'none', effects: [{ type: 'damage', amount: 90, radius: 2.5 }] }
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The fallen, laid once the game runs (the card sends it as it opens): enemies on the left to raise, footmen of yours
|
|
253
|
+
* on the right to bring back
|
|
254
|
+
*/
|
|
255
|
+
export const layFallen = defineCommand('abilities.fallen', {}, (world) => {
|
|
256
|
+
for (const z of [21, 22.5, 24]) world.kill(put(world, 'abilitiesTarget', ENEMY, { x: 16, z }), undefined)
|
|
257
|
+
for (const z of [20, 21.5]) world.kill(put(world, 'abilitiesSoldier', PLAYER, { x: 31, z }), undefined)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
/** Your wounded footman walks to the nearest lantern and uses it */
|
|
261
|
+
export const useLantern = defineCommand('abilities.useLantern', {}, (world, player) => {
|
|
262
|
+
const ally = world.named('ally')
|
|
263
|
+
if (!ally || ally.owner !== player) return
|
|
264
|
+
const lantern = [...world.units.values()].find((each) => each.alive && each.def.id === 'abilitiesLantern')
|
|
265
|
+
if (lantern) ally.order('useObject', lantern)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
export const summons: Family = {
|
|
269
|
+
abilities,
|
|
270
|
+
more,
|
|
271
|
+
units: [
|
|
272
|
+
caster(
|
|
273
|
+
'abilitiesSummoner',
|
|
274
|
+
'Summoner',
|
|
275
|
+
abilities.map((each) => each.id)
|
|
276
|
+
),
|
|
277
|
+
...units
|
|
278
|
+
],
|
|
279
|
+
commands: [useLantern, layFallen],
|
|
280
|
+
example: {
|
|
281
|
+
id: 'abilities-summons',
|
|
282
|
+
category: 'abilities',
|
|
283
|
+
title: 'Summons and ground objects',
|
|
284
|
+
summary:
|
|
285
|
+
'Summons, a ward, the dead raised and brought back, illusions; a trap, an ice wall, a wall that eats missiles, a lantern, kegs that chain, and a shadow and a ball the caster casts from. Press a button to see each.',
|
|
286
|
+
tags: ['summon', 'spawn', 'ward', 'raise dead', 'revive', 'illusion', 'trap', 'wall', 'wind wall', 'lantern', 'barrel', 'keg', 'proxy', 'shadow'],
|
|
287
|
+
source: ['examples/abilities/summons.ts', 'examples/abilities/kit.ts'],
|
|
288
|
+
map: () => exampleField({ name: 'Summons', players: 2 }),
|
|
289
|
+
start: (world) => {
|
|
290
|
+
put(world, 'abilitiesSummoner', PLAYER, { x: 24, z: 17 }, 'caster')
|
|
291
|
+
put(world, 'abilitiesWounded', PLAYER, { x: 21, z: 17 }, 'ally').health = 300
|
|
292
|
+
put(world, 'abilitiesBall', PLAYER, { x: 26, z: 18 })
|
|
293
|
+
for (const [i, x] of [21, 24, 27].entries()) put(world, 'abilitiesTarget', ENEMY, { x, z: 25 }, `enemy${i + 1}`, Math.PI)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** How the summons example looks, and its card: a button for each summon and thing put down (`./summons.ts`) */
|
|
2
|
+
import { button } from '../../shell/look'
|
|
3
|
+
import { character, hold, ONE_HANDED, person, sharedLooks } from '../common/looks'
|
|
4
|
+
import type { FamilyLooks } from './looks'
|
|
5
|
+
import { castButton, look, readyOn } from './lookKit'
|
|
6
|
+
import { reset } from './kit'
|
|
7
|
+
import { layFallen, summons, useLantern } from './summons'
|
|
8
|
+
|
|
9
|
+
type Color = [number, number, number]
|
|
10
|
+
|
|
11
|
+
/** Where each is aimed, where the nearest enemy is not it */
|
|
12
|
+
const aims: Record<string, { point?: { x: number; z: number } }> = {
|
|
13
|
+
abilitiesSummonElemental: { point: { x: 26, z: 18.5 } },
|
|
14
|
+
abilitiesPlaceWard: { point: { x: 27.5, z: 21.5 } },
|
|
15
|
+
abilitiesRaiseDead: { point: { x: 16, z: 22.5 } },
|
|
16
|
+
abilitiesCrystallize: { point: { x: 24, z: 22 } },
|
|
17
|
+
abilitiesCastWindWall: { point: { x: 24, z: 20 } },
|
|
18
|
+
abilitiesPlaceLantern: { point: { x: 22.5, z: 19.5 } },
|
|
19
|
+
abilitiesPlaceKegs: { point: { x: 24, z: 23 } },
|
|
20
|
+
abilitiesFuse: { point: { x: 22.5, z: 22 } },
|
|
21
|
+
abilitiesPlaceShadow: { point: { x: 29, z: 19 } }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A thing on the ground, as parts: a post with something on it. What shows while its model loads */
|
|
25
|
+
const thing = (color: Color, height: number, top: 'sphere' | 'box' = 'sphere') => ({
|
|
26
|
+
height,
|
|
27
|
+
shapes: [
|
|
28
|
+
{
|
|
29
|
+
shape: 'cylinder' as const,
|
|
30
|
+
size: [0.12, height, 0.12] as [number, number, number],
|
|
31
|
+
offset: [0, height / 2, 0] as [number, number, number],
|
|
32
|
+
color: [0.35, 0.25, 0.15] as Color
|
|
33
|
+
},
|
|
34
|
+
{ shape: top, size: [0.35, 0.35, 0.35] as [number, number, number], offset: [0, height, 0] as [number, number, number], color }
|
|
35
|
+
]
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export const summonsLooks: FamilyLooks = {
|
|
39
|
+
units: {
|
|
40
|
+
abilitiesSummoner: sharedLooks.mage,
|
|
41
|
+
// the shared art's mannequin in the water's blue, a skeleton minion small and in the void's violet: both with the clips
|
|
42
|
+
abilitiesElemental: {
|
|
43
|
+
height: 1.7,
|
|
44
|
+
model: character('mannequin_medium', 0.8, { tint: [0.35, 0.65, 1] }),
|
|
45
|
+
shapes: [{ shape: 'sphere', size: [1, 1.6, 1], offset: [0, 0.8, 0], color: [0.3, 0.6, 1] }]
|
|
46
|
+
},
|
|
47
|
+
abilitiesVoidling: {
|
|
48
|
+
height: 0.8,
|
|
49
|
+
model: character('skeleton_minion', 0.38, { tint: [0.6, 0.3, 0.85] }),
|
|
50
|
+
shapes: [{ shape: 'sphere', size: [0.6, 0.6, 0.6], offset: [0, 0.35, 0], color: [0.5, 0.15, 0.7] }]
|
|
51
|
+
},
|
|
52
|
+
abilitiesSkeleton: {
|
|
53
|
+
height: 1.7,
|
|
54
|
+
model: character('skeleton_minion', 0.7, { hold: [hold('sword_1handed')] }),
|
|
55
|
+
attacks: [ONE_HANDED],
|
|
56
|
+
shapes: person(1.7)
|
|
57
|
+
},
|
|
58
|
+
// a standing torch: what sees round it
|
|
59
|
+
abilitiesWard: { ...thing([1, 0.85, 0.3], 1), model: { src: 'dungeon-kit/torch.glb', scale: 0.8, raise: 0.55 } },
|
|
60
|
+
abilitiesShroom: {
|
|
61
|
+
height: 0.5,
|
|
62
|
+
model: { src: 'forest/mushroom_common.glb', scale: 2 },
|
|
63
|
+
shapes: [{ shape: 'sphere', size: [0.5, 0.3, 0.5], offset: [0, 0.25, 0], color: [0.7, 0.2, 0.6] }]
|
|
64
|
+
},
|
|
65
|
+
// ice and wind have no model in the shared art: a block of ice and a pale sheet, as parts
|
|
66
|
+
abilitiesIceWall: { height: 1.8, shapes: [{ shape: 'box', size: [1, 1.8, 0.6], offset: [0, 0.9, 0], color: [0.7, 0.9, 1] }] },
|
|
67
|
+
abilitiesWindWall: { height: 2.2, shapes: [{ shape: 'box', size: [1.1, 2.2, 0.2], offset: [0, 1.1, 0], color: [0.85, 0.95, 1] }] },
|
|
68
|
+
abilitiesLantern: { ...thing([0.4, 1, 0.6], 1.2, 'box'), model: { src: 'props/candle_stick_stand.glb', scale: 1 } },
|
|
69
|
+
abilitiesKeg: {
|
|
70
|
+
height: 0.8,
|
|
71
|
+
model: { src: 'props/barrel.glb', scale: 0.9 },
|
|
72
|
+
shapes: [{ shape: 'cylinder', size: [0.7, 0.8, 0.7], offset: [0, 0.4, 0], color: [0.5, 0.3, 0.15] }]
|
|
73
|
+
},
|
|
74
|
+
abilitiesShadow: {
|
|
75
|
+
height: 1.8,
|
|
76
|
+
model: character('rogue_hooded', 0.72, { hold: [hold('dagger'), hold('dagger', 'l')] }),
|
|
77
|
+
shapes: person(1.8, [0.1, 0.05, 0.15])
|
|
78
|
+
},
|
|
79
|
+
// a ball that floats and is sent about: the shared art has no orb to fit, so it is a sphere
|
|
80
|
+
abilitiesBall: { height: 0.8, shapes: [{ shape: 'sphere', size: [0.5, 0.5, 0.5], offset: [0, 0.8, 0], color: [0.75, 0.7, 0.5] }] }
|
|
81
|
+
},
|
|
82
|
+
abilities: {
|
|
83
|
+
abilitiesSummonElemental: look([0.3, 0.6, 1], 'burst'),
|
|
84
|
+
abilitiesVoidSwarm: look([0.5, 0.15, 0.7], 'burst'),
|
|
85
|
+
abilitiesRaiseDead: look([0.5, 1, 0.5], 'sonar'),
|
|
86
|
+
abilitiesResurrection: look([1, 0.95, 0.6], 'sonar'),
|
|
87
|
+
abilitiesMirrorImage: look([0.6, 0.6, 1], 'burst'),
|
|
88
|
+
abilitiesShroomBurst: look([0.7, 0.2, 0.6], 'burst'),
|
|
89
|
+
abilitiesProbe: look([1, 0.6, 0.3], 'missile', { options: { shape: 'ball' } }),
|
|
90
|
+
abilitiesLanternLight: look([0.4, 1, 0.6], 'burst'),
|
|
91
|
+
abilitiesKegBlast: look([1, 0.55, 0.15], 'smash'),
|
|
92
|
+
abilitiesShuriken: look([0.85, 0.85, 0.9], 'missile', { options: { shape: 'arrow' } }),
|
|
93
|
+
abilitiesCommandAttack: look([0.75, 0.7, 0.5], 'missile', { options: { shape: 'ball' } })
|
|
94
|
+
},
|
|
95
|
+
examples: {
|
|
96
|
+
'abilities-summons': {
|
|
97
|
+
ready: (client) => {
|
|
98
|
+
readyOn(client, 'abilitiesSummoner', { minX: 14, minZ: 14, maxX: 34, maxZ: 28 })
|
|
99
|
+
client.session.send(layFallen.make({}))
|
|
100
|
+
},
|
|
101
|
+
controls: [
|
|
102
|
+
...summons.abilities.map((ability) => castButton(ability, aims[ability.id])),
|
|
103
|
+
button('Use the lantern', (client) => client.session.send(useLantern.make({})), 'Your wounded footman walks to the lantern and uses it'),
|
|
104
|
+
button(
|
|
105
|
+
'Start again',
|
|
106
|
+
(client) => {
|
|
107
|
+
client.session.send(reset.make({ example: 'abilities-summons' }))
|
|
108
|
+
client.session.send(layFallen.make({}))
|
|
109
|
+
},
|
|
110
|
+
'Takes everything away and lays the example out again, the fallen on both sides too'
|
|
111
|
+
)
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triggers and passives: always on, each doing something when something happens. A blademaster striking a dummy has a
|
|
3
|
+
* chance to crit, hits harder every fourth blow, an orb of fire (and one of frost the fire's slot leaves out), grows
|
|
4
|
+
* faster with every spell, heals as its spells hurt and gathers souls from what it kills; its Siphoning Strike
|
|
5
|
+
* empowers the next blow and Quick Strike makes it come at once. A warden beside it hurts whoever hurts it, shields
|
|
6
|
+
* itself low, and cheats death
|
|
7
|
+
*/
|
|
8
|
+
import { type AbilityDef, type BuffDef, defineCommand } from '@v12-engine/engine'
|
|
9
|
+
import { exampleField } from '../define'
|
|
10
|
+
import { ENEMY, PLAYER, UNIT } from '../common/units'
|
|
11
|
+
import type { Family } from './family'
|
|
12
|
+
import { caster, put, sparring } from './kit'
|
|
13
|
+
|
|
14
|
+
const buffs: BuffDef[] = [
|
|
15
|
+
{ id: 'abilitiesSiphoning', name: 'Siphoning Strike', description: 'The next blow deals 120 more', duration: 8 },
|
|
16
|
+
{ id: 'abilitiesSouls', name: 'Souls', description: '+4 attack damage a soul', maxStacks: 100, perStack: true, stats: { attackDamage: 4 } },
|
|
17
|
+
{
|
|
18
|
+
id: 'abilitiesFlurry',
|
|
19
|
+
name: 'Flurry',
|
|
20
|
+
description: '+15% attack speed a stack',
|
|
21
|
+
duration: 6,
|
|
22
|
+
maxStacks: 5,
|
|
23
|
+
perStack: true,
|
|
24
|
+
percent: { attackSpeed: 0.15 }
|
|
25
|
+
},
|
|
26
|
+
{ id: 'abilitiesChilled', name: 'Chilled', harmful: true, duration: 2, percent: { moveSpeed: -0.3 } },
|
|
27
|
+
{ id: 'abilitiesWardShield', name: 'Mana Barrier', description: 'A shield as health runs low', duration: 6, shield: 400 },
|
|
28
|
+
{ id: 'abilitiesReborn', name: 'Reborn', description: 'Back from the dead, untouchable a moment', duration: 2, invulnerable: true }
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
/** The blademaster's passives: each a trigger in an ability aimed at nothing (`passive`) */
|
|
32
|
+
const passives: AbilityDef[] = [
|
|
33
|
+
{
|
|
34
|
+
id: 'abilitiesCrit',
|
|
35
|
+
name: 'Critical Strike',
|
|
36
|
+
description: 'A blow that lands has a 25% chance to deal 80 more',
|
|
37
|
+
target: 'passive',
|
|
38
|
+
effects: [{ type: 'onHit', chance: 0.25, effects: [{ type: 'damage', amount: 80, damageType: 'physical' }] }]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'abilitiesWhisper',
|
|
42
|
+
name: 'Every Fourth',
|
|
43
|
+
description: 'Every fourth blow that lands deals 150 more',
|
|
44
|
+
target: 'passive',
|
|
45
|
+
effects: [{ type: 'onHit', every: 4, effects: [{ type: 'damage', amount: 150, damageType: 'physical' }] }]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
// two orbs in one slot: only the one of the highest priority goes off, as Warcraft III's orbs
|
|
49
|
+
id: 'abilitiesFrostOrb',
|
|
50
|
+
name: 'Orb of Frost',
|
|
51
|
+
description: 'Blows chill (but the Orb of Fire shares its slot, and wins)',
|
|
52
|
+
target: 'passive',
|
|
53
|
+
effects: [{ type: 'onHit', slot: 'orb', effects: [{ type: 'buff', buff: 'abilitiesChilled' }] }]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'abilitiesFireOrb',
|
|
57
|
+
name: 'Orb of Fire',
|
|
58
|
+
description: 'Blows burn round the target for 25',
|
|
59
|
+
target: 'passive',
|
|
60
|
+
effects: [{ type: 'onHit', slot: 'orb', priority: 1, effects: [{ type: 'damage', amount: 25, radius: 1.5 }] }]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'abilitiesFlurryPassive',
|
|
64
|
+
name: 'Flurry',
|
|
65
|
+
description: 'Every spell cast: a stack of 15% attack speed',
|
|
66
|
+
target: 'passive',
|
|
67
|
+
effects: [{ type: 'onCast', effects: [{ type: 'buff', buff: 'abilitiesFlurry', stack: true, on: 'caster' }] }]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'abilitiesSpellVamp',
|
|
71
|
+
name: 'Spell Vamp',
|
|
72
|
+
description: 'Each enemy its spells hurt heals it for a third of the damage',
|
|
73
|
+
target: 'passive',
|
|
74
|
+
effects: [{ type: 'onAbilityHit', effects: [{ type: 'heal', amount: 0, byDamage: 0.33, on: 'caster' }] }]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'abilitiesSoulHarvest',
|
|
78
|
+
name: 'Soul Harvest',
|
|
79
|
+
description: 'Each enemy it kills: a soul, 4 more attack damage for good',
|
|
80
|
+
target: 'passive',
|
|
81
|
+
effects: [{ type: 'onKill', effects: [{ type: 'buff', buff: 'abilitiesSouls', stack: true, on: 'caster' }] }]
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
const abilities: AbilityDef[] = [
|
|
86
|
+
{
|
|
87
|
+
// cast: a buff on the caster; its own passive part spends it on the next blow (`whileBuff`, `consumes`)
|
|
88
|
+
id: 'abilitiesSiphoningStrike',
|
|
89
|
+
name: 'Siphoning Strike',
|
|
90
|
+
description: 'The next blow deals 120 more, and a kill with it gives two souls',
|
|
91
|
+
hotkey: 'Q',
|
|
92
|
+
target: 'none',
|
|
93
|
+
cooldown: 1,
|
|
94
|
+
effects: [{ type: 'buff', buff: 'abilitiesSiphoning', on: 'caster' }],
|
|
95
|
+
passive: [
|
|
96
|
+
{ type: 'onHit', whileBuff: 'abilitiesSiphoning', consumes: true, effects: [{ type: 'damage', amount: 120, damageType: 'physical' }] },
|
|
97
|
+
{ type: 'onKill', whileBuff: 'abilitiesSiphoning', effects: [{ type: 'buff', buff: 'abilitiesSouls', stack: true, on: 'caster' }] }
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
// the next blow at once, not when the swing comes round
|
|
102
|
+
id: 'abilitiesQuickStrike',
|
|
103
|
+
name: 'Quick Strike',
|
|
104
|
+
description: 'The next blow comes at once',
|
|
105
|
+
hotkey: 'W',
|
|
106
|
+
target: 'none',
|
|
107
|
+
cooldown: 1,
|
|
108
|
+
effects: [{ type: 'resetAttack' }]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
// a spell that hurts: Flurry and Spell Vamp go off with it
|
|
112
|
+
id: 'abilitiesWindSlash',
|
|
113
|
+
name: 'Wind Slash',
|
|
114
|
+
description: 'Slashes every enemy within 3 for 90',
|
|
115
|
+
hotkey: 'E',
|
|
116
|
+
target: 'none',
|
|
117
|
+
cooldown: 1,
|
|
118
|
+
effects: [{ type: 'damage', amount: 90, radius: 3 }]
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
/** The warden's passives: what happens as it is hurt */
|
|
123
|
+
const wardenPassives: AbilityDef[] = [
|
|
124
|
+
{
|
|
125
|
+
// triggered damage never sets off another `onDamaged`: two thorny units don't echo forever
|
|
126
|
+
id: 'abilitiesThorns',
|
|
127
|
+
name: 'Thorns',
|
|
128
|
+
description: 'Who strikes it up close takes half the damage back',
|
|
129
|
+
target: 'passive',
|
|
130
|
+
effects: [{ type: 'onDamaged', melee: true, effects: [{ type: 'damage', amount: 0, byDamage: 0.5 }] }]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'abilitiesManaBarrier',
|
|
134
|
+
name: 'Mana Barrier',
|
|
135
|
+
description: 'Under 40% health, a shield of 400, once every 12 seconds',
|
|
136
|
+
target: 'passive',
|
|
137
|
+
effects: [{ type: 'onLowHealth', below: 0.4, cooldown: 12, effects: [{ type: 'buff', buff: 'abilitiesWardShield', on: 'caster' }] }]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
// a killing blow stopped: it keeps all its health, a moment untouchable
|
|
141
|
+
id: 'abilitiesReincarnation',
|
|
142
|
+
name: 'Reincarnation',
|
|
143
|
+
description: 'A killing blow leaves it whole again, once every 20 seconds',
|
|
144
|
+
target: 'passive',
|
|
145
|
+
effects: [{ type: 'onFatal', cooldown: 20, effects: [{ type: 'buff', buff: 'abilitiesReborn', on: 'caster' }] }]
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
/** Sends three weaklings at the blademaster, to kill and gather souls from */
|
|
150
|
+
export const sendWeaklings = defineCommand('abilities.weaklings', {}, (world) => {
|
|
151
|
+
for (const x of [22.5, 24, 25.5]) put(world, 'abilitiesWeakling', ENEMY, { x, z: 25 }, undefined, Math.PI)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
export const triggers: Family = {
|
|
155
|
+
abilities,
|
|
156
|
+
more: [...passives, ...wardenPassives],
|
|
157
|
+
buffs,
|
|
158
|
+
commands: [sendWeaklings],
|
|
159
|
+
units: [
|
|
160
|
+
caster('abilitiesBlademaster', 'Blademaster', [...abilities.map((each) => each.id), ...passives.map((each) => each.id)], {
|
|
161
|
+
stats: {
|
|
162
|
+
health: 1400,
|
|
163
|
+
healthRegen: 10,
|
|
164
|
+
mana: 1000,
|
|
165
|
+
manaRegen: 60,
|
|
166
|
+
attackDamage: 40,
|
|
167
|
+
attackSpeed: 1,
|
|
168
|
+
attackRange: 0.6,
|
|
169
|
+
moveSpeed: 4.2,
|
|
170
|
+
armor: 10
|
|
171
|
+
},
|
|
172
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 8 }]
|
|
173
|
+
}),
|
|
174
|
+
caster(
|
|
175
|
+
'abilitiesWarden',
|
|
176
|
+
'Warden',
|
|
177
|
+
wardenPassives.map((each) => each.id),
|
|
178
|
+
{ stats: { health: 1000, healthRegen: 20, mana: 300, attackDamage: 10, attackSpeed: 1, attackRange: 0.6, moveSpeed: 3.5, armor: 5 } }
|
|
179
|
+
),
|
|
180
|
+
// what hurts the warden: hard blows, slow
|
|
181
|
+
sparring('abilitiesBerserker', 'Berserker', {
|
|
182
|
+
radius: 0.55,
|
|
183
|
+
stats: { health: 3000, healthRegen: 60, attackDamage: 140, attackSpeed: 0.8, attackRange: 0.6, moveSpeed: 3.5 },
|
|
184
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 3 }]
|
|
185
|
+
}),
|
|
186
|
+
sparring('abilitiesWeakling', 'Weakling', { stats: { health: 160, moveSpeed: 3 } })
|
|
187
|
+
],
|
|
188
|
+
example: {
|
|
189
|
+
id: 'abilities-triggers',
|
|
190
|
+
category: 'abilities',
|
|
191
|
+
title: 'Triggers and passives',
|
|
192
|
+
summary:
|
|
193
|
+
'On the left a blademaster strikes a dummy: crits, every fourth blow, an orb, flurry, spell vamp and souls from kills; empower or hurry its next blow. On the right a berserker beats a warden that hurts back, shields itself low and cheats death.',
|
|
194
|
+
tags: ['passive', 'trigger', 'on hit', 'on kill', 'on cast', 'on damaged', 'thorns', 'crit', 'orb', 'cheat death', 'reincarnation', 'empower'],
|
|
195
|
+
source: ['examples/abilities/triggers.ts', 'examples/abilities/kit.ts'],
|
|
196
|
+
map: () => exampleField({ name: 'Triggers', players: 2 }),
|
|
197
|
+
start: (world) => {
|
|
198
|
+
put(world, 'abilitiesBlademaster', PLAYER, { x: 20, z: 21 }, 'caster')
|
|
199
|
+
put(world, UNIT.dummy, ENEMY, { x: 20, z: 23 }, 'dummy', Math.PI)
|
|
200
|
+
put(world, 'abilitiesWarden', PLAYER, { x: 29, z: 21 }, 'ally')
|
|
201
|
+
put(world, 'abilitiesBerserker', ENEMY, { x: 29, z: 22.6 }, 'berserker', Math.PI)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** How the triggers example looks, and its card: what the blademaster casts, weaklings to kill, and readouts */
|
|
2
|
+
import { button, readout } from '../../shell/look'
|
|
3
|
+
import { character, hold, person, sharedLooks, TWO_HANDED } from '../common/looks'
|
|
4
|
+
import type { FamilyLooks } from './looks'
|
|
5
|
+
import { castButton, look, readyOn, resetButton } from './lookKit'
|
|
6
|
+
import { sendWeaklings, triggers } from './triggers'
|
|
7
|
+
|
|
8
|
+
/** A buff's look on its carrier: sparks of `shape` in its colour (`glow`) */
|
|
9
|
+
const glow = (color: [number, number, number], shape: string, more: Record<string, unknown> = {}) =>
|
|
10
|
+
look(color, undefined, { aura: 'glow', options: { shape, ...more } })
|
|
11
|
+
|
|
12
|
+
export const triggersLooks: FamilyLooks = {
|
|
13
|
+
units: {
|
|
14
|
+
abilitiesBlademaster: {
|
|
15
|
+
height: 1.9,
|
|
16
|
+
model: character('barbarian', 0.75, { hold: [hold('sword_2handed')] }),
|
|
17
|
+
attacks: [TWO_HANDED],
|
|
18
|
+
shapes: person(1.9)
|
|
19
|
+
},
|
|
20
|
+
abilitiesWarden: { ...sharedLooks.footman, model: character('knight', 0.75, { hold: [hold('sword_1handed'), hold('shield_spikes', 'l')] }) },
|
|
21
|
+
abilitiesBerserker: {
|
|
22
|
+
height: 2.3,
|
|
23
|
+
model: character('barbarian', 0.95, { hold: [hold('axe_2handed')] }),
|
|
24
|
+
attacks: [TWO_HANDED],
|
|
25
|
+
shapes: person(2.3)
|
|
26
|
+
},
|
|
27
|
+
abilitiesWeakling: { height: 1.5, model: character('skeleton_minion', 0.6), shapes: person(1.5) }
|
|
28
|
+
},
|
|
29
|
+
abilities: {
|
|
30
|
+
abilitiesWindSlash: look([0.75, 0.9, 1], 'slam'),
|
|
31
|
+
abilitiesQuickStrike: look([1, 0.9, 0.4], 'burst'),
|
|
32
|
+
abilitiesSiphoning: glow([0.4, 0.8, 1], 'about', { streaks: true }),
|
|
33
|
+
abilitiesSouls: look([0.5, 1, 0.8], undefined, { counter: true, aura: 'glow', options: { shape: 'rise', rate: 4 } }),
|
|
34
|
+
abilitiesFlurry: look([1, 0.85, 0.3], undefined, { counter: { max: 5 } }),
|
|
35
|
+
abilitiesChilled: glow([0.6, 0.85, 1], 'fall'),
|
|
36
|
+
abilitiesWardShield: glow([0.45, 0.6, 1], 'shell'),
|
|
37
|
+
abilitiesReborn: glow([1, 0.9, 0.5], 'column')
|
|
38
|
+
},
|
|
39
|
+
examples: {
|
|
40
|
+
'abilities-triggers': {
|
|
41
|
+
ready: (client) => readyOn(client, 'abilitiesBlademaster', { minX: 16, minZ: 16, maxX: 33, maxZ: 27 }),
|
|
42
|
+
controls: [
|
|
43
|
+
...triggers.abilities.map((ability) => castButton(ability)),
|
|
44
|
+
button('Send weaklings', (client) => client.session.send(sendWeaklings.make({})), 'Three weaklings to kill, a soul each'),
|
|
45
|
+
resetButton('abilities-triggers'),
|
|
46
|
+
readout('Souls', (client) => {
|
|
47
|
+
const blademaster = client.session.snapshot().entities.find((each) => each.type === 'abilitiesBlademaster')
|
|
48
|
+
return String(blademaster?.stacks?.abilitiesSouls ?? 0)
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|