@waica/cli 0.3.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/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/cli.js +184 -0
- package/dist/editor/assets/abap-08VXUWAP.js +1 -0
- package/dist/editor/assets/apex-BWPQTe0t.js +1 -0
- package/dist/editor/assets/azcli-Bc_sGQ0U.js +1 -0
- package/dist/editor/assets/bat-i0X4ZdIN.js +1 -0
- package/dist/editor/assets/bicep-B5-_aFwp.js +2 -0
- package/dist/editor/assets/cameligo-DMUM7wLl.js +1 -0
- package/dist/editor/assets/clojure-Cm7r79vr.js +1 -0
- package/dist/editor/assets/codicon-DCmgc-ay.ttf +0 -0
- package/dist/editor/assets/coffee-Ba7i2nA0.js +1 -0
- package/dist/editor/assets/cpp-C7h46wYY.js +1 -0
- package/dist/editor/assets/csharp-BKxtCVv1.js +1 -0
- package/dist/editor/assets/csp-bTuwJoIa.js +1 -0
- package/dist/editor/assets/css-DIMkf-bt.js +3 -0
- package/dist/editor/assets/css.worker-Fwwr_HdT.js +88 -0
- package/dist/editor/assets/cssMode-CQE-WdwL.js +4 -0
- package/dist/editor/assets/cypher-CVaqCwHa.js +1 -0
- package/dist/editor/assets/dart-onAF5SnQ.js +1 -0
- package/dist/editor/assets/dockerfile-DZFCIeNp.js +1 -0
- package/dist/editor/assets/ecl-D05T4iGw.js +1 -0
- package/dist/editor/assets/editor.worker-DsBDrWS5.js +12 -0
- package/dist/editor/assets/elixir-6RTg0lbw.js +1 -0
- package/dist/editor/assets/flow9-C5_-GSwl.js +1 -0
- package/dist/editor/assets/freemarker2-CYgdVabn.js +3 -0
- package/dist/editor/assets/fsharp-C8Ef5oNN.js +1 -0
- package/dist/editor/assets/go-C-y9NEjX.js +1 -0
- package/dist/editor/assets/graphql-fmXr3nnJ.js +1 -0
- package/dist/editor/assets/handlebars-DdeORCJD.js +1 -0
- package/dist/editor/assets/hcl-CpzslTdj.js +1 -0
- package/dist/editor/assets/html-DTIAGV84.js +1 -0
- package/dist/editor/assets/html.worker-B3Ztmmz2.js +495 -0
- package/dist/editor/assets/htmlMode-CRV3iBih.js +4 -0
- package/dist/editor/assets/index-Btzc6zxr.css +1 -0
- package/dist/editor/assets/index-Dhkqk31v.js +11016 -0
- package/dist/editor/assets/ini-sBoK_t0W.js +1 -0
- package/dist/editor/assets/java-BEtHBSE6.js +1 -0
- package/dist/editor/assets/javascript-dxBW86cl.js +1 -0
- package/dist/editor/assets/json.worker-PWQxM7ZO.js +51 -0
- package/dist/editor/assets/jsonMode-DFA5-FFK.js +10 -0
- package/dist/editor/assets/julia-Bri6UV-V.js +1 -0
- package/dist/editor/assets/kotlin-B0qVs-E9.js +1 -0
- package/dist/editor/assets/less-B9JPFI3C.js +2 -0
- package/dist/editor/assets/lexon-CfSJPG6W.js +1 -0
- package/dist/editor/assets/liquid-CScWaXYV.js +1 -0
- package/dist/editor/assets/lua-CsQS60Ue.js +1 -0
- package/dist/editor/assets/m3-D-oSqn_W.js +1 -0
- package/dist/editor/assets/markdown-Cimd5fb3.js +1 -0
- package/dist/editor/assets/mdx-Bvp84SqA.js +1 -0
- package/dist/editor/assets/mips-CIPQ_RoX.js +1 -0
- package/dist/editor/assets/msdax-DauUninz.js +1 -0
- package/dist/editor/assets/mysql-SOo6toE5.js +1 -0
- package/dist/editor/assets/objective-c-FvmIjYaQ.js +1 -0
- package/dist/editor/assets/pascal-DrH0SRf2.js +1 -0
- package/dist/editor/assets/pascaligo-D-ptJ9y-.js +1 -0
- package/dist/editor/assets/perl-oz_6vUea.js +1 -0
- package/dist/editor/assets/pgsql-DTj74zXo.js +1 -0
- package/dist/editor/assets/php-nr791fC2.js +1 -0
- package/dist/editor/assets/pla-CopQ2nXW.js +1 -0
- package/dist/editor/assets/postiats-43DmfD33.js +1 -0
- package/dist/editor/assets/powerquery-D3hlyOfw.js +1 -0
- package/dist/editor/assets/powershell-DmHpPYUd.js +1 -0
- package/dist/editor/assets/protobuf-C531GsRP.js +2 -0
- package/dist/editor/assets/pug-Z5eAx3Zn.js +1 -0
- package/dist/editor/assets/python-CG-OtvFa.js +1 -0
- package/dist/editor/assets/qsharp-DkqhCAOL.js +1 -0
- package/dist/editor/assets/r-BwWrilGY.js +1 -0
- package/dist/editor/assets/razor-r-iUEZPj.js +1 -0
- package/dist/editor/assets/redis-ClamHrr6.js +1 -0
- package/dist/editor/assets/redshift-DT7zqm-g.js +1 -0
- package/dist/editor/assets/restructuredtext-BYgofb2h.js +1 -0
- package/dist/editor/assets/ruby-DezsRK8O.js +1 -0
- package/dist/editor/assets/rust-DdL9SqIa.js +1 -0
- package/dist/editor/assets/sb-CcwsVR0C.js +1 -0
- package/dist/editor/assets/scala-DHpiXF5c.js +1 -0
- package/dist/editor/assets/scheme-BeGwcela.js +1 -0
- package/dist/editor/assets/scss-gp-XZpBa.js +3 -0
- package/dist/editor/assets/shell-CC2rA5mh.js +1 -0
- package/dist/editor/assets/solidity-BEEn4gHE.js +1 -0
- package/dist/editor/assets/sophia-CRfGWb83.js +1 -0
- package/dist/editor/assets/sparql-D_Lu-MrJ.js +1 -0
- package/dist/editor/assets/sql-NEE52Syq.js +1 -0
- package/dist/editor/assets/st-DbInun42.js +1 -0
- package/dist/editor/assets/swift-Bxkupp3x.js +1 -0
- package/dist/editor/assets/systemverilog-Bz4Y3fRF.js +1 -0
- package/dist/editor/assets/tcl-DISqw1ZD.js +1 -0
- package/dist/editor/assets/ts.worker-0mEyHVru.js +51339 -0
- package/dist/editor/assets/tsMode-Bth1rJwM.js +11 -0
- package/dist/editor/assets/twig-De2hgUGE.js +1 -0
- package/dist/editor/assets/typescript-tNRuHsoe.js +1 -0
- package/dist/editor/assets/typespec-B8J7ngcE.js +1 -0
- package/dist/editor/assets/vb-DV3o63ZY.js +1 -0
- package/dist/editor/assets/wgsl-DpFanUEy.js +298 -0
- package/dist/editor/assets/xml-BCx9e6CK.js +1 -0
- package/dist/editor/assets/yaml-Bb-peGP_.js +1 -0
- package/dist/editor/index.html +14 -0
- package/dist/mcp/archetypes.js +103 -0
- package/dist/mcp/cli.js +6 -0
- package/dist/mcp/create-project.js +151 -0
- package/dist/mcp/introspection.js +201 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/assets/waica-coin.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/assets/waica-dog.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/assets/waica-slime.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/art.d.ts +10 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/art.js +13 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/bundle.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/bundle.js +10 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/controls.d.ts +5 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/controls.js +12 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/index.d.ts +38 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/index.js +34 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/manifest.d.ts +15 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/manifest.js +26 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/prefabs.d.ts +7 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/prefabs.js +97 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry-data.d.ts +5 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry-data.js +45 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry.d.ts +6 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry.js +15 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/scene-default.d.ts +40 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/scene-default.js +93 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/ui.d.ts +10 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/ui.js +24 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/package.json +30 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/chaser.d.ts +59 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/chaser.js +127 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/collectible.d.ts +25 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/collectible.js +27 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/hazard.d.ts +29 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/hazard.js +42 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.d.ts +9 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.js +9 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/lifetime.d.ts +16 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/lifetime.js +19 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/npc.d.ts +4 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/npc.js +14 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.d.ts +43 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.js +68 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.d.ts +124 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.js +146 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/player-identity.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/player-identity.js +5 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/player-states.d.ts +18 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/player-states.js +95 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/respawnable.d.ts +23 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/respawnable.js +27 -0
- package/dist/mcp/node_modules/@waica/behaviors/package.json +24 -0
- package/dist/mcp/node_modules/@waica/engine/dist/aabb.d.ts +2 -0
- package/dist/mcp/node_modules/@waica/engine/dist/aabb.js +4 -0
- package/dist/mcp/node_modules/@waica/engine/dist/animation/clip-player.d.ts +20 -0
- package/dist/mcp/node_modules/@waica/engine/dist/animation/clip-player.js +24 -0
- package/dist/mcp/node_modules/@waica/engine/dist/animation/contract.d.ts +21 -0
- package/dist/mcp/node_modules/@waica/engine/dist/animation/contract.js +23 -0
- package/dist/mcp/node_modules/@waica/engine/dist/animation/sheet.d.ts +65 -0
- package/dist/mcp/node_modules/@waica/engine/dist/animation/sheet.js +44 -0
- package/dist/mcp/node_modules/@waica/engine/dist/archetype.d.ts +37 -0
- package/dist/mcp/node_modules/@waica/engine/dist/archetype.js +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/camera.d.ts +71 -0
- package/dist/mcp/node_modules/@waica/engine/dist/camera.js +59 -0
- package/dist/mcp/node_modules/@waica/engine/dist/collision-shape.d.ts +26 -0
- package/dist/mcp/node_modules/@waica/engine/dist/collision-shape.js +136 -0
- package/dist/mcp/node_modules/@waica/engine/dist/component-registry.d.ts +16 -0
- package/dist/mcp/node_modules/@waica/engine/dist/component-registry.js +44 -0
- package/dist/mcp/node_modules/@waica/engine/dist/component.d.ts +58 -0
- package/dist/mcp/node_modules/@waica/engine/dist/component.js +11 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.d.ts +80 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.js +210 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/dynamic-body.d.ts +68 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/dynamic-body.js +189 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/hitbox.d.ts +25 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/hitbox.js +21 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/solid.d.ts +32 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/solid.js +45 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.d.ts +51 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.js +112 -0
- package/dist/mcp/node_modules/@waica/engine/dist/entity.d.ts +22 -0
- package/dist/mcp/node_modules/@waica/engine/dist/entity.js +52 -0
- package/dist/mcp/node_modules/@waica/engine/dist/events.d.ts +8 -0
- package/dist/mcp/node_modules/@waica/engine/dist/events.js +20 -0
- package/dist/mcp/node_modules/@waica/engine/dist/game.d.ts +95 -0
- package/dist/mcp/node_modules/@waica/engine/dist/game.js +263 -0
- package/dist/mcp/node_modules/@waica/engine/dist/index.d.ts +39 -0
- package/dist/mcp/node_modules/@waica/engine/dist/index.js +26 -0
- package/dist/mcp/node_modules/@waica/engine/dist/input.d.ts +40 -0
- package/dist/mcp/node_modules/@waica/engine/dist/input.js +84 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene.d.ts +80 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene.js +93 -0
- package/dist/mcp/node_modules/@waica/engine/dist/solid-axis.d.ts +21 -0
- package/dist/mcp/node_modules/@waica/engine/dist/solid-axis.js +77 -0
- package/dist/mcp/node_modules/@waica/engine/dist/state/hooks.d.ts +90 -0
- package/dist/mcp/node_modules/@waica/engine/dist/state/hooks.js +81 -0
- package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.d.ts +83 -0
- package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.js +173 -0
- package/dist/mcp/node_modules/@waica/engine/dist/stats.d.ts +27 -0
- package/dist/mcp/node_modules/@waica/engine/dist/stats.js +54 -0
- package/dist/mcp/node_modules/@waica/engine/dist/ui.d.ts +47 -0
- package/dist/mcp/node_modules/@waica/engine/dist/ui.js +175 -0
- package/dist/mcp/node_modules/@waica/engine/package.json +25 -0
- package/dist/mcp/package-resolver.js +240 -0
- package/dist/mcp/project-path.js +86 -0
- package/dist/mcp/scaffolds.js +153 -0
- package/dist/mcp/server.js +217 -0
- package/dist/mcp/stdio.js +13 -0
- package/dist/mcp/template/README.md +22 -0
- package/dist/mcp/template/_gitignore +5 -0
- package/dist/mcp/template/index.html +25 -0
- package/dist/mcp/template/package.json.tpl +20 -0
- package/dist/mcp/template/public/waica.params.json +1 -0
- package/dist/mcp/template/src/art/waica-coin.png +0 -0
- package/dist/mcp/template/src/art/waica-dog.png +0 -0
- package/dist/mcp/template/src/art/waica-slime.png +0 -0
- package/dist/mcp/template/src/characters/player.character.json +133 -0
- package/dist/mcp/template/src/characters/slime.character.json +61 -0
- package/dist/mcp/template/src/controls.json +8 -0
- package/dist/mcp/template/src/game.json +10 -0
- package/dist/mcp/template/src/main.ts +105 -0
- package/dist/mcp/template/src/objects/coin.object.json +41 -0
- package/dist/mcp/template/src/scenes/main.scene.json +217 -0
- package/dist/mcp/template/src/stats.json +7 -0
- package/dist/mcp/template/src/tiles/block.tile.json +21 -0
- package/dist/mcp/template/src/tiles/decor.tile.json +14 -0
- package/dist/mcp/template/src/tiles/platform.tile.json +21 -0
- package/dist/mcp/template/src/ui/coin-counter.html +12 -0
- package/dist/mcp/template/tsconfig.json +15 -0
- package/dist/mcp/template/vite.config.ts +3 -0
- package/dist/mcp/validation.js +323 -0
- package/dist/mcp/workspace-loader.js +14 -0
- package/dist/mcp/workspace-runtime.js +109 -0
- package/dist/server.js +210 -0
- package/package.json +38 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AnimatedSprite, DynamicBody, Hitbox, Solid, Sprite, StateMachine, } from '@waica/engine';
|
|
2
|
+
import { Chaser, Collectible, Hazard, Lifetime, Patrol, PlatformerMotor, Respawnable, } from '@waica/behaviors';
|
|
3
|
+
import { PLATFORMER_ART } from './art.js';
|
|
4
|
+
import { PLATFORMER_PREFABS } from './prefabs.js';
|
|
5
|
+
import { PLATFORMER_UI } from './ui.js';
|
|
6
|
+
const PACKAGE_ASSETS = Object.fromEntries(PLATFORMER_ART.map((art) => [art.uri, `assets/${art.file}`]));
|
|
7
|
+
/** Asset-import-free registry. Browser URL resolution is layered on separately. */
|
|
8
|
+
export const PLATFORMER_REGISTRY_DATA = {
|
|
9
|
+
components: {
|
|
10
|
+
Sprite,
|
|
11
|
+
AnimatedSprite,
|
|
12
|
+
Solid,
|
|
13
|
+
Hitbox,
|
|
14
|
+
DynamicBody,
|
|
15
|
+
StateMachine,
|
|
16
|
+
PlatformerMotor,
|
|
17
|
+
Collectible,
|
|
18
|
+
Patrol,
|
|
19
|
+
Chaser,
|
|
20
|
+
Hazard,
|
|
21
|
+
Respawnable,
|
|
22
|
+
Lifetime,
|
|
23
|
+
},
|
|
24
|
+
resolveAsset: (uri) => PACKAGE_ASSETS[uri] ?? uri,
|
|
25
|
+
prefabs: PLATFORMER_PREFABS,
|
|
26
|
+
ui: PLATFORMER_UI,
|
|
27
|
+
};
|
|
28
|
+
const PALETTE_ICONS = {
|
|
29
|
+
player: '🐕',
|
|
30
|
+
slime: '👾',
|
|
31
|
+
coin: '🪙',
|
|
32
|
+
platform: '▬',
|
|
33
|
+
block: '■',
|
|
34
|
+
decor: '▢',
|
|
35
|
+
};
|
|
36
|
+
/** The editor palette: pieces you can drag into the viewport, one per prefab. */
|
|
37
|
+
export const PLATFORMER_PALETTE = Object.entries(PLATFORMER_PREFABS).map(([key, prefab]) => {
|
|
38
|
+
const base = key.slice(key.indexOf('/') + 1);
|
|
39
|
+
return {
|
|
40
|
+
label: base,
|
|
41
|
+
icon: PALETTE_ICONS[base] ?? '▣',
|
|
42
|
+
category: prefab.type,
|
|
43
|
+
make: () => ({ name: base.charAt(0).toUpperCase() + base.slice(1), prefab: key }),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SceneRegistry } from '@waica/engine';
|
|
2
|
+
export { PLATFORMER_PALETTE } from './registry-data.js';
|
|
3
|
+
/** Browser URL per stock-art file; Node resolves these to packed file URLs. */
|
|
4
|
+
export declare const PLATFORMER_ART_URLS: Record<string, string>;
|
|
5
|
+
/** Browser registry with bundled asset resolution layered over the shared registry. */
|
|
6
|
+
export declare const PLATFORMER_REGISTRY: SceneRegistry;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PLATFORMER_ART } from './art.js';
|
|
2
|
+
import { PLATFORMER_REGISTRY_DATA } from './registry-data.js';
|
|
3
|
+
export { PLATFORMER_PALETTE } from './registry-data.js';
|
|
4
|
+
/** Browser URL per stock-art file; Node resolves these to packed file URLs. */
|
|
5
|
+
export const PLATFORMER_ART_URLS = {
|
|
6
|
+
'waica-dog.png': new URL('../assets/waica-dog.png', import.meta.url).href,
|
|
7
|
+
'waica-coin.png': new URL('../assets/waica-coin.png', import.meta.url).href,
|
|
8
|
+
'waica-slime.png': new URL('../assets/waica-slime.png', import.meta.url).href,
|
|
9
|
+
};
|
|
10
|
+
const BUILTIN_ASSETS = Object.fromEntries(PLATFORMER_ART.map((art) => [art.uri, PLATFORMER_ART_URLS[art.file] ?? art.uri]));
|
|
11
|
+
/** Browser registry with bundled asset resolution layered over the shared registry. */
|
|
12
|
+
export const PLATFORMER_REGISTRY = {
|
|
13
|
+
...PLATFORMER_REGISTRY_DATA,
|
|
14
|
+
resolveAsset: (uri) => BUILTIN_ASSETS[uri] ?? uri,
|
|
15
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SceneJson } from '@waica/engine';
|
|
2
|
+
/** Clips matching the platformer state graph, applied to the placeholder dog. */
|
|
3
|
+
export declare const DOG_SPRITE: {
|
|
4
|
+
texture: string;
|
|
5
|
+
cols: number;
|
|
6
|
+
rows: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
clips: {
|
|
10
|
+
idle: {
|
|
11
|
+
frames: number[];
|
|
12
|
+
fps: number;
|
|
13
|
+
};
|
|
14
|
+
run: {
|
|
15
|
+
frames: number[];
|
|
16
|
+
fps: number;
|
|
17
|
+
};
|
|
18
|
+
jump: {
|
|
19
|
+
frames: number[];
|
|
20
|
+
fps: number;
|
|
21
|
+
loop: boolean;
|
|
22
|
+
};
|
|
23
|
+
fall: {
|
|
24
|
+
frames: number[];
|
|
25
|
+
fps: number;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
initialClip: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The platformer archetype's starting scene, as data: prefab refs plus the
|
|
32
|
+
* per-entity overrides that differ from the prefab. This is what the wizard
|
|
33
|
+
* copies into `src/scenes/main.scene.json` and what the editor edits.
|
|
34
|
+
*/
|
|
35
|
+
export declare const PLATFORMER_SCENE: SceneJson;
|
|
36
|
+
/**
|
|
37
|
+
* The blank-start variant: same camera framing, zero entities. No `follow`
|
|
38
|
+
* because there is no Player yet — the editor's palette places the first one.
|
|
39
|
+
*/
|
|
40
|
+
export declare const PLATFORMER_BLANK_SCENE: SceneJson;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Clips matching the platformer state graph, applied to the placeholder dog. */
|
|
2
|
+
export const DOG_SPRITE = {
|
|
3
|
+
texture: 'waica:dog',
|
|
4
|
+
cols: 4,
|
|
5
|
+
rows: 4,
|
|
6
|
+
width: 1.4,
|
|
7
|
+
height: 1.4,
|
|
8
|
+
clips: {
|
|
9
|
+
idle: { frames: [0, 1, 2, 3], fps: 5 },
|
|
10
|
+
run: { frames: [4, 5, 6, 7], fps: 10 },
|
|
11
|
+
jump: { frames: [8, 9], fps: 8, loop: false },
|
|
12
|
+
fall: { frames: [12, 13], fps: 8 },
|
|
13
|
+
},
|
|
14
|
+
initialClip: 'idle',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The platformer archetype's starting scene, as data: prefab refs plus the
|
|
18
|
+
* per-entity overrides that differ from the prefab. This is what the wizard
|
|
19
|
+
* copies into `src/scenes/main.scene.json` and what the editor edits.
|
|
20
|
+
*/
|
|
21
|
+
export const PLATFORMER_SCENE = {
|
|
22
|
+
waicaScene: 3,
|
|
23
|
+
camera: { position: [0, -1], zoom: 12, follow: 'Player' },
|
|
24
|
+
entities: [
|
|
25
|
+
{ name: 'Player', prefab: 'characters/player', position: [0, -1] },
|
|
26
|
+
{
|
|
27
|
+
name: 'Ground-A',
|
|
28
|
+
prefab: 'tiles/platform',
|
|
29
|
+
position: [-6, -5],
|
|
30
|
+
overrides: { Sprite: { width: 16, height: 2 }, Solid: { width: 16, height: 2 } },
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'Ground-B',
|
|
34
|
+
prefab: 'tiles/platform',
|
|
35
|
+
position: [16, -5],
|
|
36
|
+
overrides: { Sprite: { width: 16, height: 2 }, Solid: { width: 16, height: 2 } },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Platform-1',
|
|
40
|
+
prefab: 'tiles/platform',
|
|
41
|
+
position: [5, -2.5],
|
|
42
|
+
overrides: { Sprite: { width: 4 }, Solid: { width: 4 } },
|
|
43
|
+
},
|
|
44
|
+
{ name: 'Platform-2', prefab: 'tiles/platform', position: [9.5, -0.5] },
|
|
45
|
+
{ name: 'Platform-3', prefab: 'tiles/platform', position: [-5.5, -1.5] },
|
|
46
|
+
{
|
|
47
|
+
name: 'Platform-4',
|
|
48
|
+
prefab: 'tiles/block',
|
|
49
|
+
position: [14, 1.5],
|
|
50
|
+
overrides: { Sprite: { width: 3, height: 0.5 }, Solid: { width: 3, height: 0.5 } },
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'Wall-Left',
|
|
54
|
+
prefab: 'tiles/block',
|
|
55
|
+
position: [-15.5, 0],
|
|
56
|
+
overrides: { Sprite: { height: 12 }, Solid: { height: 12 } },
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Wall-Right',
|
|
60
|
+
prefab: 'tiles/block',
|
|
61
|
+
position: [25.5, 0],
|
|
62
|
+
overrides: { Sprite: { height: 12 }, Solid: { height: 12 } },
|
|
63
|
+
},
|
|
64
|
+
{ name: 'Coin-1', prefab: 'objects/coin', position: [-5.5, -0.5] },
|
|
65
|
+
{ name: 'Coin-2', prefab: 'objects/coin', position: [-2, -3.2] },
|
|
66
|
+
{ name: 'Coin-3', prefab: 'objects/coin', position: [5, -1.7] },
|
|
67
|
+
{ name: 'Coin-4', prefab: 'objects/coin', position: [9.5, 0.3] },
|
|
68
|
+
{ name: 'Coin-5', prefab: 'objects/coin', position: [14, 2.3] },
|
|
69
|
+
{ name: 'Coin-6', prefab: 'objects/coin', position: [19, -3.2] },
|
|
70
|
+
{
|
|
71
|
+
name: 'Slime-1',
|
|
72
|
+
prefab: 'characters/slime',
|
|
73
|
+
position: [-9, -3.55],
|
|
74
|
+
overrides: { Patrol: { speed: 1.5 } },
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'Slime-2',
|
|
78
|
+
prefab: 'characters/slime',
|
|
79
|
+
position: [17, -3.55],
|
|
80
|
+
overrides: { Patrol: { distance: 2.5, speed: 2.5 } },
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
ui: ['coin-counter'],
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The blank-start variant: same camera framing, zero entities. No `follow`
|
|
87
|
+
* because there is no Player yet — the editor's palette places the first one.
|
|
88
|
+
*/
|
|
89
|
+
export const PLATFORMER_BLANK_SCENE = {
|
|
90
|
+
waicaScene: 3,
|
|
91
|
+
camera: { position: [0, -1], zoom: 12 },
|
|
92
|
+
entities: [],
|
|
93
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The archetype's default UI pieces: self-contained HTML fragments
|
|
3
|
+
* (markup + <style>, {{stat}} bindings) keyed by piece name. Projects
|
|
4
|
+
* override them with src/ui/<name>.html files; scenes list the pieces
|
|
5
|
+
* they start with in their "ui" field.
|
|
6
|
+
*
|
|
7
|
+
* TS template literals (not .html files) because this package builds
|
|
8
|
+
* with plain tsc — no bundler to inline raw imports.
|
|
9
|
+
*/
|
|
10
|
+
export declare const PLATFORMER_UI: Record<string, string>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The archetype's default UI pieces: self-contained HTML fragments
|
|
3
|
+
* (markup + <style>, {{stat}} bindings) keyed by piece name. Projects
|
|
4
|
+
* override them with src/ui/<name>.html files; scenes list the pieces
|
|
5
|
+
* they start with in their "ui" field.
|
|
6
|
+
*
|
|
7
|
+
* TS template literals (not .html files) because this package builds
|
|
8
|
+
* with plain tsc — no bundler to inline raw imports.
|
|
9
|
+
*/
|
|
10
|
+
export const PLATFORMER_UI = {
|
|
11
|
+
'coin-counter': `<style>
|
|
12
|
+
.coin-counter {
|
|
13
|
+
position: absolute;
|
|
14
|
+
top: 12px;
|
|
15
|
+
left: 12px;
|
|
16
|
+
font: 600 20px system-ui, sans-serif;
|
|
17
|
+
color: #ffd166;
|
|
18
|
+
text-shadow: 0 1px 3px #000a;
|
|
19
|
+
user-select: none;
|
|
20
|
+
}
|
|
21
|
+
</style>
|
|
22
|
+
<div class="coin-counter">🪙 {{points}}</div>
|
|
23
|
+
`,
|
|
24
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@waica/archetype-platformer",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Waica platformer archetype — opinionated setup for side-scrolling platformers",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/chichex/waica",
|
|
10
|
+
"directory": "packages/archetype-platformer"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"assets"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./manifest": {
|
|
22
|
+
"types": "./dist/manifest.d.ts",
|
|
23
|
+
"default": "./dist/manifest.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@waica/behaviors": "^0.3.0",
|
|
28
|
+
"@waica/engine": "^0.3.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Component, type RoleDefinition, type RoleGraph } from '@waica/engine';
|
|
2
|
+
export type ChaserMode = 'walker' | 'ghost' | 'flyer';
|
|
3
|
+
/**
|
|
4
|
+
* Pursuit toward the player while they are within sight range, in one of
|
|
5
|
+
* three modes: a walker paces the ground (X only, gravity, blocked by
|
|
6
|
+
* Solids), a ghost floats straight at them through everything, a flyer
|
|
7
|
+
* floats straight at them but walls and ceilings stop it.
|
|
8
|
+
* Passive like a motor: no onUpdate of its own — the 'chaser' logic set's
|
|
9
|
+
* chase state calls step(dt), so the StateMachine stays the single owner
|
|
10
|
+
* of the frame.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Chaser extends Component {
|
|
13
|
+
static componentName: string;
|
|
14
|
+
static params: {
|
|
15
|
+
mode: {
|
|
16
|
+
label: string;
|
|
17
|
+
options: string[];
|
|
18
|
+
};
|
|
19
|
+
range: {
|
|
20
|
+
label: string;
|
|
21
|
+
min: number;
|
|
22
|
+
max: number;
|
|
23
|
+
step: number;
|
|
24
|
+
};
|
|
25
|
+
speed: {
|
|
26
|
+
label: string;
|
|
27
|
+
min: number;
|
|
28
|
+
max: number;
|
|
29
|
+
step: number;
|
|
30
|
+
};
|
|
31
|
+
gravity: {
|
|
32
|
+
label: string;
|
|
33
|
+
min: number;
|
|
34
|
+
max: number;
|
|
35
|
+
step: number;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
mode: ChaserMode;
|
|
39
|
+
range: number;
|
|
40
|
+
speed: number;
|
|
41
|
+
gravity: number;
|
|
42
|
+
private vy;
|
|
43
|
+
private target;
|
|
44
|
+
/** The player is the live entity whose StateMachine declares that role. */
|
|
45
|
+
private findTarget;
|
|
46
|
+
/** One chase step in the current mode, flipping the sprite toward prey. */
|
|
47
|
+
step(dt: number): void;
|
|
48
|
+
/** Ground pursuit: chase on X when in sight; gravity and Solids always rule. */
|
|
49
|
+
private stepWalker;
|
|
50
|
+
/** Straight-line pursuit; the flyer sweeps against Solids, the ghost doesn't. */
|
|
51
|
+
private stepAirborne;
|
|
52
|
+
/** Resolves this mode's body through the engine's shared Solid solver. */
|
|
53
|
+
private resolveAxis;
|
|
54
|
+
/** AABB sized by the sibling Hitbox (chassis default if none). */
|
|
55
|
+
private collisionBody;
|
|
56
|
+
}
|
|
57
|
+
/** The state graph new chasing characters start with, as prefab data. */
|
|
58
|
+
export declare const CHASER_STATE_GRAPH: RoleGraph;
|
|
59
|
+
export declare const CHASER_ROLE: RoleDefinition;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Component, Hitbox, resolveSolidAxis, } from '@waica/engine';
|
|
2
|
+
import { isPlayer } from './player-identity.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pursuit toward the player while they are within sight range, in one of
|
|
5
|
+
* three modes: a walker paces the ground (X only, gravity, blocked by
|
|
6
|
+
* Solids), a ghost floats straight at them through everything, a flyer
|
|
7
|
+
* floats straight at them but walls and ceilings stop it.
|
|
8
|
+
* Passive like a motor: no onUpdate of its own — the 'chaser' logic set's
|
|
9
|
+
* chase state calls step(dt), so the StateMachine stays the single owner
|
|
10
|
+
* of the frame.
|
|
11
|
+
*/
|
|
12
|
+
export class Chaser extends Component {
|
|
13
|
+
static componentName = 'Chaser';
|
|
14
|
+
static params = {
|
|
15
|
+
mode: { label: 'Mode', options: ['walker', 'ghost', 'flyer'] },
|
|
16
|
+
range: { label: 'Sight range', min: 1, max: 30, step: 0.5 },
|
|
17
|
+
speed: { label: 'Speed', min: 0.5, max: 15, step: 0.5 },
|
|
18
|
+
gravity: { label: 'Gravity (walker)', min: 5, max: 120, step: 1 },
|
|
19
|
+
};
|
|
20
|
+
mode = 'walker';
|
|
21
|
+
range = 6;
|
|
22
|
+
speed = 3;
|
|
23
|
+
gravity = 42;
|
|
24
|
+
vy = 0;
|
|
25
|
+
target;
|
|
26
|
+
/** The player is the live entity whose StateMachine declares that role. */
|
|
27
|
+
findTarget() {
|
|
28
|
+
if (!this.target?.alive || !isPlayer(this.target)) {
|
|
29
|
+
this.target = this.entity.game.entities.find(isPlayer);
|
|
30
|
+
}
|
|
31
|
+
return this.target;
|
|
32
|
+
}
|
|
33
|
+
/** One chase step in the current mode, flipping the sprite toward prey. */
|
|
34
|
+
step(dt) {
|
|
35
|
+
if (this.mode === 'walker')
|
|
36
|
+
this.stepWalker(dt);
|
|
37
|
+
else
|
|
38
|
+
this.stepAirborne(dt);
|
|
39
|
+
}
|
|
40
|
+
/** Ground pursuit: chase on X when in sight; gravity and Solids always rule. */
|
|
41
|
+
stepWalker(dt) {
|
|
42
|
+
const pos = this.entity.position;
|
|
43
|
+
const target = this.findTarget();
|
|
44
|
+
const dx = target ? target.position.x - pos.x : 0;
|
|
45
|
+
if (target && Math.abs(dx) <= this.range && Math.abs(dx) > 0.05) {
|
|
46
|
+
const dir = dx > 0 ? 1 : -1;
|
|
47
|
+
const previous = pos.x;
|
|
48
|
+
pos.x += dir * Math.min(this.speed * dt, Math.abs(dx));
|
|
49
|
+
this.resolveAxis('x', previous);
|
|
50
|
+
this.entity.scale.x = dir;
|
|
51
|
+
}
|
|
52
|
+
// Gravity applies in and out of sight — a walker spawned midair lands.
|
|
53
|
+
this.vy = Math.max(this.vy - this.gravity * dt, -22);
|
|
54
|
+
const previous = pos.y;
|
|
55
|
+
pos.y += this.vy * dt;
|
|
56
|
+
this.resolveAxis('y', previous);
|
|
57
|
+
}
|
|
58
|
+
/** Straight-line pursuit; the flyer sweeps against Solids, the ghost doesn't. */
|
|
59
|
+
stepAirborne(dt) {
|
|
60
|
+
const pos = this.entity.position;
|
|
61
|
+
const target = this.findTarget();
|
|
62
|
+
if (!target)
|
|
63
|
+
return;
|
|
64
|
+
const dx = target.position.x - pos.x;
|
|
65
|
+
const dy = target.position.y - pos.y;
|
|
66
|
+
const dist = Math.hypot(dx, dy);
|
|
67
|
+
// The inner deadband keeps it from jittering on top of the player.
|
|
68
|
+
if (dist > this.range || dist < 0.1)
|
|
69
|
+
return;
|
|
70
|
+
const move = Math.min(this.speed * dt, dist);
|
|
71
|
+
const solid = this.mode === 'flyer';
|
|
72
|
+
const previousX = pos.x;
|
|
73
|
+
pos.x += (dx / dist) * move;
|
|
74
|
+
if (solid)
|
|
75
|
+
this.resolveAxis('x', previousX);
|
|
76
|
+
const previousY = pos.y;
|
|
77
|
+
pos.y += (dy / dist) * move;
|
|
78
|
+
if (solid)
|
|
79
|
+
this.resolveAxis('y', previousY);
|
|
80
|
+
if (Math.abs(dx) > 0.05)
|
|
81
|
+
this.entity.scale.x = dx > 0 ? 1 : -1;
|
|
82
|
+
}
|
|
83
|
+
/** Resolves this mode's body through the engine's shared Solid solver. */
|
|
84
|
+
resolveAxis(axis, previous) {
|
|
85
|
+
const collided = resolveSolidAxis({
|
|
86
|
+
entity: this.entity,
|
|
87
|
+
axis,
|
|
88
|
+
previous,
|
|
89
|
+
body: () => this.collisionBody(),
|
|
90
|
+
});
|
|
91
|
+
if (collided && axis === 'y')
|
|
92
|
+
this.vy = 0;
|
|
93
|
+
}
|
|
94
|
+
/** AABB sized by the sibling Hitbox (chassis default if none). */
|
|
95
|
+
collisionBody() {
|
|
96
|
+
const box = this.entity.get(Hitbox);
|
|
97
|
+
return {
|
|
98
|
+
x: this.entity.position.x + (box?.offsetX ?? 0),
|
|
99
|
+
y: this.entity.position.y + (box?.offsetY ?? 0),
|
|
100
|
+
width: box?.width ?? 0.9,
|
|
101
|
+
height: box?.height ?? 0.95,
|
|
102
|
+
shape: 'rectangle',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** The state graph new chasing characters start with, as prefab data. */
|
|
107
|
+
export const CHASER_STATE_GRAPH = {
|
|
108
|
+
initial: 'chase',
|
|
109
|
+
states: { chase: {} },
|
|
110
|
+
};
|
|
111
|
+
// The pursuer role. One state out of the box; give chasing characters more
|
|
112
|
+
// states by registering on top (defineStates('chaser', { flee: {...} }))
|
|
113
|
+
// plus prefab data.
|
|
114
|
+
export const CHASER_ROLE = {
|
|
115
|
+
description: 'Hunts the player when they come within sight range. Its Mode picks ' +
|
|
116
|
+
'the body: a walker paces the ground with gravity, a ghost floats ' +
|
|
117
|
+
'through walls, a flyer floats but walls stop it. Its states move Chaser.',
|
|
118
|
+
driver: 'Chaser',
|
|
119
|
+
graph: CHASER_STATE_GRAPH,
|
|
120
|
+
states: {
|
|
121
|
+
chase: {
|
|
122
|
+
onUpdate({ entity }, dt) {
|
|
123
|
+
entity.get(Chaser)?.step(dt);
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Component, type Entity } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* Collected when the entity with the player role touches
|
|
4
|
+
* it: adds its value to a stat, fires onCollect and destroys itself.
|
|
5
|
+
* Requires Hitbox on both entities.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Collectible extends Component {
|
|
8
|
+
static componentName: string;
|
|
9
|
+
static params: {
|
|
10
|
+
value: {
|
|
11
|
+
label: string;
|
|
12
|
+
min: number;
|
|
13
|
+
max: number;
|
|
14
|
+
step: number;
|
|
15
|
+
};
|
|
16
|
+
stat: {
|
|
17
|
+
label: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
value: number;
|
|
21
|
+
/** Stat receiving the value ('' collects without counting anywhere). */
|
|
22
|
+
stat: string;
|
|
23
|
+
onCollect?: (value: number) => void;
|
|
24
|
+
onCollide(other: Entity): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
import { isPlayer } from './player-identity.js';
|
|
3
|
+
/**
|
|
4
|
+
* Collected when the entity with the player role touches
|
|
5
|
+
* it: adds its value to a stat, fires onCollect and destroys itself.
|
|
6
|
+
* Requires Hitbox on both entities.
|
|
7
|
+
*/
|
|
8
|
+
export class Collectible extends Component {
|
|
9
|
+
static componentName = 'Collectible';
|
|
10
|
+
static params = {
|
|
11
|
+
value: { label: 'Value', min: 1, max: 100, step: 1 },
|
|
12
|
+
stat: { label: 'Adds to stat' },
|
|
13
|
+
};
|
|
14
|
+
value = 1;
|
|
15
|
+
/** Stat receiving the value ('' collects without counting anywhere). */
|
|
16
|
+
stat = 'points';
|
|
17
|
+
onCollect;
|
|
18
|
+
onCollide(other) {
|
|
19
|
+
if (!isPlayer(other))
|
|
20
|
+
return;
|
|
21
|
+
this.onCollect?.(this.value);
|
|
22
|
+
if (this.stat)
|
|
23
|
+
this.game.stats.add(this.stat, this.value);
|
|
24
|
+
this.game.events.emit('collect', this.value);
|
|
25
|
+
this.entity.destroy();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Component, type Entity } from '@waica/engine';
|
|
2
|
+
export type HazardTouch = 'stomp' | 'hurt';
|
|
3
|
+
/**
|
|
4
|
+
* Touching a hazard by stomping it from above (falling, with the feet
|
|
5
|
+
* above its center) squashes it; any other contact hurts.
|
|
6
|
+
* Pure, so the decision is testable without the engine.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveHazardTouch(playerVy: number, playerBottom: number, hazardY: number, stompable: boolean): HazardTouch;
|
|
9
|
+
/**
|
|
10
|
+
* Hurts the player on contact. If stompable (Mario-style), stomping it
|
|
11
|
+
* destroys it and bounces the player. Requires Hitbox on both entities.
|
|
12
|
+
*/
|
|
13
|
+
export declare class Hazard extends Component {
|
|
14
|
+
static componentName: string;
|
|
15
|
+
static params: {
|
|
16
|
+
stompable: {
|
|
17
|
+
label: string;
|
|
18
|
+
};
|
|
19
|
+
bounce: {
|
|
20
|
+
label: string;
|
|
21
|
+
min: number;
|
|
22
|
+
max: number;
|
|
23
|
+
step: number;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
stompable: boolean;
|
|
27
|
+
bounce: number;
|
|
28
|
+
onCollide(other: Entity): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
import { PlatformerMotor } from './platformer-motor.js';
|
|
3
|
+
import { isPlayer } from './player-identity.js';
|
|
4
|
+
import { Respawnable } from './respawnable.js';
|
|
5
|
+
/**
|
|
6
|
+
* Touching a hazard by stomping it from above (falling, with the feet
|
|
7
|
+
* above its center) squashes it; any other contact hurts.
|
|
8
|
+
* Pure, so the decision is testable without the engine.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveHazardTouch(playerVy, playerBottom, hazardY, stompable) {
|
|
11
|
+
if (stompable && playerVy < 0 && playerBottom > hazardY)
|
|
12
|
+
return 'stomp';
|
|
13
|
+
return 'hurt';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Hurts the player on contact. If stompable (Mario-style), stomping it
|
|
17
|
+
* destroys it and bounces the player. Requires Hitbox on both entities.
|
|
18
|
+
*/
|
|
19
|
+
export class Hazard extends Component {
|
|
20
|
+
static componentName = 'Hazard';
|
|
21
|
+
static params = {
|
|
22
|
+
stompable: { label: 'Stompable' },
|
|
23
|
+
bounce: { label: 'Stomp bounce', min: 0, max: 30, step: 0.5 },
|
|
24
|
+
};
|
|
25
|
+
stompable = true;
|
|
26
|
+
bounce = 10;
|
|
27
|
+
onCollide(other) {
|
|
28
|
+
if (!isPlayer(other))
|
|
29
|
+
return;
|
|
30
|
+
const motor = other.get(PlatformerMotor);
|
|
31
|
+
if (motor) {
|
|
32
|
+
const playerBottom = other.position.y - motor.hitboxHeight / 2;
|
|
33
|
+
const touch = resolveHazardTouch(motor.vy, playerBottom, this.entity.position.y, this.stompable);
|
|
34
|
+
if (touch === 'stomp') {
|
|
35
|
+
this.entity.destroy();
|
|
36
|
+
motor.vy = this.bounce;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
other.get(Respawnable)?.respawn();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { PlatformerMotor } from './platformer-motor.js';
|
|
2
|
+
export { PLAYER_ROLE, PLAYER_STATE_GRAPH, playerUpdate } from './player-states.js';
|
|
3
|
+
export { Collectible } from './collectible.js';
|
|
4
|
+
export { Patrol, PATROLLER_ROLE, PATROLLER_STATE_GRAPH, type PatrolAxis } from './patrol.js';
|
|
5
|
+
export { Chaser, CHASER_ROLE, CHASER_STATE_GRAPH, type ChaserMode } from './chaser.js';
|
|
6
|
+
export { NPC_ROLE, NPC_STATE_GRAPH } from './npc.js';
|
|
7
|
+
export { Hazard, resolveHazardTouch, type HazardTouch } from './hazard.js';
|
|
8
|
+
export { Respawnable } from './respawnable.js';
|
|
9
|
+
export { Lifetime } from './lifetime.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { PlatformerMotor } from './platformer-motor.js';
|
|
2
|
+
export { PLAYER_ROLE, PLAYER_STATE_GRAPH, playerUpdate } from './player-states.js';
|
|
3
|
+
export { Collectible } from './collectible.js';
|
|
4
|
+
export { Patrol, PATROLLER_ROLE, PATROLLER_STATE_GRAPH } from './patrol.js';
|
|
5
|
+
export { Chaser, CHASER_ROLE, CHASER_STATE_GRAPH } from './chaser.js';
|
|
6
|
+
export { NPC_ROLE, NPC_STATE_GRAPH } from './npc.js';
|
|
7
|
+
export { Hazard, resolveHazardTouch } from './hazard.js';
|
|
8
|
+
export { Respawnable } from './respawnable.js';
|
|
9
|
+
export { Lifetime } from './lifetime.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
/** Destroys its entity after a configurable amount of simulated time. */
|
|
3
|
+
export declare class Lifetime extends Component {
|
|
4
|
+
static componentName: string;
|
|
5
|
+
static params: {
|
|
6
|
+
seconds: {
|
|
7
|
+
label: string;
|
|
8
|
+
min: number;
|
|
9
|
+
max: number;
|
|
10
|
+
step: number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
seconds: number;
|
|
14
|
+
private elapsed;
|
|
15
|
+
onUpdate(dt: number): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
/** Destroys its entity after a configurable amount of simulated time. */
|
|
3
|
+
export class Lifetime extends Component {
|
|
4
|
+
static componentName = 'Lifetime';
|
|
5
|
+
static params = {
|
|
6
|
+
seconds: { label: 'Seconds', min: 0.05, max: 60, step: 0.05 },
|
|
7
|
+
};
|
|
8
|
+
seconds = 1;
|
|
9
|
+
elapsed = 0;
|
|
10
|
+
onUpdate(dt) {
|
|
11
|
+
// The frame that destroys the entity still iterates a copy of its
|
|
12
|
+
// components: stop counting instead of destroying twice.
|
|
13
|
+
if (!this.entity.alive)
|
|
14
|
+
return;
|
|
15
|
+
this.elapsed += dt;
|
|
16
|
+
if (this.elapsed >= this.seconds)
|
|
17
|
+
this.entity.destroy();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {} from '@waica/engine';
|
|
2
|
+
/** The state graph new NPCs start with, as prefab data. */
|
|
3
|
+
export const NPC_STATE_GRAPH = {
|
|
4
|
+
initial: 'idle',
|
|
5
|
+
states: { idle: {} },
|
|
6
|
+
};
|
|
7
|
+
// The bystander role: no driver and no code — the character stands where
|
|
8
|
+
// you put it, playing its idle animation, and hurts no one. Bring it to
|
|
9
|
+
// life by adding states (defineStates('npc', {...})) and behaviours.
|
|
10
|
+
export const NPC_ROLE = {
|
|
11
|
+
description: 'Stands where you put it — no input, no movement, harmless. Good for ' +
|
|
12
|
+
'villagers and signposts. Give it states to bring it to life.',
|
|
13
|
+
graph: NPC_STATE_GRAPH,
|
|
14
|
+
};
|