@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,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creep camps: neutral monsters that guard a spot. Hit one and the whole camp comes, for whoever hurt it most; lead it
|
|
3
|
+
* too far from home (its leash, 14 cells) and it gives up, walks back and heals. Cleared, a camp can come back
|
|
4
|
+
* (`respawnCamp`). Camps come with the heroes pack, placed by a map or in code (`spawnCamp`)
|
|
5
|
+
*/
|
|
6
|
+
import { defineCommand, oneOf, type World } from '@v12-engine/engine'
|
|
7
|
+
import { PLAYER } from '../common/units'
|
|
8
|
+
import { type ExampleDef, exampleField } from '../define'
|
|
9
|
+
import { clear, ownedOf } from './base'
|
|
10
|
+
import { camps, type CampSpot, withCamps } from './camps'
|
|
11
|
+
import { RTS } from './data'
|
|
12
|
+
|
|
13
|
+
/** Three camps across the north of the field */
|
|
14
|
+
export const CREEP_CAMPS: readonly CampSpot[] = [
|
|
15
|
+
{ name: 'west', at: { x: 12, z: 34 }, types: [RTS.gnoll, RTS.gnoll, RTS.gnoll] },
|
|
16
|
+
{ name: 'middle', at: { x: 24, z: 38 }, types: [RTS.ogre, RTS.brute, RTS.brute] },
|
|
17
|
+
{ name: 'east', at: { x: 36, z: 34 }, types: [RTS.brute, RTS.gnoll, RTS.gnoll] }
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
/** Where the army waits, and pulls back to */
|
|
21
|
+
const HOME = { x: 24, z: 10 }
|
|
22
|
+
|
|
23
|
+
/** Eight footmen and four riflemen in the south by their hall and farms, the camps in the north */
|
|
24
|
+
function layOut(world: World): void {
|
|
25
|
+
clear(world)
|
|
26
|
+
// the supply the army takes: the hall's and three farms'
|
|
27
|
+
world.spawn(RTS.townhall, { at: { x: 24, z: 4 }, owner: PLAYER })
|
|
28
|
+
for (let i = 0; i < 3; i++) world.spawn(RTS.farm, { at: { x: 30 + i * 2.5, z: 4 }, owner: PLAYER })
|
|
29
|
+
for (let i = 0; i < 12; i++) {
|
|
30
|
+
const type = i < 8 ? RTS.footman : RTS.rifleman
|
|
31
|
+
world.spawn(type, { at: { x: HOME.x - 3 + (i % 4) * 1.6, z: HOME.z + 2 - Math.floor(i / 4) * 1.6 }, owner: PLAYER, facing: 0 })
|
|
32
|
+
}
|
|
33
|
+
camps(world, CREEP_CAMPS)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The army goes for a camp */
|
|
37
|
+
export const attackCamp = defineCommand('rts.attackCamp', { camp: oneOf('west', 'middle', 'east') }, (world, player, { camp }) => {
|
|
38
|
+
const spot = CREEP_CAMPS.find((each) => each.name === camp)!
|
|
39
|
+
const army = ownedOf(world, player).filter((each) => each.isUnit)
|
|
40
|
+
world.issue({ type: 'order', player, entities: army.map((each) => each.id), order: { type: 'attackMove', point: spot.at } })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
/** The army runs home without fighting: the creeps chase as far as their leash, then go back to their camp */
|
|
44
|
+
export const pullBack = defineCommand('rts.pullBack', {}, (world, player) => {
|
|
45
|
+
const army = ownedOf(world, player).filter((each) => each.isUnit)
|
|
46
|
+
world.issue({ type: 'order', player, entities: army.map((each) => each.id), order: { type: 'move', point: HOME } })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export const resetCreeps = defineCommand('rts.resetCreeps', {}, (world) => layOut(world))
|
|
50
|
+
|
|
51
|
+
export const creeps: ExampleDef = {
|
|
52
|
+
id: 'rts-creeps',
|
|
53
|
+
category: 'rts',
|
|
54
|
+
title: 'Creep camps',
|
|
55
|
+
summary:
|
|
56
|
+
'Three camps of neutral creeps guard their ground. Attack one and the whole camp answers; pull back and they chase only so far, then walk home and heal. Clear them all, or bring them back.',
|
|
57
|
+
tags: ['creeps', 'neutral', 'camp', 'leash', 'aggro', 'jungle', 'monsters'],
|
|
58
|
+
map: () => withCamps(exampleField({ name: 'Creeps', size: 48 }), CREEP_CAMPS),
|
|
59
|
+
start: (world) => layOut(world),
|
|
60
|
+
source: ['examples/rts/creeps.ts', 'examples/rts/camps.ts', 'examples/rts/data.ts', 'examples/rts/looks.ts']
|
|
61
|
+
}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the RTS examples play with, as data: workers, soldiers, a hero, the buildings of a base, gold mines and trees,
|
|
3
|
+
* creeps, two upgrades, the hero's spells and a shop's items. A small classic roster, its ids starting `rts` so that no
|
|
4
|
+
* other example's collide. How each looks is `./looks.ts`
|
|
5
|
+
*/
|
|
6
|
+
import type { AbilityDef, BuffDef, ItemDef, UnitTypeDef, UpgradeDef } from '@v12-engine/engine'
|
|
7
|
+
|
|
8
|
+
/** The ids, in one place: `RTS.peasant` everywhere else */
|
|
9
|
+
export const RTS = {
|
|
10
|
+
peasant: 'rtsPeasant',
|
|
11
|
+
footman: 'rtsFootman',
|
|
12
|
+
rifleman: 'rtsRifleman',
|
|
13
|
+
paladin: 'rtsPaladin',
|
|
14
|
+
townhall: 'rtsTownhall',
|
|
15
|
+
keep: 'rtsKeep',
|
|
16
|
+
farm: 'rtsFarm',
|
|
17
|
+
barracks: 'rtsBarracks',
|
|
18
|
+
blacksmith: 'rtsBlacksmith',
|
|
19
|
+
tower: 'rtsTower',
|
|
20
|
+
altar: 'rtsAltar',
|
|
21
|
+
shop: 'rtsShop',
|
|
22
|
+
goldmine: 'rtsGoldmine',
|
|
23
|
+
tree: 'rtsTree',
|
|
24
|
+
gnoll: 'rtsGnoll',
|
|
25
|
+
brute: 'rtsGnollBrute',
|
|
26
|
+
ogre: 'rtsOgre'
|
|
27
|
+
} as const
|
|
28
|
+
|
|
29
|
+
// ---- units
|
|
30
|
+
|
|
31
|
+
export const peasant: UnitTypeDef = {
|
|
32
|
+
id: RTS.peasant,
|
|
33
|
+
name: 'Peasant',
|
|
34
|
+
kind: 'unit',
|
|
35
|
+
radius: 0.4,
|
|
36
|
+
stats: { health: 250, attackDamage: 8, attackSpeed: 0.6, attackRange: 0.4, moveSpeed: 4 },
|
|
37
|
+
sight: 8,
|
|
38
|
+
// it fights only when told to: it never goes after an enemy on its own (`acquisitionRange: 0`)
|
|
39
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 0, targets: ['unit', 'building'] }],
|
|
40
|
+
cost: { gold: 75 },
|
|
41
|
+
supply: 1,
|
|
42
|
+
buildTime: 8,
|
|
43
|
+
// a worker: it carries 10 a trip, and a load of gold takes 1.5 seconds to dig
|
|
44
|
+
harvester: { capacity: 10, gatherTime: 1.5 },
|
|
45
|
+
builds: [RTS.townhall, RTS.farm, RTS.barracks, RTS.blacksmith, RTS.tower, RTS.altar],
|
|
46
|
+
hotkey: 'P'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const footman: UnitTypeDef = {
|
|
50
|
+
id: RTS.footman,
|
|
51
|
+
name: 'Footman',
|
|
52
|
+
kind: 'unit',
|
|
53
|
+
radius: 0.45,
|
|
54
|
+
stats: { health: 420, attackDamage: 12, attackSpeed: 1.2, attackRange: 0.5, armor: 10, moveSpeed: 4 },
|
|
55
|
+
sight: 10,
|
|
56
|
+
attacks: [{ damagePoint: 0.45, acquisitionRange: 8 }],
|
|
57
|
+
cost: { gold: 135 },
|
|
58
|
+
supply: 2,
|
|
59
|
+
buildTime: 6,
|
|
60
|
+
hotkey: 'F'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const rifleman: UnitTypeDef = {
|
|
64
|
+
id: RTS.rifleman,
|
|
65
|
+
name: 'Rifleman',
|
|
66
|
+
kind: 'unit',
|
|
67
|
+
radius: 0.45,
|
|
68
|
+
stats: { health: 300, attackDamage: 20, attackSpeed: 0.7, attackRange: 6.5, moveSpeed: 4 },
|
|
69
|
+
sight: 11,
|
|
70
|
+
attacks: [{ damagePoint: 0.35, acquisitionRange: 9, projectileSpeed: 24, targets: ['ground', 'air'] }],
|
|
71
|
+
cost: { gold: 205, lumber: 30 },
|
|
72
|
+
supply: 3,
|
|
73
|
+
buildTime: 8,
|
|
74
|
+
// a finished blacksmith first
|
|
75
|
+
requires: [RTS.blacksmith],
|
|
76
|
+
hotkey: 'R'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** A hero: it levels up by what it kills, and learns a spell a level from the three it `learns` */
|
|
80
|
+
export const paladin: UnitTypeDef = {
|
|
81
|
+
id: RTS.paladin,
|
|
82
|
+
name: 'Paladin',
|
|
83
|
+
kind: 'unit',
|
|
84
|
+
radius: 0.5,
|
|
85
|
+
stats: { health: 700, mana: 280, manaRegen: 1.2, attackDamage: 30, attackSpeed: 0.9, attackRange: 0.6, armor: 25, moveSpeed: 4.4 },
|
|
86
|
+
sight: 12,
|
|
87
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 8 }],
|
|
88
|
+
hero: {
|
|
89
|
+
learns: ['rtsHolyLight', 'rtsThunderClap', 'rtsDevotionAura'],
|
|
90
|
+
growth: { stats: { health: 90, mana: 25, attackDamage: 3, armor: 3 } },
|
|
91
|
+
maxLevel: 10
|
|
92
|
+
},
|
|
93
|
+
inventory: 6,
|
|
94
|
+
level: 1,
|
|
95
|
+
cost: { gold: 425, lumber: 100 },
|
|
96
|
+
supply: 5,
|
|
97
|
+
buildTime: 10,
|
|
98
|
+
hotkey: 'P'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ---- buildings
|
|
102
|
+
|
|
103
|
+
export const townhall: UnitTypeDef = {
|
|
104
|
+
id: RTS.townhall,
|
|
105
|
+
name: 'Town Hall',
|
|
106
|
+
kind: 'building',
|
|
107
|
+
radius: 2,
|
|
108
|
+
footprint: { w: 4, d: 4 },
|
|
109
|
+
stats: { health: 2000, armor: 40 },
|
|
110
|
+
sight: 12,
|
|
111
|
+
cost: { gold: 385, lumber: 205 },
|
|
112
|
+
buildTime: 45,
|
|
113
|
+
supplyGiven: 12,
|
|
114
|
+
trains: [RTS.peasant],
|
|
115
|
+
// workers bring what they gather here
|
|
116
|
+
dropOff: true,
|
|
117
|
+
// it can become a keep, as it stands (`upgradeBuilding`)
|
|
118
|
+
upgradesTo: RTS.keep,
|
|
119
|
+
hotkey: 'H'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const keep: UnitTypeDef = {
|
|
123
|
+
...townhall,
|
|
124
|
+
id: RTS.keep,
|
|
125
|
+
name: 'Keep',
|
|
126
|
+
stats: { health: 2800, armor: 50 },
|
|
127
|
+
cost: { gold: 200, lumber: 100 },
|
|
128
|
+
buildTime: 12,
|
|
129
|
+
// it counts as a town hall for what asks for one
|
|
130
|
+
provides: [RTS.townhall],
|
|
131
|
+
upgradesTo: undefined,
|
|
132
|
+
hotkey: 'U'
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const farm: UnitTypeDef = {
|
|
136
|
+
id: RTS.farm,
|
|
137
|
+
name: 'Farm',
|
|
138
|
+
kind: 'building',
|
|
139
|
+
radius: 1,
|
|
140
|
+
footprint: { w: 2, d: 2 },
|
|
141
|
+
stats: { health: 600, armor: 20 },
|
|
142
|
+
sight: 6,
|
|
143
|
+
cost: { gold: 80, lumber: 20 },
|
|
144
|
+
buildTime: 10,
|
|
145
|
+
// room for six more of supply
|
|
146
|
+
supplyGiven: 6,
|
|
147
|
+
hotkey: 'F'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const barracks: UnitTypeDef = {
|
|
151
|
+
id: RTS.barracks,
|
|
152
|
+
name: 'Barracks',
|
|
153
|
+
kind: 'building',
|
|
154
|
+
radius: 1.5,
|
|
155
|
+
footprint: { w: 3, d: 3 },
|
|
156
|
+
stats: { health: 1500, armor: 30 },
|
|
157
|
+
sight: 9,
|
|
158
|
+
cost: { gold: 160, lumber: 60 },
|
|
159
|
+
buildTime: 15,
|
|
160
|
+
trains: [RTS.footman, RTS.rifleman],
|
|
161
|
+
hotkey: 'B'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const blacksmith: UnitTypeDef = {
|
|
165
|
+
id: RTS.blacksmith,
|
|
166
|
+
name: 'Blacksmith',
|
|
167
|
+
kind: 'building',
|
|
168
|
+
radius: 1.5,
|
|
169
|
+
footprint: { w: 3, d: 3 },
|
|
170
|
+
stats: { health: 1200, armor: 30 },
|
|
171
|
+
sight: 9,
|
|
172
|
+
cost: { gold: 140, lumber: 60 },
|
|
173
|
+
buildTime: 15,
|
|
174
|
+
requires: [RTS.barracks],
|
|
175
|
+
researches: ['rtsSwords', 'rtsPlating'],
|
|
176
|
+
hotkey: 'S'
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const tower: UnitTypeDef = {
|
|
180
|
+
id: RTS.tower,
|
|
181
|
+
name: 'Guard Tower',
|
|
182
|
+
kind: 'building',
|
|
183
|
+
radius: 1,
|
|
184
|
+
footprint: { w: 2, d: 2 },
|
|
185
|
+
stats: { health: 1000, attackDamage: 25, attackSpeed: 0.9, attackRange: 8, armor: 30 },
|
|
186
|
+
sight: 12,
|
|
187
|
+
attacks: [{ damagePoint: 0.3, acquisitionRange: 8, projectileSpeed: 24, targets: ['ground', 'air'] }],
|
|
188
|
+
cost: { gold: 110, lumber: 50 },
|
|
189
|
+
buildTime: 12,
|
|
190
|
+
requires: [RTS.barracks],
|
|
191
|
+
hotkey: 'T'
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const altar: UnitTypeDef = {
|
|
195
|
+
id: RTS.altar,
|
|
196
|
+
name: 'Altar of Kings',
|
|
197
|
+
kind: 'building',
|
|
198
|
+
radius: 1.5,
|
|
199
|
+
footprint: { w: 3, d: 3 },
|
|
200
|
+
stats: { health: 1200, armor: 30 },
|
|
201
|
+
sight: 9,
|
|
202
|
+
cost: { gold: 180, lumber: 50 },
|
|
203
|
+
buildTime: 15,
|
|
204
|
+
// trains the hero, and brings it back when it falls
|
|
205
|
+
trains: [RTS.paladin],
|
|
206
|
+
revives: true,
|
|
207
|
+
hotkey: 'A'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** A goblin merchant: neutral and unbreakable, selling to whoever has a unit near it */
|
|
211
|
+
export const shop: UnitTypeDef = {
|
|
212
|
+
id: RTS.shop,
|
|
213
|
+
name: 'Goblin Merchant',
|
|
214
|
+
kind: 'building',
|
|
215
|
+
radius: 1.5,
|
|
216
|
+
footprint: { w: 3, d: 3 },
|
|
217
|
+
stats: { health: 1000, armor: 40 },
|
|
218
|
+
sight: 8,
|
|
219
|
+
invulnerable: true,
|
|
220
|
+
sells: ['rtsHealingSalve', 'rtsClaws', 'rtsRingOfProtection', 'rtsGlovesOfHaste']
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---- what is gathered
|
|
224
|
+
|
|
225
|
+
export const goldmine: UnitTypeDef = {
|
|
226
|
+
id: RTS.goldmine,
|
|
227
|
+
name: 'Gold Mine',
|
|
228
|
+
kind: 'resource',
|
|
229
|
+
radius: 2,
|
|
230
|
+
footprint: { w: 4, d: 4 },
|
|
231
|
+
stats: { health: 1 },
|
|
232
|
+
sight: 0,
|
|
233
|
+
invulnerable: true,
|
|
234
|
+
// workers go in one at a time (`entered`) and come out with a load
|
|
235
|
+
resource: { id: 'gold', amount: 12500, slots: 1, entered: true }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export const tree: UnitTypeDef = {
|
|
239
|
+
id: RTS.tree,
|
|
240
|
+
name: 'Tree',
|
|
241
|
+
kind: 'resource',
|
|
242
|
+
radius: 0.5,
|
|
243
|
+
footprint: { w: 1, d: 1 },
|
|
244
|
+
stats: { health: 1 },
|
|
245
|
+
sight: 0,
|
|
246
|
+
invulnerable: true,
|
|
247
|
+
fellable: true,
|
|
248
|
+
// a worker chops a load in 4 seconds; 50 in all and the tree is gone
|
|
249
|
+
resource: { id: 'lumber', amount: 50, slots: 1, gatherTime: 4 }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ---- creeps
|
|
253
|
+
|
|
254
|
+
export const gnoll: UnitTypeDef = {
|
|
255
|
+
id: RTS.gnoll,
|
|
256
|
+
name: 'Gnoll Poacher',
|
|
257
|
+
kind: 'unit',
|
|
258
|
+
radius: 0.42,
|
|
259
|
+
stats: { health: 300, attackDamage: 12, attackSpeed: 0.8, attackRange: 0.5, moveSpeed: 4 },
|
|
260
|
+
sight: 8,
|
|
261
|
+
attacks: [{ damagePoint: 0.4, acquisitionRange: 5 }],
|
|
262
|
+
level: 1,
|
|
263
|
+
bounty: [8, 12],
|
|
264
|
+
xp: 40
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const brute: UnitTypeDef = {
|
|
268
|
+
...gnoll,
|
|
269
|
+
id: RTS.brute,
|
|
270
|
+
name: 'Gnoll Brute',
|
|
271
|
+
radius: 0.5,
|
|
272
|
+
stats: { health: 600, attackDamage: 20, attackSpeed: 0.7, attackRange: 0.6, armor: 10, moveSpeed: 3.8 },
|
|
273
|
+
level: 3,
|
|
274
|
+
bounty: [18, 24],
|
|
275
|
+
xp: 80,
|
|
276
|
+
drops: [{ item: 'rtsHealingSalve', chance: 1 }]
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export const ogre: UnitTypeDef = {
|
|
280
|
+
id: RTS.ogre,
|
|
281
|
+
name: 'Ogre Lord',
|
|
282
|
+
kind: 'unit',
|
|
283
|
+
radius: 0.75,
|
|
284
|
+
stats: { health: 1400, healthRegen: 1, attackDamage: 45, attackSpeed: 0.5, attackRange: 0.7, armor: 20, moveSpeed: 3.6 },
|
|
285
|
+
sight: 9,
|
|
286
|
+
attacks: [{ damagePoint: 0.55, acquisitionRange: 5 }],
|
|
287
|
+
level: 5,
|
|
288
|
+
bounty: [45, 60],
|
|
289
|
+
xp: 200,
|
|
290
|
+
drops: [{ item: 'rtsClaws', chance: 1 }]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export const rtsUnits: UnitTypeDef[] = [
|
|
294
|
+
peasant,
|
|
295
|
+
footman,
|
|
296
|
+
rifleman,
|
|
297
|
+
paladin,
|
|
298
|
+
townhall,
|
|
299
|
+
keep,
|
|
300
|
+
farm,
|
|
301
|
+
barracks,
|
|
302
|
+
blacksmith,
|
|
303
|
+
tower,
|
|
304
|
+
altar,
|
|
305
|
+
shop,
|
|
306
|
+
goldmine,
|
|
307
|
+
tree,
|
|
308
|
+
gnoll,
|
|
309
|
+
brute,
|
|
310
|
+
ogre
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
// ---- upgrades
|
|
314
|
+
|
|
315
|
+
export const rtsUpgrades: UpgradeDef[] = [
|
|
316
|
+
{
|
|
317
|
+
id: 'rtsSwords',
|
|
318
|
+
name: 'Forged Swords',
|
|
319
|
+
description: '+4 damage for footmen, each level',
|
|
320
|
+
cost: { gold: 100, lumber: 50 },
|
|
321
|
+
researchTime: 8,
|
|
322
|
+
maxLevel: 3,
|
|
323
|
+
// the second and third levels ask for a keep
|
|
324
|
+
requires: [[], [RTS.keep], [RTS.keep]],
|
|
325
|
+
hotkey: 'W',
|
|
326
|
+
effects: [{ units: [RTS.footman], stats: { attackDamage: 4 } }]
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
id: 'rtsPlating',
|
|
330
|
+
name: 'Iron Plating',
|
|
331
|
+
description: '+10 armor for footmen and riflemen, each level',
|
|
332
|
+
cost: { gold: 125, lumber: 75 },
|
|
333
|
+
researchTime: 8,
|
|
334
|
+
maxLevel: 2,
|
|
335
|
+
hotkey: 'P',
|
|
336
|
+
effects: [{ units: [RTS.footman, RTS.rifleman], stats: { armor: 10 } }]
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
|
|
340
|
+
// ---- the hero's spells
|
|
341
|
+
|
|
342
|
+
export const rtsBuffs: BuffDef[] = [
|
|
343
|
+
{ id: 'rtsStunned', name: 'Stunned', description: 'Cannot do anything', harmful: true, stun: true },
|
|
344
|
+
{ id: 'rtsDevotion', name: 'Devotion Aura', description: 'More armor, near the paladin', stats: { armor: [10, 20, 30] } }
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
export const rtsAbilities: AbilityDef[] = [
|
|
348
|
+
{
|
|
349
|
+
id: 'rtsHolyLight',
|
|
350
|
+
name: 'Holy Light',
|
|
351
|
+
description: 'Heals an ally for 200, 350 or 500',
|
|
352
|
+
hotkey: 'Q',
|
|
353
|
+
target: 'unit',
|
|
354
|
+
targets: { allies: true, self: false },
|
|
355
|
+
manaCost: 65,
|
|
356
|
+
cooldown: 5,
|
|
357
|
+
range: 8,
|
|
358
|
+
castTime: 0.4,
|
|
359
|
+
effects: [{ type: 'heal', amount: [200, 350, 500] }]
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
id: 'rtsThunderClap',
|
|
363
|
+
name: 'Thunder Clap',
|
|
364
|
+
description: 'Slams the ground: 60, 110 or 160 damage to enemies around, and they are stunned a second',
|
|
365
|
+
hotkey: 'W',
|
|
366
|
+
target: 'none',
|
|
367
|
+
manaCost: 75,
|
|
368
|
+
cooldown: 8,
|
|
369
|
+
castTime: 0.4,
|
|
370
|
+
effects: [
|
|
371
|
+
{ type: 'damage', amount: [60, 110, 160], radius: 3.5 },
|
|
372
|
+
{ type: 'buff', buff: 'rtsStunned', duration: 1, radius: 3.5 }
|
|
373
|
+
]
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
id: 'rtsDevotionAura',
|
|
377
|
+
name: 'Devotion Aura',
|
|
378
|
+
description: 'Allies near the paladin have 10, 20 or 30 more armor',
|
|
379
|
+
target: 'passive',
|
|
380
|
+
effects: [{ type: 'aura', buff: 'rtsDevotion', radius: 9 }]
|
|
381
|
+
},
|
|
382
|
+
// what the items use
|
|
383
|
+
{
|
|
384
|
+
id: 'rtsDrinkSalve',
|
|
385
|
+
name: 'Healing Salve',
|
|
386
|
+
description: 'Heals 400 at once',
|
|
387
|
+
target: 'none',
|
|
388
|
+
cooldown: 1,
|
|
389
|
+
effects: [{ type: 'heal', amount: 400, on: 'caster' }]
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
|
|
393
|
+
// ---- what the shop sells
|
|
394
|
+
|
|
395
|
+
export const rtsItems: ItemDef[] = [
|
|
396
|
+
{ id: 'rtsHealingSalve', name: 'Healing Salve', description: 'Heals 400 at once', cost: { gold: 100 }, charges: 2, ability: 'rtsDrinkSalve' },
|
|
397
|
+
{ id: 'rtsClaws', name: 'Claws of Attack', description: '+12 damage', cost: { gold: 300 }, bonus: { stats: { attackDamage: 12 } } },
|
|
398
|
+
{ id: 'rtsRingOfProtection', name: 'Ring of Protection', description: '+15 armor', cost: { gold: 250 }, bonus: { stats: { armor: 15 } } },
|
|
399
|
+
{
|
|
400
|
+
id: 'rtsGlovesOfHaste',
|
|
401
|
+
name: 'Gloves of Haste',
|
|
402
|
+
description: 'Attacks 20% faster',
|
|
403
|
+
cost: { gold: 200 },
|
|
404
|
+
bonus: { percent: { attackSpeed: 0.2 } }
|
|
405
|
+
}
|
|
406
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A transport ship across a river: footmen, archers and a mage board it from the west bank, it sails over and lets
|
|
3
|
+
* them out on the east bank, where nothing that walks could go. A ship is a unit type with `naval` (it goes on deep
|
|
4
|
+
* water and nowhere else) and `transport` (it carries others); what it carries goes down with it if it sinks
|
|
5
|
+
*/
|
|
6
|
+
import { defineCommand, type World } from '@v12-engine/engine'
|
|
7
|
+
import { PLAYER, UNIT } from '../common/units'
|
|
8
|
+
import type { ExampleDef } from '../define'
|
|
9
|
+
import { clear } from './base'
|
|
10
|
+
import { CARRY, route } from './carry'
|
|
11
|
+
import { riverAt, riverMap } from './waterside'
|
|
12
|
+
|
|
13
|
+
/** Where the ship waits by the west bank, where it crosses to, and where it lets them out on the east bank */
|
|
14
|
+
const MOOR = { x: riverAt(20) - 3.4, z: 21.5 }
|
|
15
|
+
const ACROSS = { x: riverAt(20) + 3.4, z: 21.5 }
|
|
16
|
+
const LAND = { x: riverAt(20) + 9, z: 20 }
|
|
17
|
+
|
|
18
|
+
/** The squad on the west bank, by the jetty, and the ship by the bank waiting for them */
|
|
19
|
+
export function layOutFerry(world: World): void {
|
|
20
|
+
clear(world)
|
|
21
|
+
world.setState(route, { moor: MOOR, across: ACROSS, land: LAND })
|
|
22
|
+
world.spawn(CARRY.ship, { at: MOOR, owner: PLAYER, facing: 0 })
|
|
23
|
+
const squad = [UNIT.footman, UNIT.footman, UNIT.footman, UNIT.footman, UNIT.archer, UNIT.archer, UNIT.archer, UNIT.mage]
|
|
24
|
+
squad.forEach((type, i) =>
|
|
25
|
+
world.spawn(type, { at: { x: 9 + Math.floor(i / 4) * 1.8, z: 17.3 + (i % 4) * 1.8 }, owner: PLAYER, facing: Math.PI / 2 })
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const resetFerry = defineCommand('rts.resetFerry', {}, (world) => layOutFerry(world))
|
|
30
|
+
|
|
31
|
+
export const ferry: ExampleDef = {
|
|
32
|
+
id: 'rts-ferry',
|
|
33
|
+
category: 'rts',
|
|
34
|
+
title: 'Ferrying across a river',
|
|
35
|
+
summary:
|
|
36
|
+
'Board the squad onto the ship, sail it across the river and unload on the far bank, or play it by hand: right-click the ship with your units, select the ship and right-click the far bank. Sink it loaded to see what it carries go down with it.',
|
|
37
|
+
tags: ['transport', 'ship', 'boat', 'naval', 'board', 'load', 'unload', 'cargo', 'river', 'water', 'ferry'],
|
|
38
|
+
map: riverMap,
|
|
39
|
+
start: (world) => layOutFerry(world),
|
|
40
|
+
source: ['examples/rts/ferry.ts', 'examples/rts/carry.ts', 'examples/rts/waterside.ts', 'examples/rts/looks.ts']
|
|
41
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A garrison: soldiers go into a tower, out of reach inside, and it shoots for them. A building that carries others is
|
|
3
|
+
* one with `transport`; with `armed` it attacks only while someone is in it, and each one inside adds to its arrows.
|
|
4
|
+
* Brought down, it lets them out alive, where a ship or a zeppelin takes them down with it
|
|
5
|
+
*/
|
|
6
|
+
import { defineCommand, type World } from '@v12-engine/engine'
|
|
7
|
+
import { ENEMY, PLAYER, UNIT } from '../common/units'
|
|
8
|
+
import type { ExampleDef } from '../define'
|
|
9
|
+
import { clear } from './base'
|
|
10
|
+
import { CARRY, route } from './carry'
|
|
11
|
+
import { RTS } from './data'
|
|
12
|
+
import { outpostMap, roadAt } from './waterside'
|
|
13
|
+
|
|
14
|
+
/** Where the tower stands, before the hall it guards, and where the gnolls come from */
|
|
15
|
+
export const TOWER = { x: roadAt(22) + 2.5, z: 22 }
|
|
16
|
+
const DEN = { x: roadAt(35), z: 35 }
|
|
17
|
+
|
|
18
|
+
/** The hall and the tower before it on the road, four soldiers by the tower */
|
|
19
|
+
export function layOutGarrison(world: World): void {
|
|
20
|
+
clear(world)
|
|
21
|
+
world.setState(route, { moor: TOWER, across: TOWER, land: TOWER })
|
|
22
|
+
world.spawn(RTS.townhall, { at: { x: 24, z: 13 }, owner: PLAYER })
|
|
23
|
+
world.spawn(RTS.farm, { at: { x: 18.5, z: 12 }, owner: PLAYER })
|
|
24
|
+
world.spawn(RTS.farm, { at: { x: 29.5, z: 12 }, owner: PLAYER })
|
|
25
|
+
world.spawn(CARRY.tower, { at: TOWER, owner: PLAYER })
|
|
26
|
+
;[UNIT.archer, UNIT.archer, UNIT.archer, UNIT.footman].forEach((type, i) =>
|
|
27
|
+
world.spawn(type, { at: { x: TOWER.x - 4 + i * 1.6, z: 18.5 }, owner: PLAYER, facing: Math.PI / 2 })
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A pack of gnolls, four poachers and two brutes, comes down the way for the tower and the hall behind it */
|
|
32
|
+
export const gnollsCome = defineCommand('rts.gnollsCome', {}, (world) => {
|
|
33
|
+
const pack = [RTS.gnoll, RTS.gnoll, RTS.brute, RTS.gnoll, RTS.gnoll, RTS.brute]
|
|
34
|
+
const ids = pack.map((type, i) => world.spawn(type, { at: { x: DEN.x - 3 + (i % 3) * 3, z: DEN.z + Math.floor(i / 3) * 2 }, owner: ENEMY }).id)
|
|
35
|
+
world.issue({ type: 'order', player: ENEMY, entities: ids, order: { type: 'attackMove', point: { x: TOWER.x, z: TOWER.z - 4 } } })
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export const resetGarrison = defineCommand('rts.resetGarrison', {}, (world) => layOutGarrison(world))
|
|
39
|
+
|
|
40
|
+
export const garrison: ExampleDef = {
|
|
41
|
+
id: 'rts-garrison',
|
|
42
|
+
category: 'rts',
|
|
43
|
+
title: 'Garrisoning a tower',
|
|
44
|
+
summary:
|
|
45
|
+
'Put the soldiers in the tower (or right-click it with them): it shoots only while someone is inside, harder for each. Send in the gnolls, then let them out with the Unload button, or bring the tower down and see them come out alive.',
|
|
46
|
+
tags: ['garrison', 'tower', 'bunker', 'burrow', 'transport', 'board', 'unload', 'cargo', 'defend', 'armed'],
|
|
47
|
+
map: outpostMap,
|
|
48
|
+
start: (world) => layOutGarrison(world),
|
|
49
|
+
source: ['examples/rts/garrison.ts', 'examples/rts/carry.ts', 'examples/rts/waterside.ts', 'examples/rts/looks.ts']
|
|
50
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gathering: peasants dig gold at the mine and chop trees, carry a load at a time to the town hall and hand it in.
|
|
3
|
+
* A worker is a unit type with `harvester`; a mine and a tree are resources (`resource`); the hall takes loads in
|
|
4
|
+
* (`dropOff`). A mine takes one worker in at a time, so five or so keep it busy; each tree is felled once it is cut down
|
|
5
|
+
*/
|
|
6
|
+
import type { World } from '@v12-engine/engine'
|
|
7
|
+
import { PLAYER } from '../common/units'
|
|
8
|
+
import { type ExampleDef, exampleField } from '../define'
|
|
9
|
+
import { base, clear, purse, startWork } from './base'
|
|
10
|
+
|
|
11
|
+
/** The base, its five peasants put to work, three on the gold and two in the wood */
|
|
12
|
+
export function harvestStart(world: World): void {
|
|
13
|
+
clear(world)
|
|
14
|
+
purse(world, PLAYER, 200, 50)
|
|
15
|
+
base(world, PLAYER, { x: 26, z: 22 })
|
|
16
|
+
startWork(world, PLAYER, 3)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const harvest: ExampleDef = {
|
|
20
|
+
id: 'rts-harvest',
|
|
21
|
+
category: 'rts',
|
|
22
|
+
title: 'Gold and lumber',
|
|
23
|
+
summary:
|
|
24
|
+
'Peasants dig gold at the mine and chop wood, carrying each load home to the town hall. Train more from the hall (or press Train a peasant) and send them to work with a right-click on the mine or a tree.',
|
|
25
|
+
tags: ['economy', 'gather', 'harvest', 'workers', 'gold', 'lumber', 'resources', 'mine'],
|
|
26
|
+
map: () => exampleField({ name: 'Homestead', size: 44 }),
|
|
27
|
+
start: (world) => harvestStart(world),
|
|
28
|
+
source: ['examples/rts/harvest.ts', 'examples/rts/base.ts', 'examples/rts/data.ts', 'examples/rts/looks.ts']
|
|
29
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A hero: a unit type with `hero`. It earns experience from what is killed near it, levels up (stronger each level by
|
|
3
|
+
* its `growth`), and gets a point a level to learn one of the spells it `learns` or raise one a level. Camps of creeps
|
|
4
|
+
* round the field are worth hunting; the altar trains another hero and brings a fallen one back
|
|
5
|
+
*/
|
|
6
|
+
import { defineCommand, grantXp, heroesOf, oneOf, type World } from '@v12-engine/engine'
|
|
7
|
+
import { PLAYER } from '../common/units'
|
|
8
|
+
import { type ExampleDef, exampleField } from '../define'
|
|
9
|
+
import { clear, ownedOf, purse } from './base'
|
|
10
|
+
import { camps, type CampSpot, withCamps } from './camps'
|
|
11
|
+
import { RTS } from './data'
|
|
12
|
+
|
|
13
|
+
/** The camps round the field, from the easiest to the ogre */
|
|
14
|
+
export const HERO_CAMPS: readonly CampSpot[] = [
|
|
15
|
+
{ name: 'gnolls', at: { x: 22, z: 32 }, types: [RTS.gnoll, RTS.gnoll] },
|
|
16
|
+
{ name: 'brutes', at: { x: 34, z: 28 }, types: [RTS.brute, RTS.gnoll, RTS.gnoll] },
|
|
17
|
+
{ name: 'ogre', at: { x: 30, z: 38 }, types: [RTS.ogre, RTS.gnoll] }
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
/** The paladin and two footmen by the altar, the camps round them */
|
|
21
|
+
function layOut(world: World): void {
|
|
22
|
+
clear(world)
|
|
23
|
+
purse(world, PLAYER, 600, 200)
|
|
24
|
+
world.spawn(RTS.altar, { at: { x: 16.5, z: 16.5 }, owner: PLAYER })
|
|
25
|
+
// the supply the hero and the footmen take
|
|
26
|
+
world.spawn(RTS.farm, { at: { x: 13, z: 21 }, owner: PLAYER })
|
|
27
|
+
world.spawn(RTS.farm, { at: { x: 16, z: 21 }, owner: PLAYER })
|
|
28
|
+
world.spawn(RTS.paladin, { at: { x: 21, z: 18 }, owner: PLAYER, facing: 0.6 })
|
|
29
|
+
world.spawn(RTS.footman, { at: { x: 19.5, z: 19.5 }, owner: PLAYER })
|
|
30
|
+
world.spawn(RTS.footman, { at: { x: 22.5, z: 19.5 }, owner: PLAYER })
|
|
31
|
+
camps(world, HERO_CAMPS)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The hero and its footmen go for a camp, fighting what they meet on the way */
|
|
35
|
+
export const hunt = defineCommand('rts.hunt', { camp: oneOf('gnolls', 'brutes', 'ogre') }, (world, player, { camp }) => {
|
|
36
|
+
const spot = HERO_CAMPS.find((each) => each.name === camp)!
|
|
37
|
+
const party = ownedOf(world, player).filter((each) => each.isUnit)
|
|
38
|
+
world.issue({ type: 'order', player, entities: party.map((each) => each.id), order: { type: 'attackMove', point: spot.at } })
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
/** Experience for the hero, as a kill would give it: enough for a level or two */
|
|
42
|
+
export const giveXp = defineCommand('rts.heroXp', {}, (world, player) => {
|
|
43
|
+
for (const hero of heroesOf(world, player)) if (hero.alive) grantXp(world, hero, 300)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
/** The hero learns a spell, or raises one a level, with a point it has: what its card's learn buttons send */
|
|
47
|
+
export const learnSpell = defineCommand(
|
|
48
|
+
'rts.learn',
|
|
49
|
+
{ ability: oneOf('rtsHolyLight', 'rtsThunderClap', 'rtsDevotionAura') },
|
|
50
|
+
(world, player, { ability }) => {
|
|
51
|
+
for (const hero of heroesOf(world, player)) world.issue({ type: 'learn', player, entity: hero.id, ability })
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
export const resetHero = defineCommand('rts.resetHero', {}, (world) => layOut(world))
|
|
56
|
+
|
|
57
|
+
export const hero: ExampleDef = {
|
|
58
|
+
id: 'rts-hero',
|
|
59
|
+
category: 'rts',
|
|
60
|
+
title: 'A hero levels up',
|
|
61
|
+
summary:
|
|
62
|
+
'A paladin hunts creep camps for experience, levels up, and spends a point a level on Holy Light, Thunder Clap or Devotion Aura. Hunt a camp, or give experience and learn from the card; Q and W cast.',
|
|
63
|
+
tags: ['hero', 'level', 'experience', 'xp', 'skills', 'learn', 'spells', 'aura', 'creeps', 'altar', 'revive'],
|
|
64
|
+
map: () => withCamps(exampleField({ name: 'Hunting grounds', size: 48 }), HERO_CAMPS),
|
|
65
|
+
start: (world) => layOut(world),
|
|
66
|
+
source: ['examples/rts/hero.ts', 'examples/rts/camps.ts', 'examples/rts/data.ts', 'examples/rts/looks.ts']
|
|
67
|
+
}
|