@v12-engine/create 0.0.0-stage → 0.1.1

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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -2
  3. package/base/_gitignore +10 -0
  4. package/base/index.html +20 -0
  5. package/base/package.json +23 -0
  6. package/base/src/ground.worker.ts +8 -0
  7. package/base/src/main.ts +61 -0
  8. package/base/tsconfig.json +14 -0
  9. package/base/vite.config.ts +25 -0
  10. package/bin.mjs +10 -0
  11. package/cli.mjs +381 -0
  12. package/index.mjs +286 -0
  13. package/layers/assets/package.json +5 -0
  14. package/layers/editor/package.json +8 -0
  15. package/layers/editor/src/editor.ts +13 -0
  16. package/layers/rooms/server.ts +69 -0
  17. package/layers/server/package.json +8 -0
  18. package/layers/server/server.ts +34 -0
  19. package/layers/source/package.json +12 -0
  20. package/layers/source/tsconfig.json +5 -0
  21. package/layers/source/vite.config.ts +45 -0
  22. package/layers/tests/package.json +8 -0
  23. package/package.json +46 -5
  24. package/starters/campaign/README.md +61 -0
  25. package/starters/campaign/files.txt +42 -0
  26. package/starters/campaign/layers/editor/src/editor.ts +18 -0
  27. package/starters/campaign/layers/rooms/server.ts +39 -0
  28. package/starters/campaign/layers/server/server.ts +33 -0
  29. package/starters/campaign/layers/tests/tests/rules.test.ts +320 -0
  30. package/starters/campaign/public/games/campaign/assets.json +1 -0
  31. package/starters/campaign/src/campaign/campaign.json +52 -0
  32. package/starters/campaign/src/campaign/carry.ts +47 -0
  33. package/starters/campaign/src/campaign/cinematics/intro.json +148 -0
  34. package/starters/campaign/src/campaign/cinematics/outro.json +92 -0
  35. package/starters/campaign/src/campaign/data.json +1 -0
  36. package/starters/campaign/src/campaign/fx.json +1 -0
  37. package/starters/campaign/src/campaign/game.ts +6 -0
  38. package/starters/campaign/src/campaign/ids.ts +77 -0
  39. package/starters/campaign/src/campaign/index.ts +121 -0
  40. package/starters/campaign/src/campaign/kills.ts +16 -0
  41. package/starters/campaign/src/campaign/looks.json +1 -0
  42. package/starters/campaign/src/campaign/maps.ts +34 -0
  43. package/starters/campaign/src/campaign/missions/homecoming.ts +160 -0
  44. package/starters/campaign/src/campaign/missions/siege.ts +91 -0
  45. package/starters/campaign/src/campaign/missions/warlord.ts +177 -0
  46. package/starters/campaign/src/campaign/prefabs/watchpost.prefab.json +18 -0
  47. package/starters/campaign/src/campaign/scenes.ts +46 -0
  48. package/starters/campaign/src/campaign/tiles.ts +8 -0
  49. package/starters/campaign/src/campaign/units.ts +390 -0
  50. package/starters/campaign/src/campaign/view/cinematics.ts +59 -0
  51. package/starters/campaign/src/campaign/view/fire.ts +115 -0
  52. package/starters/campaign/src/campaign/view/index.ts +334 -0
  53. package/starters/campaign/src/campaign/view/labels.ts +105 -0
  54. package/starters/campaign/src/campaign/view/rubble.ts +40 -0
  55. package/starters/campaign/src/campaign/view/waveLane.ts +88 -0
  56. package/starters/campaign/src/campaign/waves.json +36 -0
  57. package/starters/campaign/src/campaign/waves.ts +186 -0
  58. package/starters/campaign/src/campaign-editor.ts +43 -0
  59. package/starters/campaign/src/game.ts +2 -0
  60. package/starters/campaign/src/main.ts +70 -0
  61. package/starters/campaign/src/maps/fort.names.ts +31 -0
  62. package/starters/campaign/src/maps/fort.vgmap.json +1162 -0
  63. package/starters/campaign/src/maps/homestead.names.ts +36 -0
  64. package/starters/campaign/src/maps/homestead.vgmap.json +916 -0
  65. package/starters/campaign/src/maps/pass.names.ts +40 -0
  66. package/starters/campaign/src/maps/pass.vgmap.json +522 -0
  67. package/starters/campaign/src/maps.ts +2 -0
  68. package/starters/campaign/src/rules.ts +20 -0
  69. package/starters/campaign/src/waves-panel.ts +355 -0
  70. package/starters/empty/README.md +71 -0
  71. package/starters/empty/layers/tests/tests/rules.test.ts +31 -0
  72. package/starters/empty/src/game.ts +79 -0
  73. package/starters/empty/src/generate.worker.ts +9 -0
  74. package/starters/empty/src/maps.ts +44 -0
  75. package/starters/empty/src/rules.ts +67 -0
  76. package/starters/empty/src/tiles.ts +12 -0
  77. package/starters/melee/README.md +61 -0
  78. package/starters/melee/files.txt +60 -0
  79. package/starters/melee/layers/editor/src/editor.ts +13 -0
  80. package/starters/melee/layers/tests/tests/rules.test.ts +58 -0
  81. package/starters/melee/src/game.ts +2 -0
  82. package/starters/melee/src/maps/bay.names.ts +28 -0
  83. package/starters/melee/src/maps/bay.vgmap.json +3123 -0
  84. package/starters/melee/src/maps.ts +20 -0
  85. package/starters/melee/src/melee/abilities.ts +376 -0
  86. package/starters/melee/src/melee/bay.ts +319 -0
  87. package/starters/melee/src/melee/blight.ts +102 -0
  88. package/starters/melee/src/melee/cellLayers.json +11 -0
  89. package/starters/melee/src/melee/data.json +18 -0
  90. package/starters/melee/src/melee/floats.json +199 -0
  91. package/starters/melee/src/melee/formations.ts +46 -0
  92. package/starters/melee/src/melee/fx.json +1 -0
  93. package/starters/melee/src/melee/game.ts +6 -0
  94. package/starters/melee/src/melee/generator.ts +116 -0
  95. package/starters/melee/src/melee/heroSpells.ts +38 -0
  96. package/starters/melee/src/melee/hud.json +269 -0
  97. package/starters/melee/src/melee/index.ts +182 -0
  98. package/starters/melee/src/melee/items.ts +362 -0
  99. package/starters/melee/src/melee/languages/he.json +96 -0
  100. package/starters/melee/src/melee/looks.json +1 -0
  101. package/starters/melee/src/melee/maps.ts +53 -0
  102. package/starters/melee/src/melee/pings.json +16 -0
  103. package/starters/melee/src/melee/pings.ts +34 -0
  104. package/starters/melee/src/melee/quality.json +1 -0
  105. package/starters/melee/src/melee/saves.ts +12 -0
  106. package/starters/melee/src/melee/sounds.json +1 -0
  107. package/starters/melee/src/melee/undead.ts +683 -0
  108. package/starters/melee/src/melee/units.ts +785 -0
  109. package/starters/melee/src/melee/view/buildings.ts +33 -0
  110. package/starters/melee/src/melee/view/cheerLane.ts +42 -0
  111. package/starters/melee/src/melee/view/heroSpellLooks.ts +21 -0
  112. package/starters/melee/src/melee/view/index.ts +268 -0
  113. package/starters/melee/src/melee/view/languages.ts +10 -0
  114. package/starters/melee/src/melee/view/looks.ts +659 -0
  115. package/starters/melee/src/melee/view/undeadLooks.ts +220 -0
  116. package/starters/melee/src/melee/view/walls.ts +59 -0
  117. package/starters/melee/src/rules.ts +24 -0
  118. package/starters.mjs +35 -0
  119. package/v12.mjs +111 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The rules: what there is, and what happens once a game starts. They use the engine alone, so the page, the server
