lantern-engine 0.0.0-stage → 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/CHANGELOG.md +101 -0
- package/LICENSE +21 -0
- package/README.md +113 -2
- package/dist/assets/takeCleanerWorker-DTcI_Jqr.js +31 -0
- package/dist/assets/takeCleanerWorker-DTcI_Jqr.js.map +1 -0
- package/dist/constants-CjYVdFid.js +252 -0
- package/dist/constants-CjYVdFid.js.map +1 -0
- package/dist/lantern-engine.js +3473 -0
- package/dist/lantern-engine.js.map +1 -0
- package/dist/pixel.js +1404 -0
- package/dist/pixel.js.map +1 -0
- package/dist/types/anim/Animator.d.ts +50 -0
- package/dist/types/anim/Animator.d.ts.map +1 -0
- package/dist/types/anim/animationSystem.d.ts +13 -0
- package/dist/types/anim/animationSystem.d.ts.map +1 -0
- package/dist/types/assets/AssetLoader.d.ts +85 -0
- package/dist/types/assets/AssetLoader.d.ts.map +1 -0
- package/dist/types/audio/MusicPlayer.d.ts +57 -0
- package/dist/types/audio/MusicPlayer.d.ts.map +1 -0
- package/dist/types/audio/SoundEngine.d.ts +42 -0
- package/dist/types/audio/SoundEngine.d.ts.map +1 -0
- package/dist/types/audio/cleanTake.d.ts +3 -0
- package/dist/types/audio/cleanTake.d.ts.map +1 -0
- package/dist/types/audio/sampleBank.d.ts +27 -0
- package/dist/types/audio/sampleBank.d.ts.map +1 -0
- package/dist/types/audio/soundTable.d.ts +14 -0
- package/dist/types/audio/soundTable.d.ts.map +1 -0
- package/dist/types/audio/synth.d.ts +34 -0
- package/dist/types/audio/synth.d.ts.map +1 -0
- package/dist/types/audio/takeCleanerWorker.d.ts +2 -0
- package/dist/types/audio/takeCleanerWorker.d.ts.map +1 -0
- package/dist/types/core/BitmapLabel.d.ts +26 -0
- package/dist/types/core/BitmapLabel.d.ts.map +1 -0
- package/dist/types/core/Camera.d.ts +90 -0
- package/dist/types/core/Camera.d.ts.map +1 -0
- package/dist/types/core/Clock.d.ts +11 -0
- package/dist/types/core/Clock.d.ts.map +1 -0
- package/dist/types/core/Graphics.d.ts +37 -0
- package/dist/types/core/Graphics.d.ts.map +1 -0
- package/dist/types/core/Group.d.ts +15 -0
- package/dist/types/core/Group.d.ts.map +1 -0
- package/dist/types/core/Image.d.ts +25 -0
- package/dist/types/core/Image.d.ts.map +1 -0
- package/dist/types/core/ObjectFactory.d.ts +34 -0
- package/dist/types/core/ObjectFactory.d.ts.map +1 -0
- package/dist/types/core/Pointer.d.ts +27 -0
- package/dist/types/core/Pointer.d.ts.map +1 -0
- package/dist/types/core/Rectangle.d.ts +26 -0
- package/dist/types/core/Rectangle.d.ts.map +1 -0
- package/dist/types/core/Stage.d.ts +58 -0
- package/dist/types/core/Stage.d.ts.map +1 -0
- package/dist/types/core/Text.d.ts +22 -0
- package/dist/types/core/Text.d.ts.map +1 -0
- package/dist/types/core/TextureRegistry.d.ts +51 -0
- package/dist/types/core/TextureRegistry.d.ts.map +1 -0
- package/dist/types/core/Tweener.d.ts +53 -0
- package/dist/types/core/Tweener.d.ts.map +1 -0
- package/dist/types/core/atlas.d.ts +16 -0
- package/dist/types/core/atlas.d.ts.map +1 -0
- package/dist/types/core/atlasPlan.d.ts +34 -0
- package/dist/types/core/atlasPlan.d.ts.map +1 -0
- package/dist/types/core/eases.d.ts +3 -0
- package/dist/types/core/eases.d.ts.map +1 -0
- package/dist/types/core/gameObject.d.ts +42 -0
- package/dist/types/core/gameObject.d.ts.map +1 -0
- package/dist/types/core/pixiPatches.d.ts +9 -0
- package/dist/types/core/pixiPatches.d.ts.map +1 -0
- package/dist/types/core/types.d.ts +4 -0
- package/dist/types/core/types.d.ts.map +1 -0
- package/dist/types/ecs/SparseSet.d.ts +25 -0
- package/dist/types/ecs/SparseSet.d.ts.map +1 -0
- package/dist/types/ecs/World.d.ts +133 -0
- package/dist/types/ecs/World.d.ts.map +1 -0
- package/dist/types/ecs/fields.d.ts +34 -0
- package/dist/types/ecs/fields.d.ts.map +1 -0
- package/dist/types/engine/Engine.d.ts +80 -0
- package/dist/types/engine/Engine.d.ts.map +1 -0
- package/dist/types/engine/FixedLoop.d.ts +32 -0
- package/dist/types/engine/FixedLoop.d.ts.map +1 -0
- package/dist/types/engine/Scene.d.ts +37 -0
- package/dist/types/engine/Scene.d.ts.map +1 -0
- package/dist/types/engine/SceneManager.d.ts +31 -0
- package/dist/types/engine/SceneManager.d.ts.map +1 -0
- package/dist/types/events/EventBus.d.ts +20 -0
- package/dist/types/events/EventBus.d.ts.map +1 -0
- package/dist/types/fx/hitStop.d.ts +20 -0
- package/dist/types/fx/hitStop.d.ts.map +1 -0
- package/dist/types/fx/viewCull.d.ts +14 -0
- package/dist/types/fx/viewCull.d.ts.map +1 -0
- package/dist/types/index.d.ts +68 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/input/Actions.d.ts +75 -0
- package/dist/types/input/Actions.d.ts.map +1 -0
- package/dist/types/input/Keyboard.d.ts +25 -0
- package/dist/types/input/Keyboard.d.ts.map +1 -0
- package/dist/types/input/aimAxis.d.ts +9 -0
- package/dist/types/input/aimAxis.d.ts.map +1 -0
- package/dist/types/input/longPress.d.ts +21 -0
- package/dist/types/input/longPress.d.ts.map +1 -0
- package/dist/types/input/stickAxis.d.ts +26 -0
- package/dist/types/input/stickAxis.d.ts.map +1 -0
- package/dist/types/input/touchDetect.d.ts +18 -0
- package/dist/types/input/touchDetect.d.ts.map +1 -0
- package/dist/types/light/LightLayer.d.ts +65 -0
- package/dist/types/light/LightLayer.d.ts.map +1 -0
- package/dist/types/net/Connection.d.ts +66 -0
- package/dist/types/net/Connection.d.ts.map +1 -0
- package/dist/types/net/InterpolationBuffer.d.ts +36 -0
- package/dist/types/net/InterpolationBuffer.d.ts.map +1 -0
- package/dist/types/net/PredictedMover.d.ts +43 -0
- package/dist/types/net/PredictedMover.d.ts.map +1 -0
- package/dist/types/net/Room.d.ts +69 -0
- package/dist/types/net/Room.d.ts.map +1 -0
- package/dist/types/net/RoomHost.d.ts +74 -0
- package/dist/types/net/RoomHost.d.ts.map +1 -0
- package/dist/types/net/cadence.d.ts +22 -0
- package/dist/types/net/cadence.d.ts.map +1 -0
- package/dist/types/net/constants.d.ts +24 -0
- package/dist/types/net/constants.d.ts.map +1 -0
- package/dist/types/net/movement.d.ts +50 -0
- package/dist/types/net/movement.d.ts.map +1 -0
- package/dist/types/net/protocol.d.ts +59 -0
- package/dist/types/net/protocol.d.ts.map +1 -0
- package/dist/types/net/serverClock.d.ts +14 -0
- package/dist/types/net/serverClock.d.ts.map +1 -0
- package/dist/types/net/snapshotCodec.d.ts +55 -0
- package/dist/types/net/snapshotCodec.d.ts.map +1 -0
- package/dist/types/pixel/Darkness.d.ts +78 -0
- package/dist/types/pixel/Darkness.d.ts.map +1 -0
- package/dist/types/pixel/Evaporation.d.ts +30 -0
- package/dist/types/pixel/Evaporation.d.ts.map +1 -0
- package/dist/types/pixel/PixelLayer.d.ts +85 -0
- package/dist/types/pixel/PixelLayer.d.ts.map +1 -0
- package/dist/types/pixel/index.d.ts +22 -0
- package/dist/types/pixel/index.d.ts.map +1 -0
- package/dist/types/pixel/lights.d.ts +34 -0
- package/dist/types/pixel/lights.d.ts.map +1 -0
- package/dist/types/pixel/palette.d.ts +54 -0
- package/dist/types/pixel/palette.d.ts.map +1 -0
- package/dist/types/pixel/pixel.d.ts +80 -0
- package/dist/types/pixel/pixel.d.ts.map +1 -0
- package/dist/types/pixel/pixelPaint.d.ts +43 -0
- package/dist/types/pixel/pixelPaint.d.ts.map +1 -0
- package/dist/types/pixel/pixelTone.d.ts +49 -0
- package/dist/types/pixel/pixelTone.d.ts.map +1 -0
- package/dist/types/pixel/propKit.d.ts +26 -0
- package/dist/types/pixel/propKit.d.ts.map +1 -0
- package/dist/types/pixel/ramp.d.ts +31 -0
- package/dist/types/pixel/ramp.d.ts.map +1 -0
- package/dist/types/pixel/selout.d.ts +3 -0
- package/dist/types/pixel/selout.d.ts.map +1 -0
- package/dist/types/pixel/spritePixels.d.ts +29 -0
- package/dist/types/pixel/spritePixels.d.ts.map +1 -0
- package/dist/types/platform/touch.d.ts +5 -0
- package/dist/types/platform/touch.d.ts.map +1 -0
- package/dist/types/rig/PosePlayer.d.ts +64 -0
- package/dist/types/rig/PosePlayer.d.ts.map +1 -0
- package/dist/types/rig/RigView.d.ts +68 -0
- package/dist/types/rig/RigView.d.ts.map +1 -0
- package/dist/types/rig/Skeleton.d.ts +14 -0
- package/dist/types/rig/Skeleton.d.ts.map +1 -0
- package/dist/types/rig/humanoid.d.ts +4 -0
- package/dist/types/rig/humanoid.d.ts.map +1 -0
- package/dist/types/rig/humanoidPoses.d.ts +117 -0
- package/dist/types/rig/humanoidPoses.d.ts.map +1 -0
- package/dist/types/rig/poseMath.d.ts +14 -0
- package/dist/types/rig/poseMath.d.ts.map +1 -0
- package/dist/types/rig/rigTypes.d.ts +82 -0
- package/dist/types/rig/rigTypes.d.ts.map +1 -0
- package/dist/types/settings.d.ts +25 -0
- package/dist/types/settings.d.ts.map +1 -0
- package/dist/types/tilemap/TilemapView.d.ts +42 -0
- package/dist/types/tilemap/TilemapView.d.ts.map +1 -0
- package/dist/types/tilemap/collision.d.ts +16 -0
- package/dist/types/tilemap/collision.d.ts.map +1 -0
- package/dist/types/tilemap/tiled.d.ts +104 -0
- package/dist/types/tilemap/tiled.d.ts.map +1 -0
- package/package.json +82 -4
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { InputStep, MovementParams } from './constants.js';
|
|
2
|
+
import { type CollisionGrid, type MoverState } from './movement.js';
|
|
3
|
+
import { type MoverWire } from './protocol.js';
|
|
4
|
+
/**
|
|
5
|
+
* Client-side prediction for the local player's body. Every input step is applied at once (the player sees their
|
|
6
|
+
* own move with no lag) and kept until the server acknowledges it. When a snapshot says where the server has the
|
|
7
|
+
* body as of step `ack`, the body is put there and every step after `ack` is replayed on top.
|
|
8
|
+
*
|
|
9
|
+
* Movement is deterministic (`stepMover`), so with the same steps, params and grid the replay lands exactly where
|
|
10
|
+
* the prediction was and nothing moves. When it does not (the server refused a step, a collision only it knew of),
|
|
11
|
+
* the difference is `correction`: draw the body at `drawX/drawY`, which ease that jump out over a few frames instead
|
|
12
|
+
* of snapping.
|
|
13
|
+
*/
|
|
14
|
+
export declare class PredictedMover {
|
|
15
|
+
private params;
|
|
16
|
+
private grid;
|
|
17
|
+
readonly mover: MoverState;
|
|
18
|
+
private pending;
|
|
19
|
+
private nextSeq;
|
|
20
|
+
/** The jump the last reconciliation made, still being eased out (world px). */
|
|
21
|
+
private offsetX;
|
|
22
|
+
private offsetY;
|
|
23
|
+
/** How big the last correction was (0 when the prediction was right). */
|
|
24
|
+
correction: number;
|
|
25
|
+
/** Share of a visual correction left after each 60th of a second (default 0.8). */
|
|
26
|
+
smoothing: number;
|
|
27
|
+
constructor(x: number, y: number, params: MovementParams, grid: CollisionGrid);
|
|
28
|
+
/** Applies one input step now and keeps it for replay; returns its sequence number (send it with the step). */
|
|
29
|
+
step(step: InputStep): number;
|
|
30
|
+
/** Steps sent but not yet acknowledged. */
|
|
31
|
+
get unacknowledged(): number;
|
|
32
|
+
/** The server's word: the body as of step `ack`. Rewinds there and replays the rest. */
|
|
33
|
+
reconcile(ack: number, server: MoverWire): void;
|
|
34
|
+
/** Eases the visual correction out; call once a frame with its length in seconds. */
|
|
35
|
+
smooth(dt: number): void;
|
|
36
|
+
/** Where to draw the body: the predicted place plus the correction still being eased out. */
|
|
37
|
+
get drawX(): number;
|
|
38
|
+
get drawY(): number;
|
|
39
|
+
/** New movement rules (a buff, a slow): applied to future steps and to replays. */
|
|
40
|
+
setParams(params: MovementParams): void;
|
|
41
|
+
setGrid(grid: CollisionGrid): void;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=PredictedMover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PredictedMover.d.ts","sourceRoot":"","sources":["../../../src/net/PredictedMover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAA0B,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;;;;;;GASG;AACH,qBAAa,cAAc;IAevB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,IAAI;IAfd,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,OAAO,CAAC,OAAO,CAA0C;IACzD,OAAO,CAAC,OAAO,CAAK;IACpB,+EAA+E;IAC/E,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;IACpB,yEAAyE;IACzE,UAAU,SAAK;IACf,mFAAmF;IACnF,SAAS,SAAO;gBAGd,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACD,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,aAAa;IAK7B,+GAA+G;IAC/G,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM;IAO7B,2CAA2C;IAC3C,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,wFAAwF;IACxF,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAoB/C,qFAAqF;IACrF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQxB,6FAA6F;IAC7F,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,mFAAmF;IACnF,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAIvC,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;CAGnC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { InputStep, MovementParams } from './constants.js';
|
|
2
|
+
import { type CollisionGrid, type MoverState } from './movement.js';
|
|
3
|
+
import { type Snapshot } from './protocol.js';
|
|
4
|
+
export interface RoomPlayer<D = unknown> {
|
|
5
|
+
readonly id: number;
|
|
6
|
+
readonly mover: MoverState;
|
|
7
|
+
/** The game's own data for this player (sent to others in `sp` when they first see it). */
|
|
8
|
+
data: D;
|
|
9
|
+
params: MovementParams;
|
|
10
|
+
/** The last input step applied (-1 before any): what snapshots acknowledge. */
|
|
11
|
+
ack: number;
|
|
12
|
+
/** Steps received and not yet applied. */
|
|
13
|
+
readonly queue: InputStep[];
|
|
14
|
+
/** What the snapshot's update record carries beside the position; the game sets them. */
|
|
15
|
+
aim: number;
|
|
16
|
+
anim: number;
|
|
17
|
+
hp: number;
|
|
18
|
+
flags: number;
|
|
19
|
+
/** Refused inputs (nonsense numbers, a flood): a game may kick past some count. */
|
|
20
|
+
strikes: number;
|
|
21
|
+
}
|
|
22
|
+
export interface RoomOptions<D> {
|
|
23
|
+
grid: CollisionGrid;
|
|
24
|
+
/** Movement rules a new player starts with. */
|
|
25
|
+
params: MovementParams;
|
|
26
|
+
/** Where a player who joins stands. */
|
|
27
|
+
spawn: (id: number, data: D) => {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
};
|
|
31
|
+
/** Steps a player may have waiting before more are refused (default 30: half a second at 60). */
|
|
32
|
+
maxQueue?: number;
|
|
33
|
+
/** A player with more than `backlog` waiting catches up by running up to `maxStepsPerTick` a tick (default 3, 4). */
|
|
34
|
+
maxStepsPerTick?: number;
|
|
35
|
+
backlog?: number;
|
|
36
|
+
/** Called after each step a player takes (set aim, anim, flags from it). */
|
|
37
|
+
onStep?: (player: RoomPlayer<D>, step: InputStep) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* An authoritative room for a server: players send input steps, the room applies them with the same `stepMover` the
|
|
41
|
+
* clients predict with, and builds each player a snapshot of everyone (with their own body whole, so their
|
|
42
|
+
* prediction can rewind to it). Transport-free: wire `input`, `step` and `snapshotFor` to any socket library.
|
|
43
|
+
*
|
|
44
|
+
* Every step a player sends is applied once, in order; a client cannot move faster by sending more (the queue is
|
|
45
|
+
* capped and drained at most `maxStepsPerTick` a tick), and nonsense input is refused and counted.
|
|
46
|
+
*/
|
|
47
|
+
export declare class Room<D = unknown> {
|
|
48
|
+
private readonly options;
|
|
49
|
+
readonly players: Map<number, RoomPlayer<D>>;
|
|
50
|
+
tick: number;
|
|
51
|
+
private readonly known;
|
|
52
|
+
private readonly maxQueue;
|
|
53
|
+
private readonly maxSteps;
|
|
54
|
+
private readonly backlog;
|
|
55
|
+
constructor(options: RoomOptions<D>);
|
|
56
|
+
join(id: number, data: D): RoomPlayer<D>;
|
|
57
|
+
leave(id: number): void;
|
|
58
|
+
/**
|
|
59
|
+
* Input steps `seq`, `seq + 1`, … from a player. Steps already received are skipped (a resend), any step with
|
|
60
|
+
* numbers no client sends refuses the whole message, and steps past a full queue are dropped. Returns false when
|
|
61
|
+
* something was refused.
|
|
62
|
+
*/
|
|
63
|
+
input(id: number, seq: number, steps: readonly InputStep[]): boolean;
|
|
64
|
+
/** One simulation step for everyone. */
|
|
65
|
+
step(): void;
|
|
66
|
+
/** The snapshot player `id` should get now. `time` is the server's wall clock (ms). */
|
|
67
|
+
snapshotFor(id: number, time: number): Snapshot | null;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=Room.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Room.d.ts","sourceRoot":"","sources":["../../../src/net/Room.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAA0B,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,2FAA2F;IAC3F,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,cAAc,CAAC;IACvB,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;IAC5B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,aAAa,CAAC;IACpB,+CAA+C;IAC/C,MAAM,EAAE,cAAc,CAAC;IACvB,uCAAuC;IACvC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,iGAAiG;IACjG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qHAAqH;IACrH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;CAC3D;AAED;;;;;;;GAOG;AACH,qBAAa,IAAI,CAAC,CAAC,GAAG,OAAO;IAQf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,QAAQ,CAAC,OAAO,6BAAoC;IACpD,IAAI,SAAK;IACT,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAEJ,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAMpD,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAqBxC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKvB;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO;IAkCpE,wCAAwC;IACxC,IAAI,IAAI,IAAI;IAeZ,uFAAuF;IACvF,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;CA2BvD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Room } from './Room.js';
|
|
2
|
+
import type { ServerMessage } from './protocol.js';
|
|
3
|
+
/** The least a server socket must do; a `ws` WebSocket, a uWebSockets one or a test double all fit. */
|
|
4
|
+
export interface HostSocket {
|
|
5
|
+
send(data: string | Uint8Array): void;
|
|
6
|
+
close(code?: number, reason?: string): void;
|
|
7
|
+
}
|
|
8
|
+
export interface RoomHostOptions<D> {
|
|
9
|
+
/** Simulation steps a second (default 60) and snapshots a second (default 20). */
|
|
10
|
+
stepRate?: number;
|
|
11
|
+
snapRate?: number;
|
|
12
|
+
/** Turns a hello's data into the player's data (validate it here); throw to refuse the player. */
|
|
13
|
+
admit?: (data: unknown, id: number) => D;
|
|
14
|
+
/** Refused inputs before the player is disconnected (default 20). */
|
|
15
|
+
maxStrikes?: number;
|
|
16
|
+
/** The game's own messages (anything the engine does not handle). */
|
|
17
|
+
onMessage?: (id: number, msg: {
|
|
18
|
+
t: string;
|
|
19
|
+
[k: string]: unknown;
|
|
20
|
+
}) => void;
|
|
21
|
+
/** Called when a player joins or leaves. */
|
|
22
|
+
onJoin?: (id: number) => void;
|
|
23
|
+
onLeave?: (id: number) => void;
|
|
24
|
+
/** Wall clock, for tests (default Date.now). */
|
|
25
|
+
now?: () => number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Runs a `Room` for connected sockets: a socket says hello and joins, its input goes to the room, the room steps at
|
|
29
|
+
* `stepRate`, and every player gets its own snapshot at `snapRate` (binary when it carries bodies). Pings are
|
|
30
|
+
* answered with the server's time. A player who keeps sending nonsense is disconnected.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* const host = new RoomHost(room);
|
|
34
|
+
* wss.on('connection', (ws) => {
|
|
35
|
+
* const id = host.connect(ws);
|
|
36
|
+
* ws.on('message', (data, binary) => host.message(id, binary ? data : data.toString()));
|
|
37
|
+
* ws.on('close', () => host.disconnect(id));
|
|
38
|
+
* });
|
|
39
|
+
* host.start();
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare class RoomHost<D = unknown> {
|
|
43
|
+
readonly room: Room<D>;
|
|
44
|
+
private readonly options;
|
|
45
|
+
private readonly sockets;
|
|
46
|
+
private readonly joined;
|
|
47
|
+
private nextId;
|
|
48
|
+
private stepTimer;
|
|
49
|
+
private lastStepAt;
|
|
50
|
+
private stepsDue;
|
|
51
|
+
private readonly stepMs;
|
|
52
|
+
private readonly snapEvery;
|
|
53
|
+
private readonly now;
|
|
54
|
+
constructor(room: Room<D>, options?: RoomHostOptions<D>);
|
|
55
|
+
/** A socket connected; returns the id its messages and its close are reported under. */
|
|
56
|
+
connect(socket: HostSocket): number;
|
|
57
|
+
/** A message arrived from socket `id` (text as a string, binary as bytes). */
|
|
58
|
+
message(id: number, data: string | ArrayBuffer | Uint8Array): void;
|
|
59
|
+
/** Socket `id` closed. */
|
|
60
|
+
disconnect(id: number): void;
|
|
61
|
+
/** Sends one of the game's own messages to a player. */
|
|
62
|
+
sendTo(id: number, msg: ServerMessage | {
|
|
63
|
+
t: string;
|
|
64
|
+
[k: string]: unknown;
|
|
65
|
+
}): void;
|
|
66
|
+
/** Starts stepping on a timer. Steps are counted by the clock, so a late timer runs the steps it owes. */
|
|
67
|
+
start(): void;
|
|
68
|
+
stop(): void;
|
|
69
|
+
/** One step, and the snapshots when they are due. Exposed for tests and for servers that drive their own loop. */
|
|
70
|
+
stepOnce(): void;
|
|
71
|
+
private strike;
|
|
72
|
+
private checkStrikes;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=RoomHost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoomHost.d.ts","sourceRoot":"","sources":["../../../src/net/RoomHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAiB,aAAa,EAAY,MAAM,YAAY,CAAC;AAEzE,uGAAuG;AACvG,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kGAAkG;IAClG,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,CAAC,CAAC;IACzC,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,4CAA4C;IAC5C,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAQ,CAAC,CAAC,GAAG,OAAO;IAY7B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAGxB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EACL,OAAO,GAAE,eAAe,CAAC,CAAC,CAAM;IAOnD,wFAAwF;IACxF,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAMnC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI;IAyClE,0BAA0B;IAC1B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQ5B,wDAAwD;IACxD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI;IAOlF,0GAA0G;IAC1G,KAAK,IAAI,IAAI;IAgBb,IAAI,IAAI,IAAI;IAKZ,kHAAkH;IAClH,QAAQ,IAAI,IAAI;IAUhB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,YAAY;CAOrB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every repeated player action with a timer — the basic attack, a skill's cooldown, the cast and attack locks, a
|
|
3
|
+
* potion — runs on a **schedule**, not on "the last time plus some slack". An action may arrive up to its slack
|
|
4
|
+
* early (network jitter bunches packets), but the next one is then due a whole interval after the later of the
|
|
5
|
+
* arrival and the time this one was due, so the slack never adds up: a client sending every 0.8 of an interval
|
|
6
|
+
* lands at most one per interval (67 in 100), not 100. A server must run the same arithmetic.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CADENCE: {
|
|
9
|
+
/** A basic attack may arrive this share of its interval early. */
|
|
10
|
+
readonly attackEarlyShare: 0.2;
|
|
11
|
+
/** The cast lock and the attack lock a cast leaves may be cut this short. */
|
|
12
|
+
readonly lockSlackMs: 60;
|
|
13
|
+
/** A skill may be cast this long before its cooldown ends. */
|
|
14
|
+
readonly cooldownSlackMs: 40;
|
|
15
|
+
/** A potion may be drunk this long before its kind's cooldown ends. */
|
|
16
|
+
readonly potionSlackMs: 150;
|
|
17
|
+
};
|
|
18
|
+
/** Whether an action arriving at `now` is earlier than its slack allows; `due` undefined = never taken, due at once. */
|
|
19
|
+
export declare function cadenceEarly(now: number, due: number | undefined, slack: number): boolean;
|
|
20
|
+
/** When the action after one accepted at `now` is due: a whole interval after the later of `now` and `due`. */
|
|
21
|
+
export declare function cadenceNext(now: number, due: number | undefined, interval: number): number;
|
|
22
|
+
//# sourceMappingURL=cadence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cadence.d.ts","sourceRoot":"","sources":["../../../src/net/cadence.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;IAClB,kEAAkE;;IAElE,6EAA6E;;IAE7E,8DAA8D;;IAE9D,uEAAuE;;CAE/D,CAAC;AAEX,wHAAwH;AACxH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzF;AAED,+GAA+G;AAC/G,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** World and movement constants. A game that needs other numbers forks these; client and server must agree. */
|
|
2
|
+
export declare const TILE_SIZE = 16;
|
|
3
|
+
/** Fixed input steps a second; movement is simulated per step on both sides. */
|
|
4
|
+
export declare const INPUT_STEP_RATE = 60;
|
|
5
|
+
export declare const INPUT_STEP_DT: number;
|
|
6
|
+
/** Bit of an input step's flags that asks for a dodge roll. */
|
|
7
|
+
export declare const INPUT_FLAG_DASH = 1;
|
|
8
|
+
/** The body's collision box, half extents (px). */
|
|
9
|
+
export declare const BODY_HALF_WIDTH = 5;
|
|
10
|
+
export declare const BODY_HALF_HEIGHT = 3;
|
|
11
|
+
export declare const COLLISION_EPSILON = 0.01;
|
|
12
|
+
/** One fixed input step: [axisX, axisY, flags]. */
|
|
13
|
+
export type InputStep = [number, number, number];
|
|
14
|
+
export interface MovementParams {
|
|
15
|
+
speed: number;
|
|
16
|
+
dashSpeed: number;
|
|
17
|
+
dashSteps: number;
|
|
18
|
+
dashCooldownSteps: number;
|
|
19
|
+
canMove: boolean;
|
|
20
|
+
canDash: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** One body's state in a snapshot: [id, x, y, aim, anim, hp, flags]. */
|
|
23
|
+
export type EntityUpdate = [number, number, number, number, number, number, number];
|
|
24
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/net/constants.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAE/G,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,gFAAgF;AAChF,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,aAAa,QAAsB,CAAC;AAEjD,+DAA+D;AAC/D,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,mDAAmD;AACnD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,mDAAmD;AACnD,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type InputStep, type MovementParams } from './constants.js';
|
|
2
|
+
/** 1/sqrt(2), written as a literal so every port uses the exact same double. */
|
|
3
|
+
export declare const DIAGONAL = 0.7071067811865476;
|
|
4
|
+
export interface CollisionGrid {
|
|
5
|
+
readonly width: number;
|
|
6
|
+
readonly height: number;
|
|
7
|
+
readonly tileSize: number;
|
|
8
|
+
/** 1 = solid, row-major. */
|
|
9
|
+
readonly solid: Uint8Array;
|
|
10
|
+
/** 1 = a solid tile a shot flies over (`shotOverTiles`), row-major; absent when the ground was not given. */
|
|
11
|
+
readonly shotOver?: Uint8Array;
|
|
12
|
+
/** Half the moving body's collision box, px. */
|
|
13
|
+
readonly bodyHalfWidth: number;
|
|
14
|
+
readonly bodyHalfHeight: number;
|
|
15
|
+
}
|
|
16
|
+
export interface CollisionGridOptions {
|
|
17
|
+
/** The tile id of every cell, row-major; needed only for `shotOverTiles`. */
|
|
18
|
+
ground?: readonly number[];
|
|
19
|
+
/** Tile ids nothing walks but a shot flies over (water, lava…): solid for bodies, open for shots. */
|
|
20
|
+
shotOverTiles?: Iterable<number>;
|
|
21
|
+
/** Half the moving body's collision box, px (default `BODY_HALF_WIDTH` × `BODY_HALF_HEIGHT`, a 10×6 foot box). */
|
|
22
|
+
bodyHalfWidth?: number;
|
|
23
|
+
bodyHalfHeight?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface MoverState {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
/** Last non-zero movement direction (normalized), used for dashing while idle. */
|
|
29
|
+
lastDx: number;
|
|
30
|
+
lastDy: number;
|
|
31
|
+
dashLeft: number;
|
|
32
|
+
dashCd: number;
|
|
33
|
+
dashDx: number;
|
|
34
|
+
dashDy: number;
|
|
35
|
+
}
|
|
36
|
+
/** A grid from a string of '0'/'1' per cell (row-major, '1' solid). */
|
|
37
|
+
export declare function createCollisionGrid(width: number, height: number, tileSize: number, solid: string, options?: CollisionGridOptions): CollisionGrid;
|
|
38
|
+
/** Whether a shot in flight stops on this tile: solid, and not ground a shot flies over (`shotOverTiles`). */
|
|
39
|
+
export declare function blocksShot(grid: CollisionGrid, tx: number, ty: number): boolean;
|
|
40
|
+
export declare function createMover(x: number, y: number): MoverState;
|
|
41
|
+
export declare function isSolidTile(grid: CollisionGrid, tx: number, ty: number): boolean;
|
|
42
|
+
/** True if the body box centered at (x, y) overlaps a solid tile. */
|
|
43
|
+
export declare function bodyCollides(grid: CollisionGrid, x: number, y: number): boolean;
|
|
44
|
+
/** Moves along X by dx, stopping flush against the first solid tile column. */
|
|
45
|
+
export declare function moveX(grid: CollisionGrid, x: number, y: number, dx: number): number;
|
|
46
|
+
/** Moves along Y by dy, stopping flush against the first solid tile row. */
|
|
47
|
+
export declare function moveY(grid: CollisionGrid, x: number, y: number, dy: number): number;
|
|
48
|
+
/** Advances the mover by one fixed input step. Mutates `s`. */
|
|
49
|
+
export declare function stepMover(s: MoverState, step: InputStep, p: MovementParams, grid: CollisionGrid): void;
|
|
50
|
+
//# sourceMappingURL=movement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"movement.d.ts","sourceRoot":"","sources":["../../../src/net/movement.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAUrB,gFAAgF;AAChF,eAAO,MAAM,QAAQ,qBAAqB,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,6GAA6G;IAC7G,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,qGAAqG;IACrG,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,kHAAkH;IAClH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAgBrJ;AAED,8GAA8G;AAC9G,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAG/E;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAE5D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAGhF;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAY/E;AAED,+EAA+E;AAC/E,wBAAgB,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAkBnF;AAED,4EAA4E;AAC5E,wBAAgB,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAkBnF;AAMD,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAoCtG"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { EntityUpdate, InputStep } from './constants.js';
|
|
2
|
+
import type { MoverState } from './movement.js';
|
|
3
|
+
/** Client → server. */
|
|
4
|
+
export type ClientMessage =
|
|
5
|
+
/** Asks to join; `data` is the game's own (a name, a character). */
|
|
6
|
+
{
|
|
7
|
+
t: 'hello';
|
|
8
|
+
data?: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** Input steps `seq`, `seq + 1`, … taken since the last send. */
|
|
11
|
+
| {
|
|
12
|
+
t: 'input';
|
|
13
|
+
seq: number;
|
|
14
|
+
steps: InputStep[];
|
|
15
|
+
}
|
|
16
|
+
/** A ping carrying the client's own clock, answered by a pong. */
|
|
17
|
+
| {
|
|
18
|
+
t: 'ping';
|
|
19
|
+
c: number;
|
|
20
|
+
};
|
|
21
|
+
/** Server → client. */
|
|
22
|
+
export type ServerMessage = {
|
|
23
|
+
t: 'welcome';
|
|
24
|
+
id: number;
|
|
25
|
+
tick: number;
|
|
26
|
+
stepRate: number;
|
|
27
|
+
snapRate: number;
|
|
28
|
+
} | {
|
|
29
|
+
t: 'pong';
|
|
30
|
+
c: number;
|
|
31
|
+
st: number;
|
|
32
|
+
} | Snapshot;
|
|
33
|
+
/** A body's whole mover, so a client can rewind to it exactly: x, y, lastDx, lastDy, dashLeft, dashCd, dashDx, dashDy. */
|
|
34
|
+
export type MoverWire = [number, number, number, number, number, number, number, number];
|
|
35
|
+
/** A body coming into view: its id and the game's own description of it (what it looks like). */
|
|
36
|
+
export interface SpawnInfo {
|
|
37
|
+
id: number;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
}
|
|
40
|
+
export interface Snapshot {
|
|
41
|
+
t: 'snap';
|
|
42
|
+
/** The server's step count when it was taken. */
|
|
43
|
+
tick: number;
|
|
44
|
+
/** The server's wall time when it was taken (ms since the epoch), for interpolation. */
|
|
45
|
+
time: number;
|
|
46
|
+
/** The last input step of this client the server has applied (-1 before any). */
|
|
47
|
+
ack: number;
|
|
48
|
+
/** This client's own body, whole, as of `ack`. */
|
|
49
|
+
me?: MoverWire;
|
|
50
|
+
/** Bodies that came into view. */
|
|
51
|
+
sp?: SpawnInfo[];
|
|
52
|
+
/** Bodies that left view. */
|
|
53
|
+
rm?: number[];
|
|
54
|
+
/** Every body in view: [id, x, y, aim, anim, hp, flags]. */
|
|
55
|
+
up?: EntityUpdate[];
|
|
56
|
+
}
|
|
57
|
+
export declare function moverToWire(m: MoverState): MoverWire;
|
|
58
|
+
export declare function moverFromWire(w: MoverWire, into: MoverState): MoverState;
|
|
59
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/net/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAQ7C,uBAAuB;AACvB,MAAM,MAAM,aAAa;AACvB,oEAAoE;AAClE;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;AAChC,iEAAiE;GAC/D;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE;AACjD,kEAAkE;GAChE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7B,uBAAuB;AACvB,MAAM,MAAM,aAAa,GACrB;IAAE,CAAC,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACpC,QAAQ,CAAC;AAEb,0HAA0H;AAC1H,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEzF,iGAAiG;AACjG,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,kCAAkC;IAClC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC;IACjB,6BAA6B;IAC7B,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,4DAA4D;IAC5D,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAEpD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,CAGxE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** A sample whose round trip took longer than this says too little about when the server answered: it is passed over. */
|
|
2
|
+
export declare const MAX_SAMPLE_RTT_MS = 2000;
|
|
3
|
+
/** Server wall time now, in ms since the epoch. */
|
|
4
|
+
export declare function serverNow(): number;
|
|
5
|
+
/** The current estimate of server time minus this device's time, in ms. */
|
|
6
|
+
export declare function serverOffset(): number;
|
|
7
|
+
/**
|
|
8
|
+
* A server timestamp `serverMs` taken halfway through an exchange that took `rttMs` to come back: the server's clock
|
|
9
|
+
* then stood at `serverMs + rttMs / 2` of this device's now.
|
|
10
|
+
*/
|
|
11
|
+
export declare function noteServerTime(serverMs: number, rttMs: number, now?: number): void;
|
|
12
|
+
/** Back to this device's own clock (offline, or leaving a server). */
|
|
13
|
+
export declare function resetServerClock(): void;
|
|
14
|
+
//# sourceMappingURL=serverClock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverClock.d.ts","sourceRoot":"","sources":["../../../src/net/serverClock.ts"],"names":[],"mappings":"AAcA,yHAAyH;AACzH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAOtC,mDAAmD;AACnD,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI,CAOtF;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { EntityUpdate } from './constants.js';
|
|
2
|
+
/** Any snapshot whose `up` block travels as binary records; the rest is JSON. */
|
|
3
|
+
export interface SnapshotLike {
|
|
4
|
+
up?: EntityUpdate[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Binary wire form of a snapshot's `up` block. A server in another language must write the same bytes.
|
|
8
|
+
*
|
|
9
|
+
* A snapshot that carries no updates is still one text frame of JSON. A snapshot that does is one **binary** WebSocket
|
|
10
|
+
* frame, big-endian throughout:
|
|
11
|
+
*
|
|
12
|
+
* ```
|
|
13
|
+
* u8 SNAP_FRAME_TAG
|
|
14
|
+
* u32 L, the byte length of the JSON that follows
|
|
15
|
+
* L UTF-8 JSON of the snapshot with `up` left out
|
|
16
|
+
* n × UPDATE_RECORD_BYTES, one record per EntityUpdate, n = (frame length - 5 - L) / 16
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* One record (16 bytes), the `[id, x, y, aim, anim, hp, flags]` tuple:
|
|
20
|
+
*
|
|
21
|
+
* | off | type | field |
|
|
22
|
+
* |-----|-------|------------------------------------------------------------------------------------|
|
|
23
|
+
* | 0 | i32 | id |
|
|
24
|
+
* | 4 | i16 | x in 1/UPDATE_POS_SCALE px, clamped to [-32768, 32767] (−4096 … 4095.875 px) |
|
|
25
|
+
* | 6 | i16 | y, the same |
|
|
26
|
+
* | 8 | u8 | aim in 1/UPDATE_AIM_STEPS of a turn, wrapped; read back in [-π, π) |
|
|
27
|
+
* | 9 | u8 | anim (ANIM), clamped to 0..255 |
|
|
28
|
+
* | 10 | i32 | hp (big bodies go past 65535) |
|
|
29
|
+
* | 14 | u16 | flags (ENTITY_FLAG, nine bits used today), clamped to 0..65535 |
|
|
30
|
+
*
|
|
31
|
+
* Quantisation error: position ≤ 1/16 px, aim ≤ π/256 rad. The position range holds maps up to 4096 px a side.
|
|
32
|
+
* Each record is written once per actor state version on the server and shared by every viewer.
|
|
33
|
+
*/
|
|
34
|
+
export declare const SNAP_FRAME_TAG = 1;
|
|
35
|
+
/** Tag byte + the u32 JSON length. */
|
|
36
|
+
export declare const SNAP_FRAME_HEADER_BYTES = 5;
|
|
37
|
+
export declare const UPDATE_RECORD_BYTES = 16;
|
|
38
|
+
export declare const UPDATE_POS_SCALE = 8;
|
|
39
|
+
export declare const UPDATE_AIM_STEPS = 256;
|
|
40
|
+
/** World px → the i16 on the wire. */
|
|
41
|
+
export declare function quantizePos(v: number): number;
|
|
42
|
+
/** Radians → the u8 on the wire (wrapped, so any angle is accepted). */
|
|
43
|
+
export declare function quantizeAim(a: number): number;
|
|
44
|
+
export declare function dequantizeAim(b: number): number;
|
|
45
|
+
export declare function writeUpdate(view: DataView, off: number, u: EntityUpdate): void;
|
|
46
|
+
export declare function readUpdate(view: DataView, off: number): EntityUpdate;
|
|
47
|
+
export declare function encodeUpdates(ups: readonly EntityUpdate[]): Uint8Array;
|
|
48
|
+
export declare function decodeUpdates(bytes: Uint8Array): EntityUpdate[];
|
|
49
|
+
/** The updates exactly as a client reads them off the wire: what a local simulation sends, so it quantises like the server. */
|
|
50
|
+
export declare function quantizeUpdates(ups: readonly EntityUpdate[]): EntityUpdate[];
|
|
51
|
+
/** A snapshot as the server frames it: binary when it carries updates (see the layout above). */
|
|
52
|
+
export declare function encodeSnapshotFrame<S extends SnapshotLike>(snap: S): Uint8Array;
|
|
53
|
+
/** Reads a binary snapshot frame back into the same object a text frame parses to. */
|
|
54
|
+
export declare function decodeSnapshotFrame<S extends SnapshotLike>(frame: ArrayBuffer | Uint8Array): S;
|
|
55
|
+
//# sourceMappingURL=snapshotCodec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotCodec.d.ts","sourceRoot":"","sources":["../../../src/net/snapshotCodec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,sCAAsC;AACtC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAUpC,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,GAAG,IAAI,CAQ9E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAUpE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,YAAY,EAAE,GAAG,UAAU,CAKtE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,EAAE,CAM/D;AAED,+HAA+H;AAC/H,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,EAAE,CAgB5E;AAWD,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAY/E;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,CAAC,CAY9F"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Stage } from '../core/Stage.js';
|
|
2
|
+
/** A plain light's colour (a lamp nobody named one for, the light a body carries): warm, as flame is. */
|
|
3
|
+
export declare const WARM_LIGHT = 16761466;
|
|
4
|
+
export declare class Darkness {
|
|
5
|
+
private readonly stage;
|
|
6
|
+
private color;
|
|
7
|
+
private alpha;
|
|
8
|
+
private readonly scratch;
|
|
9
|
+
private readonly sheet;
|
|
10
|
+
/** Every hole is erased before any colour is laid, or a later hole would wipe an earlier light's colour away. */
|
|
11
|
+
private readonly holes;
|
|
12
|
+
private readonly washes;
|
|
13
|
+
private readonly lamps;
|
|
14
|
+
/** The transient lights (`lights.ts`), reused frame to frame. */
|
|
15
|
+
private readonly flashes;
|
|
16
|
+
private rt;
|
|
17
|
+
/**
|
|
18
|
+
* On the screen layer, laid over the world with its exact transform by the camera (`Camera.follow`), shake included:
|
|
19
|
+
* the night is drawn in world pixels round the unshaken camera and only this layer moves with a shake, so a shaking
|
|
20
|
+
* screen does not redraw the night every frame.
|
|
21
|
+
*/
|
|
22
|
+
private readonly layer;
|
|
23
|
+
private readonly view;
|
|
24
|
+
/** The light the player carries: the only one that follows them, and the last one they lose. */
|
|
25
|
+
private readonly carried;
|
|
26
|
+
private readonly unsubscribeResize;
|
|
27
|
+
/** Lamps that flicker: while one does (or the player carries a light) every frame differs. */
|
|
28
|
+
private flickering;
|
|
29
|
+
/** Something other than the camera changed what the night looks like (a lamp added, dimmed, the window resized). */
|
|
30
|
+
private dirty;
|
|
31
|
+
/** What the last drawn frame was drawn from: the texture, the camera's texel and the shade. */
|
|
32
|
+
private drawn;
|
|
33
|
+
/** The carried light's place at the last redraw (it follows the body). */
|
|
34
|
+
private carriedAt;
|
|
35
|
+
/** When the sheet was last redrawn. */
|
|
36
|
+
private drawnAt;
|
|
37
|
+
/** The world pixel the night's top-left texel stands on. */
|
|
38
|
+
private ox;
|
|
39
|
+
private oy;
|
|
40
|
+
/** The night texture's size in world pixels, and the zoom it was made for. */
|
|
41
|
+
private rtKey;
|
|
42
|
+
/** The lamps' colour turned down (`setLampsDimmed`). */
|
|
43
|
+
private dimLamps;
|
|
44
|
+
/** A phone redraws the night at most `PHONE_MS` apart, whatever moves (`isTouchUi`). */
|
|
45
|
+
private readonly phone;
|
|
46
|
+
constructor(stage: Stage, color: number, alpha: number);
|
|
47
|
+
/** A hole and its colour, added to the night's layers. */
|
|
48
|
+
private light;
|
|
49
|
+
/** A lamp fixed in the world: a torch on a wall, the seep in its chamber, a crystal in a seam, in its own colour. */
|
|
50
|
+
addLamp(x: number, y: number, radius: number, flicker?: number, color?: number): void;
|
|
51
|
+
/**
|
|
52
|
+
* Changes how dark and what colour the sheet is (the outdoor night, `NightShade`, deepens and lifts with the
|
|
53
|
+
* world clock). A sheet with no alpha left is neither drawn nor redrawn, so a map at noon pays nothing for its night.
|
|
54
|
+
*/
|
|
55
|
+
setShade(color: number, alpha: number): void;
|
|
56
|
+
/**
|
|
57
|
+
* Dimmed lamps lay only `LAMP_DIM` of their colour on the dark (a "fewer ambient lights" setting). The holes they
|
|
58
|
+
* cut stay as wide, so a dark place is still seen by them.
|
|
59
|
+
*/
|
|
60
|
+
setLampsDimmed(dim: boolean): void;
|
|
61
|
+
/**
|
|
62
|
+
* The night's own texture, one texel a world pixel and `MARGIN` to spare on each side (a shake, or a camera that
|
|
63
|
+
* moved while a phone waits for its next redraw, never shows its edge), remade when the window or the zoom changes:
|
|
64
|
+
* at zoom 2 or 3 it is a quarter or a ninth of the screen's pixels. True when it was remade.
|
|
65
|
+
*/
|
|
66
|
+
private fit;
|
|
67
|
+
/** Redraws the night with this frame's holes in it, when anything in it changed. `px`/`py` is where the player stands. */
|
|
68
|
+
update(now: number, px: number, py: number, carriedRadius: number): void;
|
|
69
|
+
private hide;
|
|
70
|
+
/** Lays light `l` at world (wx, wy), `r` px across its radius, in the night's world-pixel grid. */
|
|
71
|
+
private place;
|
|
72
|
+
destroy(): void;
|
|
73
|
+
}
|
|
74
|
+
/** What is left of a lamp's glow and colour while lamps are dimmed (`setLampsDimmed`). */
|
|
75
|
+
export declare const LAMP_DIM = 0.35;
|
|
76
|
+
/** World units of light the player carries underground — about five tiles of certainty in any direction. */
|
|
77
|
+
export declare const CARRIED_LIGHT: number;
|
|
78
|
+
//# sourceMappingURL=Darkness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Darkness.d.ts","sourceRoot":"","sources":["../../../src/pixel/Darkness.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AA2C3C,yGAAyG;AACzG,eAAO,MAAM,UAAU,WAAW,CAAC;AAsEnC,qBAAa,QAAQ;IAyCjB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,KAAK;IA1Cf,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,iHAAiH;IACjH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqF;IAC3G,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,EAAE,CAAgB;IAC1B;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,gGAAgG;IAChG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAa;IAC/C,8FAA8F;IAC9F,OAAO,CAAC,UAAU,CAAK;IACvB,oHAAoH;IACpH,OAAO,CAAC,KAAK,CAAQ;IACrB,+FAA+F;IAC/F,OAAO,CAAC,KAAK,CAAM;IACnB,0EAA0E;IAC1E,OAAO,CAAC,SAAS,CAAM;IACvB,uCAAuC;IACvC,OAAO,CAAC,OAAO,CAAa;IAC5B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,CAAK;IACf,OAAO,CAAC,EAAE,CAAK;IACf,8EAA8E;IAC9E,OAAO,CAAC,KAAK,CAAM;IACnB,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAS;IACzB,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAGlB,KAAK,EAAE,KAAK,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM;IAgBvB,0DAA0D;IAC1D,OAAO,CAAC,KAAK;IAUb,qHAAqH;IACrH,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,SAAI,EAAE,KAAK,SAAa,GAAG,IAAI;IAMpF;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAM5C;;;OAGG;IACH,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI;IAMlC;;;;OAIG;IACH,OAAO,CAAC,GAAG;IAaX,0HAA0H;IAC1H,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAiExE,OAAO,CAAC,IAAI;IAKZ,mGAAmG;IACnG,OAAO,CAAC,KAAK;IAmBb,OAAO,IAAI,IAAI;CAOhB;AAED,0FAA0F;AAC1F,eAAO,MAAM,QAAQ,OAAO,CAAC;AAE7B,4GAA4G;AAC5G,eAAO,MAAM,aAAa,QAAgB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Stage } from '../core/Stage.js';
|
|
2
|
+
import type { WorldObject } from '../core/types.js';
|
|
3
|
+
import type { PackedPixels } from './spritePixels.js';
|
|
4
|
+
/** What a dying body hands over: its feet in the world, how it was drawn, and the pixels of its last frame. */
|
|
5
|
+
export interface DeathPixels {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
/** World pixels per sprite pixel (the body's `scale`), negative when the body faced left. */
|
|
9
|
+
scaleX: number;
|
|
10
|
+
scaleY: number;
|
|
11
|
+
pixels: PackedPixels;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A dead body evaporating: it breaks into its own pixels, which let go top rows first, rise, scatter a little and
|
|
15
|
+
* give out, while the body itself is hidden by the caller. Every particle is one whole world pixel in a colour read
|
|
16
|
+
* off the body's last frame, painted at 12 fps like every other effect; it pales up its own ramp as it rises and
|
|
17
|
+
* steps its alpha down instead of melting, so it stays pixel art the whole way. A cloud is one `PixelLayer` (a pooled
|
|
18
|
+
* texture) rather than a Graphics of up to 1,400 rects.
|
|
19
|
+
*/
|
|
20
|
+
export declare class Evaporation {
|
|
21
|
+
private readonly scene;
|
|
22
|
+
private readonly track;
|
|
23
|
+
private readonly clouds;
|
|
24
|
+
private live;
|
|
25
|
+
constructor(scene: Stage, track: (object: WorldObject) => void);
|
|
26
|
+
play(d: DeathPixels, big: boolean, now: number): void;
|
|
27
|
+
update(now: number): void;
|
|
28
|
+
private draw;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=Evaporation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Evaporation.d.ts","sourceRoot":"","sources":["../../../src/pixel/Evaporation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,+GAA+G;AAC/G,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;CACtB;AAgCD;;;;;;GAMG;AACH,qBAAa,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IALxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,IAAI,CAAK;gBAGE,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI;IAGvD,IAAI,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAqDrD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAazB,OAAO,CAAC,IAAI;CA8Bb"}
|