3
+ * and the tests all play the same game. Start here: a unit type is an entry of `units`
4
+ */
5
+ import { defineIds, type GameMap, type GameModule, hotRules, type IdOf, presetGenerator, type World } from '@v12-engine/engine'
6
+ import { maps } from './maps'
7
+ import { tiles } from './tiles'
8
+
9
+ /** The ids of the game's unit types, named once: `UNIT.scout` everywhere else */
10
+ export const UNIT = defineIds('scout')
11
+ export type Unit = IdOf<typeof UNIT>
12
+
13
+ export const rules: GameModule = {
14
+ // the maps of this game say they are its own by this id: change it, and change theirs (`"game"`) with it
15
+ id: 'my-game',
16
+ name: 'My game',
17
+ units: [
18
+ {
19
+ id: UNIT.scout,
20
+ name: 'Scout',
21
+ kind: 'unit',
22
+ radius: 0.4,
23
+ // no attack yet: give it `attackDamage`, `attackSpeed`, `attackRange` here and an attack in its `attacks`, and it fights
24
+ stats: { health: 100, moveSpeed: 5 },
25
+ sight: 10
26
+ }
27
+ ],
28
+ // what the ground is painted with (src/tiles.ts)
29
+ tiles,
30
+ // @if assets
31
+ /**
32
+ * Its generator of maps: the map editor's File › New map from a generator. The engine's preset (hills, water, ramps,
33
+ * the paint, the starts, trees and rocks) with the shared art's trees and rocks; a game's own passes go in its list
34
+ * (`presetGenerator(...).passes`, or a `MapGenerator` of its own: `terrainPass`, `scatterPass`…)
35
+ */
36
+ generator: presetGenerator({
37
+ trees: ['forest/common_tree_1.glb', 'forest/common_tree_2.glb', 'forest/common_tree_5.glb', 'forest/pine_1.glb', 'forest/pine_3.glb'],
38
+ rocks: ['forest/rock_medium_1.glb', 'forest/rock_medium_2.glb', 'forest/rock_medium_3.glb'],
39
+ // the forest's trees are near 4 high as they are
40
+ treeScale: { scale: 1, vary: 0.2 }
41
+ }),
42
+ // @end
43
+ // @if !assets
44
+ /**
45
+ * Its generator of maps: the map editor's File › New map from a generator. The engine's preset (hills, water, ramps,
46
+ * the paint, the starts); trees and rocks are models of the game's to name (`presetGenerator({ trees: [...] })`)
47
+ */
48
+ generator: presetGenerator(),
49
+ // @end
50
+ // the maps the game ships: a save of a game on one of them keeps only what changed of its ground
51
+ maps: Object.values(maps),
52
+ // nobody wins or loses yet. `coreVictory` (the last side with something wins) or a system of your own decides it
53
+ victory: null
54
+ }
55
+
56
+ /**
57
+ * What the game does once a world is made from a map, before anyone plays: a scout for each player with a start, at
58
+ * it. The page, the server and the editor's Play here each call it with the map
59
+ */
60
+ export function start(world: World, { map }: { map?: GameMap }): void {
61
+ for (const player of map?.players ?? []) {
62
+ if (player.start) world.spawn(UNIT.scout, player.id, player.start)
63
+ }
64
+ }
65
+
66
+ // saved while it runs (npm run dev): the game and the editor open take the new version, with no new page
67
+ if (import.meta.hot) import.meta.hot.accept((module) => hotRules(import.meta.hot, rules, module?.rules))
@@ -0,0 +1,12 @@
1
+ /**
2
+ * What the ground can be painted with: the rules' names for it, which the map editor's Paint offers and the rules read
3
+ * (`world.registry.tile('road')`). How each looks is the game's look's (`tiles` in `src/game.ts`). A new one is a line
4
+ * here and one there; a map keeps its paint by these names, so their order can change
5
+ */
6
+ import { presetTiles } from '@v12-engine/engine'
7
+
8
+ /**
9
+ * The engine's tiles of an RTS (grass, dark grass, dirt, road, sand, rock, snow), drawn with the client's `TILE_LOOKS`.
10
+ * A game's own goes beside them: `defineTile({ id: 'lava', name: 'Lava' })` here, its look in `src/game.ts`
11
+ */
12
+ export const tiles = [...presetTiles]
@@ -0,0 +1,61 @@
1
+ # My game
2
+
3
+ A real-time strategy game made with V12, whole: melee. Two races, the humans and the Restless, each with its workers, halls, army and heroes; gold from mines and lumber from the woods; bases built up, armies trained, the other side's buildings brought down. You play the first player of the Bay, and the computer the others; or a random map, made anew each time.
4
+
5
+ | To | Run |
6
+ | ------- | ---------------------------------------------------------------------------------- |
7
+ | Play it | `npm run dev`, and open the page it says: its start screen, Play (or Random map) |
8
+ <!-- @if editor -->
9
+ | Make maps | `npm run dev`, and open `/?editor`: Save writes them into `src/maps` |
10
+ | Check the maps | `npm run map check`: every map in `src/maps` against the game's rules |
11
+ <!-- @end -->
12
+ <!-- @if server -->
13
+ <!-- @if !rooms -->
14
+ | Play it on a server | `npm run server`, beside `npm run dev`, and open `/?server` |
15
+ <!-- @end -->
16
+ <!-- @if rooms -->
17
+ | Play it online | `npm run server`, beside `npm run dev`, and open `/?find-game` |
18
+ <!-- @end -->
19
+ <!-- @end -->
20
+ <!-- @if tests -->
21
+ | Test the rules | `npm test` |
22
+ <!-- @end -->
23
+ | Check the types | `npm run typecheck` |
24
+ | Build the page to host | `npm run build`, into `dist/` |
25
+
26
+ ## What is where
27
+
28
+ - `src/melee/`: the game. `index.ts` puts the rules together (the engine's packs it turns on: stances, capture points, bushes that hide); `units.ts` the humans, `undead.ts` the Restless, `heroSpells.ts` the spells every hero brings along, `abilities.ts` and `items.ts` what they cast and carry; `blight.ts` the Restless' ground; `generator.ts` the random maps and `bay.ts` the Bay made in code; `maps.ts` what a melee map must have (a town hall for someone, and someone to play against); `view/` how it looks; `data.json`, `looks.json`, `hud.json`, `fx.json`, `floats.json`, `pings.json`, `quality.json`, `cellLayers.json` and `sounds.json` what the editor's panels write
29
+ - `src/rules.ts`, `src/game.ts`, `src/maps.ts`: the game by the names the page, the server and the tests know it by. `start` in `src/rules.ts` seats the computer at every player but you
30
+ - `src/maps/`: the maps, made in the editor. A new one is imported in `src/maps.ts`
31
+ - `src/main.ts`: the page, its scenes made by `appScenes`
32
+
33
+ <!-- @if editor -->
34
+
35
+ - `src/editor.ts`: the map editor's own settings, for `appScenes`: the capture points placed as things of their kind. What it checks of a map is the rules' (`src/melee/maps.ts`)
36
+
37
+ <!-- @end -->
38
+ <!-- @if server -->
39
+ <!-- @if !rooms -->
40
+
41
+ - `server.ts`: a server of the game, one game at a time, `startServer`: the first player's seat for whoever joins, the computer the rest
42
+
43
+ <!-- @end -->
44
+ <!-- @if rooms -->
45
+
46
+ - `server.ts`: a server of many games, `GameRooms`: a room for each map, a random map, and games players make (New game). The page's `/?find-game` is the client's Find a game
47
+
48
+ <!-- @end -->
49
+ <!-- @if ratings -->
50
+
51
+ - `ratings.json` (made by the server): a rating for each player, moved by each game that ends with a winner
52
+
53
+ <!-- @end -->
54
+ <!-- @end -->
55
+ <!-- @if tests -->
56
+
57
+ - `tests/`: the rules tested with no page
58
+
59
+ <!-- @end -->
60
+
61
+ Every model it plays with is of the shared art, `@v12-engine/assets` (whose each is: its group's `CREDITS.md` in `node_modules/@v12-engine/assets/groups/`)
@@ -0,0 +1,60 @@
1
+ # The files melee plays with: what its page asks for in a game on the Bay (the models' levels, lod1 and lod2, as the
2
+ # camera asks, besides). All of them @v12-engine/assets has: the project takes them from it (its groups' CREDITS.md say
3
+ # whose they are). The package's tests check that a project's build has every one
4
+ # the races' workers, soldiers and heroes, with what they hold
5
+ models/characters/barbarian.glb
6
+ models/characters/knight.glb
7
+ models/characters/mage.glb
8
+ models/characters/ranger.glb
9
+ models/characters/rogue_hooded.glb
10
+ models/characters/skeleton_mage.glb
11
+ models/characters/skeleton_minion.glb
12
+ models/characters/skeleton_rogue.glb
13
+ models/characters/skeleton_warrior.glb
14
+ models/gear/axe_1handed.glb
15
+ models/gear/crossbow_2handed.glb
16
+ models/gear/dagger.glb
17
+ models/gear/shield_round.glb
18
+ models/gear/spellbook_open.glb
19
+ models/gear/staff.glb
20
+ models/gear/sword_1handed.glb
21
+ models/gear/sword_2handed.glb
22
+ models/gear/wand.glb
23
+ models/skeleton-gear/axe.glb
24
+ models/skeleton-gear/blade.glb
25
+ models/skeleton-gear/crossbow.glb
26
+ models/skeleton-gear/shield_large_a.glb
27
+ models/skeleton-gear/shield_small_a.glb
28
+ models/skeleton-gear/staff.glb
29
+ # the halls, farms, barracks, towers and the mine
30
+ models/rts/archery_building.glb
31
+ models/rts/archery_towers.glb
32
+ models/rts/barracks.glb
33
+ models/rts/castle.glb
34
+ models/rts/castle_2.glb
35
+ models/rts/castle_fortress.glb
36
+ models/rts/fortress.glb
37
+ models/rts/houses.glb
38
+ models/rts/mine.glb
39
+ models/rts/small_farm.glb
40
+ models/rts/stone_tower.glb
41
+ models/rts/stone_tower_2.glb
42
+ models/rts/storage_house.glb
43
+ models/rts/storage_shed.glb
44
+ models/rts/temple.glb
45
+ models/rts/temple_2.glb
46
+ models/rts/temple_3.glb
47
+ models/rts/tower_house.glb
48
+ models/rts/watch_tower.glb
49
+ models/rts/wooden_fortress.glb
50
+ models/rts/wooden_monument.glb
51
+ models/rts/wooden_temple.glb
52
+ models/village/blacksmith.glb
53
+ models/village/fantasy_sawmill.glb
54
+ # the trees
55
+ models/trees/columnar_1.glb
56
+ models/trees/columnar_2.glb
57
+ models/trees/columnar_5.glb
58
+ models/trees/oak_5.glb
59
+ models/trees/pine_4.glb
60
+ models/trees/pine_5.glb
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The map editor of the game, a scene of the app (Map editor on the start screen, or `/?editor`) that `appScenes` makes
3
+ * (`src/main.ts`): it opens on the Bay, as a file of the project that Save writes back, and Play here tries it in the
4
+ * page's own game. What is set here is the editor's own: what melee checks of a map and says of its players are its
5
+ * rules' (`src/melee/maps.ts`, `meleeEditor`); the capture points of the engine's pack, which melee turns on, are placed
6
+ * as things of their kind
7
+ */
8
+ import type { AppEditorScene } from '@v12-engine/client/app'
9
+ import '@v12-engine/editor/styles.css'
10
+
11
+ // fetched as it first opens: the start screen carries none of it
12
+ export const editor = (scene: AppEditorScene) =>
13
+ import('@v12-engine/editor').then(({ captureKind, editorScene }) => editorScene({ ...scene, kinds: [captureKind] }))
@@ -0,0 +1,58 @@
1
+ /** The rules, tested with no page: a world steps here as it does in a page */
2
+ import { checkRules, createRegistry, createWorldFromMap, generateMap, type World } from '@v12-engine/engine'
3
+ import { describe, expect, it } from 'vitest'
4
+ import { DEFAULT_MAP, mapNamed, maps } from '../src/maps'
5
+ import { rules, start } from '../src/rules'
6
+ import { checkMeleeMap, HALLS } from '../src/melee/maps'
7
+
8
+ /** A game on a map, started as the page starts it: you are the first player, the computer the others */
9
+ async function game(name = DEFAULT_MAP): Promise<World> {
10
+ const map = mapNamed(name)
11
+ const world = await createWorldFromMap({ map, game: rules, seed: 1 })
12
+ start(world, { map, player: map.players[0].id })
13
+ return world
14
+ }
15
+
16
+ describe('the rules', () => {
17
+ it('fit together: every id they name is theirs, every effect has a handler (checkRules)', () => {
18
+ expect(checkRules(rules).filter((problem) => problem.level === 'error')).toEqual([])
19
+ })
20
+
21
+ it('give every player with a start a hall and workers, and seat the computer at all but you', async () => {
22
+ const world = await game()
23
+ const starts = mapNamed().players.filter((player) => player.start)
24
+ for (const player of starts) {
25
+ const own = [...world.index.ownedBy(player.id)]
26
+ expect(
27
+ own.some((entity) => HALLS.has(entity.def.id)),
28
+ `player ${player.id}`
29
+ ).toBe(true)
30
+ expect(own.filter((entity) => entity.def.kind === 'unit').length).toBeGreaterThanOrEqual(5)
31
+ }
32
+ expect(world.computers.list()).toEqual(starts.slice(1).map((player) => player.id))
33
+ world.dispose()
34
+ }, 30_000)
35
+
36
+ it('have the computer gather and build as the game goes', async () => {
37
+ const world = await game()
38
+ const enemy = world.computers.list()[0]
39
+ const gold = world.player(enemy).resources.gold
40
+ for (let i = 0; i < 60 * world.tickRate; i++) world.step()
41
+ expect(world.player(enemy).resources.gold).not.toBe(gold)
42
+ world.dispose()
43
+ }, 30_000)
44
+
45
+ it('read every map, each one melee can be played on', () => {
46
+ for (const [name, map] of Object.entries(maps))
47
+ expect(
48
+ checkMeleeMap(map).filter((problem) => problem.error),
49
+ name
50
+ ).toEqual([])
51
+ })
52
+
53
+ it('make random maps with their generator, each one melee can be played on', () => {
54
+ const { map, problems } = generateMap(rules.generator!, { seed: 3, width: 128, depth: 128, players: 2, symmetry: 'half' }, createRegistry(rules))
55
+ expect(problems.filter((problem) => problem.error)).toEqual([])
56
+ expect(checkMeleeMap(map).filter((problem) => problem.error)).toEqual([])
57
+ })
58
+ })
@@ -0,0 +1,2 @@
1
+ /** The game, whole: melee's rules and how it looks (`src/melee/game.ts`, its view in `src/melee/view`) */
2
+ export { meleeGame as game } from './melee/game'
@@ -0,0 +1,28 @@
1
+ // Written by the map editor with bay.vgmap.json, anew with every save: not to be changed by hand
2
+
3
+ /** What is named on the map, for `world.named<BayNames>(...)` */
4
+ export type BayNames =
5
+ | 'the island alchemist'
6
+ | 'the island tavern'
7
+
8
+ /** The camps that are named on the map, for `campNamed(world, ...)` */
9
+ export type BayCamps =
10
+ | 'the island guard'
11
+
12
+ /** The regions of the map, for `world.areas` and `enteredArea` */
13
+ export type BayRegions = never
14
+
15
+ /** The points of the map, for `world.map.point(...)` */
16
+ export type BayPoints = never
17
+
18
+ /** The paths of the map, for `world.map.path(...)` */
19
+ export type BayPaths = never
20
+
21
+ /** The lights of the map that are named, for `client.lights.named(...)` */
22
+ export type BayLights = never
23
+
24
+ /** The effects of the map that are named, for `client.mapEffects.named(...)` */
25
+ export type BayEffects = never
26
+
27
+ /** The ids of the regions, points and paths by name: what a field that names one keeps (it is handed the name) */
28
+ export const BayIds = { regions: { }, points: { }, paths: { } } as const