@crowdedkingdoms/crowdyjs 18.10.0 → 18.12.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +69 -0
- package/README.md +91 -30
- package/dist/crowdy-client.d.ts +11 -3
- package/dist/crowdy-client.d.ts.map +1 -1
- package/dist/crowdy-client.js +25 -4
- package/dist/default-origin.d.ts +6 -6
- package/dist/default-origin.d.ts.map +1 -1
- package/dist/default-origin.js +6 -6
- package/dist/domains/exec.d.ts +6 -0
- package/dist/domains/exec.d.ts.map +1 -1
- package/dist/domains/exec.js +28 -2
- package/dist/generated/graphql-schema.d.ts +1 -1
- package/dist/generated/graphql-schema.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/kit/index.d.ts +22 -2
- package/dist/kit/index.d.ts.map +1 -1
- package/dist/kit/index.js +22 -2
- package/dist/kit/kit.d.ts +141 -7
- package/dist/kit/kit.d.ts.map +1 -1
- package/dist/kit/kit.js +138 -4
- package/dist/kit/mirror.d.ts +111 -0
- package/dist/kit/mirror.d.ts.map +1 -0
- package/dist/kit/mirror.js +295 -0
- package/dist/kit/modules/arenas.d.ts +87 -0
- package/dist/kit/modules/arenas.d.ts.map +1 -0
- package/dist/kit/modules/arenas.js +66 -0
- package/dist/kit/modules/encounters.d.ts +75 -0
- package/dist/kit/modules/encounters.d.ts.map +1 -0
- package/dist/kit/modules/encounters.js +57 -0
- package/dist/kit/modules/instances.d.ts +78 -0
- package/dist/kit/modules/instances.d.ts.map +1 -0
- package/dist/kit/modules/instances.js +72 -0
- package/dist/kit/modules/leaderboards.d.ts +79 -0
- package/dist/kit/modules/leaderboards.d.ts.map +1 -0
- package/dist/kit/modules/leaderboards.js +69 -0
- package/dist/kit/modules/matchmaking.d.ts +107 -0
- package/dist/kit/modules/matchmaking.d.ts.map +1 -0
- package/dist/kit/modules/matchmaking.js +109 -0
- package/dist/kit/modules/minigames.d.ts +59 -0
- package/dist/kit/modules/minigames.d.ts.map +1 -0
- package/dist/kit/modules/minigames.js +55 -0
- package/dist/kit/modules/npcs.d.ts +80 -0
- package/dist/kit/modules/npcs.d.ts.map +1 -0
- package/dist/kit/modules/npcs.js +102 -0
- package/dist/kit/modules/presence.d.ts +81 -0
- package/dist/kit/modules/presence.d.ts.map +1 -0
- package/dist/kit/modules/presence.js +99 -0
- package/dist/kit/modules/racing.d.ts +131 -0
- package/dist/kit/modules/racing.d.ts.map +1 -0
- package/dist/kit/modules/racing.js +113 -0
- package/dist/kit/modules/sessions.d.ts +91 -0
- package/dist/kit/modules/sessions.d.ts.map +1 -0
- package/dist/kit/modules/sessions.js +84 -0
- package/dist/kit/modules/tables.d.ts +76 -0
- package/dist/kit/modules/tables.d.ts.map +1 -0
- package/dist/kit/modules/tables.js +73 -0
- package/dist/kit/modules/tactics.d.ts +79 -0
- package/dist/kit/modules/tactics.d.ts.map +1 -0
- package/dist/kit/modules/tactics.js +67 -0
- package/dist/kit/modules/territory.d.ts +92 -0
- package/dist/kit/modules/territory.d.ts.map +1 -0
- package/dist/kit/modules/territory.js +84 -0
- package/dist/kit/modules/world.d.ts +69 -0
- package/dist/kit/modules/world.d.ts.map +1 -0
- package/dist/kit/modules/world.js +35 -0
- package/dist/kit/protocol.d.ts +128 -0
- package/dist/kit/protocol.d.ts.map +1 -0
- package/dist/kit/protocol.js +171 -0
- package/dist/kit/realtime.d.ts +72 -0
- package/dist/kit/realtime.d.ts.map +1 -0
- package/dist/kit/realtime.js +108 -0
- package/dist/kit/runtime.d.ts +90 -0
- package/dist/kit/runtime.d.ts.map +1 -0
- package/dist/kit/runtime.js +194 -0
- package/dist/kit/store.d.ts +20 -0
- package/dist/kit/store.d.ts.map +1 -0
- package/dist/kit/store.js +25 -0
- package/dist/kit/topology.d.ts +69 -0
- package/dist/kit/topology.d.ts.map +1 -0
- package/dist/kit/topology.js +156 -0
- package/dist/kit/wire.d.ts +45 -5
- package/dist/kit/wire.d.ts.map +1 -1
- package/dist/kit/wire.js +51 -5
- package/dist/live-coding/assets/browser-authoring-index.json +1 -1
- package/dist/live-coding/assets/manifest.json +1 -1
- package/dist/live-coding/browser-authoring-index.generated.d.ts.map +1 -1
- package/dist/live-coding/browser-authoring-index.generated.js +5092 -45
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
/** Races and ball games (the `racing` starter, types `race` and `ball`). */
|
|
3
|
+
export class RacingKit {
|
|
4
|
+
constructor(runtime, raceType = 'race', ballType = 'ball') {
|
|
5
|
+
this.runtime = runtime;
|
|
6
|
+
this.raceType = raceType;
|
|
7
|
+
this.ballType = ballType;
|
|
8
|
+
this.races = new Map();
|
|
9
|
+
this.matches = new Map();
|
|
10
|
+
}
|
|
11
|
+
/** The race with key `key`. */
|
|
12
|
+
race(key) {
|
|
13
|
+
let r = this.races.get(key);
|
|
14
|
+
if (!r) {
|
|
15
|
+
r = new Race(this.runtime, this.raceType, key);
|
|
16
|
+
this.races.set(key, r);
|
|
17
|
+
}
|
|
18
|
+
return r;
|
|
19
|
+
}
|
|
20
|
+
/** The ball game with key `key`. */
|
|
21
|
+
ball(key) {
|
|
22
|
+
let m = this.matches.get(key);
|
|
23
|
+
if (!m) {
|
|
24
|
+
m = new BallMatch(this.runtime, this.ballType, key);
|
|
25
|
+
this.matches.set(key, m);
|
|
26
|
+
}
|
|
27
|
+
return m;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One race: a course of gates passed in order, a countdown, laps and splits, places in finishing
|
|
32
|
+
* order, and a ghost of the winner's run. A client reports where its racer is ({@link pos}, a few
|
|
33
|
+
* times a second) and the hub times the positions its movement warden kept. `view.onEvent` hears
|
|
34
|
+
* `go`, `timing` (with its `stage`: `started`, `checkpoint`, `lap`, `finished`), `violation` and
|
|
35
|
+
* `finished`.
|
|
36
|
+
*/
|
|
37
|
+
export class Race {
|
|
38
|
+
constructor(runtime, nodeType, key) {
|
|
39
|
+
this.runtime = runtime;
|
|
40
|
+
this.nodeType = nodeType;
|
|
41
|
+
this.key = key;
|
|
42
|
+
this.view = new HubState(runtime, nodeType, key, 'race');
|
|
43
|
+
}
|
|
44
|
+
watch() {
|
|
45
|
+
return this.view.start();
|
|
46
|
+
}
|
|
47
|
+
unwatch() {
|
|
48
|
+
return this.view.stop();
|
|
49
|
+
}
|
|
50
|
+
/** @throws {KitError} `started`, `full`. */
|
|
51
|
+
join() {
|
|
52
|
+
return this.runtime.call(this.nodeType, this.key, 'join', null, { action: true });
|
|
53
|
+
}
|
|
54
|
+
leave() {
|
|
55
|
+
return this.runtime.call(this.nodeType, this.key, 'leave', null, { action: true });
|
|
56
|
+
}
|
|
57
|
+
/** Starts the countdown (the first to join, or a developer). @throws {KitError} `forbidden`, `started`. */
|
|
58
|
+
start() {
|
|
59
|
+
return this.runtime.call(this.nodeType, this.key, 'start', null, { action: true });
|
|
60
|
+
}
|
|
61
|
+
/** Reports where the racer is; resolves with the position the hub kept. */
|
|
62
|
+
pos(x, z) {
|
|
63
|
+
return this.runtime.call(this.nodeType, this.key, 'pos', { x, z });
|
|
64
|
+
}
|
|
65
|
+
/** The winner's run, once there is one. */
|
|
66
|
+
ghost() {
|
|
67
|
+
return this.runtime.call(this.nodeType, this.key, 'ghost');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* One ball game: two teams, a ball the hub moves (passes, shots, friction, the side walls),
|
|
72
|
+
* possession (claim a loose ball near it, or take it from an opponent whose protection is over), a
|
|
73
|
+
* goal in each end wall. `view.onEvent` hears `claimed`, `passed`, `shot`, `goal`, `violation` and
|
|
74
|
+
* `finished`.
|
|
75
|
+
*/
|
|
76
|
+
export class BallMatch {
|
|
77
|
+
constructor(runtime, nodeType, key) {
|
|
78
|
+
this.runtime = runtime;
|
|
79
|
+
this.nodeType = nodeType;
|
|
80
|
+
this.key = key;
|
|
81
|
+
this.view = new HubState(runtime, nodeType, key, 'ball');
|
|
82
|
+
}
|
|
83
|
+
watch() {
|
|
84
|
+
return this.view.start();
|
|
85
|
+
}
|
|
86
|
+
unwatch() {
|
|
87
|
+
return this.view.stop();
|
|
88
|
+
}
|
|
89
|
+
/** @throws {KitError} `bad_request` (red or blue), `full`, `started`. */
|
|
90
|
+
join(team) {
|
|
91
|
+
return this.runtime.call(this.nodeType, this.key, 'join', { team }, { action: true });
|
|
92
|
+
}
|
|
93
|
+
/** @throws {KitError} `forbidden`, `not_ready` (each team needs a player), `started`. */
|
|
94
|
+
start() {
|
|
95
|
+
return this.runtime.call(this.nodeType, this.key, 'start', null, { action: true });
|
|
96
|
+
}
|
|
97
|
+
/** Reports where the player is; resolves with the position the hub kept. */
|
|
98
|
+
pos(x, z) {
|
|
99
|
+
return this.runtime.call(this.nodeType, this.key, 'pos', { x, z });
|
|
100
|
+
}
|
|
101
|
+
/** Claims the ball. @throws {KitError} `out_of_reach`, `cooldown` (too soon, or its holder's protection), `conflict` (a teammate has it). */
|
|
102
|
+
claim() {
|
|
103
|
+
return this.runtime.call(this.nodeType, this.key, 'claim', null, { action: true });
|
|
104
|
+
}
|
|
105
|
+
/** Passes to a teammate. @throws {KitError} `conflict` (not the holder), `bad_request` (not a teammate). */
|
|
106
|
+
pass(to) {
|
|
107
|
+
return this.runtime.call(this.nodeType, this.key, 'pass', { to: Number(to) }, { action: true });
|
|
108
|
+
}
|
|
109
|
+
/** Shoots toward `(x, z)`. @throws {KitError} `conflict` (not the holder). */
|
|
110
|
+
shoot(x, z) {
|
|
111
|
+
return this.runtime.call(this.nodeType, this.key, 'shoot', { x, z }, { action: true });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
import type { KitRuntime } from '../runtime.js';
|
|
3
|
+
export type SessionPhase = 'lobby' | 'playing' | 'finished';
|
|
4
|
+
export interface SessionConfig {
|
|
5
|
+
min_players: number;
|
|
6
|
+
max_players: number;
|
|
7
|
+
turn_ms: number;
|
|
8
|
+
target_score: number;
|
|
9
|
+
max_rounds: number;
|
|
10
|
+
/** A lobby nobody joins closes after this long. */
|
|
11
|
+
lobby_idle_ms: number;
|
|
12
|
+
}
|
|
13
|
+
export interface SessionMove {
|
|
14
|
+
player: number;
|
|
15
|
+
round: number;
|
|
16
|
+
/** null when the player ran out of time. */
|
|
17
|
+
points: number | null;
|
|
18
|
+
}
|
|
19
|
+
/** A session as everyone sees it: what `state` answers and the `session` topic carries. */
|
|
20
|
+
export interface SessionState {
|
|
21
|
+
config: SessionConfig;
|
|
22
|
+
phase: SessionPhase;
|
|
23
|
+
/** Only these players may join, when set (a matchmaker's reservation). */
|
|
24
|
+
reserved: number[];
|
|
25
|
+
players: number[];
|
|
26
|
+
ready: number[];
|
|
27
|
+
connected: number[];
|
|
28
|
+
host: number | null;
|
|
29
|
+
turn: number;
|
|
30
|
+
round: number;
|
|
31
|
+
/** Scores by player id (the keys are the ids as text). */
|
|
32
|
+
scores: Record<string, number>;
|
|
33
|
+
winner: number | null;
|
|
34
|
+
moves: SessionMove[];
|
|
35
|
+
}
|
|
36
|
+
/** Where the session hubs are: type `session`, one per session key, in the starter pack. */
|
|
37
|
+
export interface SessionsKitOptions {
|
|
38
|
+
nodeType?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Game sessions (the `session` starter): {@link open} one by its key. */
|
|
41
|
+
export declare class SessionsKit {
|
|
42
|
+
private readonly runtime;
|
|
43
|
+
private readonly sessions;
|
|
44
|
+
private readonly nodeType;
|
|
45
|
+
constructor(runtime: KitRuntime, options?: SessionsKitOptions);
|
|
46
|
+
/** The session with key `key` (a matchmaker's `matched.session`, or one a game names). */
|
|
47
|
+
open(key: string): GameSessionHandle;
|
|
48
|
+
}
|
|
49
|
+
/** Whose turn it is in `state`, while it plays. */
|
|
50
|
+
export declare function sessionTurn(state: SessionState | undefined): number | null;
|
|
51
|
+
/**
|
|
52
|
+
* One session from lobby to result: join, ready up, the host starts, players act in turn
|
|
53
|
+
* under a time limit, and the hub keeps the score and names the winner. {@link state} mirrors
|
|
54
|
+
* the `session` topic; every change is a snapshot on it.
|
|
55
|
+
*/
|
|
56
|
+
export declare class GameSessionHandle {
|
|
57
|
+
private readonly runtime;
|
|
58
|
+
readonly nodeType: string;
|
|
59
|
+
readonly key: string;
|
|
60
|
+
/** The `session` topic: the whole session. {@link watch} starts it. */
|
|
61
|
+
readonly state: HubState<SessionState>;
|
|
62
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string);
|
|
63
|
+
/** Mirrors the session; resolves with it. */
|
|
64
|
+
watch(): Promise<SessionState>;
|
|
65
|
+
unwatch(): Promise<void>;
|
|
66
|
+
/** Whose turn it is, as the mirror last saw it. */
|
|
67
|
+
currentTurn(): number | null;
|
|
68
|
+
/** Whether it is the player's turn, as the mirror last saw it. */
|
|
69
|
+
isMyTurn(): Promise<boolean>;
|
|
70
|
+
/** @throws {KitError} `started`, `reserved`, `full`. */
|
|
71
|
+
join(): Promise<boolean>;
|
|
72
|
+
leave(): Promise<boolean>;
|
|
73
|
+
/** @throws {KitError} `not_member` (join the lobby first). */
|
|
74
|
+
ready(ready?: boolean): Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Starts the session (the host, once everyone is ready).
|
|
77
|
+
* @throws {KitError} `forbidden` (not the host), `not_ready`, `too_few_players`, `started`.
|
|
78
|
+
*/
|
|
79
|
+
startGame(): Promise<boolean>;
|
|
80
|
+
/**
|
|
81
|
+
* Plays the player's move (the starter's is `{ points }`, 0 to 3); resolves with whether play
|
|
82
|
+
* goes on. Sent with an action id: a move resent after a lost reply is played once.
|
|
83
|
+
* @throws {KitError} `not_your_turn`, `invalid_move` (the game's rule).
|
|
84
|
+
*/
|
|
85
|
+
act<A extends object = {
|
|
86
|
+
points: number;
|
|
87
|
+
}>(action: A): Promise<boolean>;
|
|
88
|
+
/** Changes the config: the host in the lobby, or a developer. */
|
|
89
|
+
configure(config: Partial<SessionConfig>): Promise<boolean>;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/kit/modules/sessions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
/** Game sessions (the `session` starter): {@link open} one by its key. */
|
|
3
|
+
export class SessionsKit {
|
|
4
|
+
constructor(runtime, options = {}) {
|
|
5
|
+
this.runtime = runtime;
|
|
6
|
+
this.sessions = new Map();
|
|
7
|
+
this.nodeType = options.nodeType ?? 'session';
|
|
8
|
+
}
|
|
9
|
+
/** The session with key `key` (a matchmaker's `matched.session`, or one a game names). */
|
|
10
|
+
open(key) {
|
|
11
|
+
let s = this.sessions.get(key);
|
|
12
|
+
if (!s) {
|
|
13
|
+
s = new GameSessionHandle(this.runtime, this.nodeType, key);
|
|
14
|
+
this.sessions.set(key, s);
|
|
15
|
+
}
|
|
16
|
+
return s;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Whose turn it is in `state`, while it plays. */
|
|
20
|
+
export function sessionTurn(state) {
|
|
21
|
+
if (!state || state.phase !== 'playing')
|
|
22
|
+
return null;
|
|
23
|
+
return state.players[state.turn] ?? null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* One session from lobby to result: join, ready up, the host starts, players act in turn
|
|
27
|
+
* under a time limit, and the hub keeps the score and names the winner. {@link state} mirrors
|
|
28
|
+
* the `session` topic; every change is a snapshot on it.
|
|
29
|
+
*/
|
|
30
|
+
export class GameSessionHandle {
|
|
31
|
+
constructor(runtime, nodeType, key) {
|
|
32
|
+
this.runtime = runtime;
|
|
33
|
+
this.nodeType = nodeType;
|
|
34
|
+
this.key = key;
|
|
35
|
+
this.state = new HubState(runtime, nodeType, key, 'session');
|
|
36
|
+
}
|
|
37
|
+
/** Mirrors the session; resolves with it. */
|
|
38
|
+
watch() {
|
|
39
|
+
return this.state.start();
|
|
40
|
+
}
|
|
41
|
+
unwatch() {
|
|
42
|
+
return this.state.stop();
|
|
43
|
+
}
|
|
44
|
+
/** Whose turn it is, as the mirror last saw it. */
|
|
45
|
+
currentTurn() {
|
|
46
|
+
return sessionTurn(this.state.get());
|
|
47
|
+
}
|
|
48
|
+
/** Whether it is the player's turn, as the mirror last saw it. */
|
|
49
|
+
async isMyTurn() {
|
|
50
|
+
const me = await this.runtime.playerId();
|
|
51
|
+
const turn = this.currentTurn();
|
|
52
|
+
return me !== null && turn !== null && String(turn) === me;
|
|
53
|
+
}
|
|
54
|
+
/** @throws {KitError} `started`, `reserved`, `full`. */
|
|
55
|
+
join() {
|
|
56
|
+
return this.runtime.call(this.nodeType, this.key, 'join', null, { action: true });
|
|
57
|
+
}
|
|
58
|
+
leave() {
|
|
59
|
+
return this.runtime.call(this.nodeType, this.key, 'leave', null, { action: true });
|
|
60
|
+
}
|
|
61
|
+
/** @throws {KitError} `not_member` (join the lobby first). */
|
|
62
|
+
ready(ready = true) {
|
|
63
|
+
return this.runtime.call(this.nodeType, this.key, 'ready', { ready }, { action: true });
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Starts the session (the host, once everyone is ready).
|
|
67
|
+
* @throws {KitError} `forbidden` (not the host), `not_ready`, `too_few_players`, `started`.
|
|
68
|
+
*/
|
|
69
|
+
startGame() {
|
|
70
|
+
return this.runtime.call(this.nodeType, this.key, 'start', null, { action: true });
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Plays the player's move (the starter's is `{ points }`, 0 to 3); resolves with whether play
|
|
74
|
+
* goes on. Sent with an action id: a move resent after a lost reply is played once.
|
|
75
|
+
* @throws {KitError} `not_your_turn`, `invalid_move` (the game's rule).
|
|
76
|
+
*/
|
|
77
|
+
act(action) {
|
|
78
|
+
return this.runtime.call(this.nodeType, this.key, 'act', action, { action: true });
|
|
79
|
+
}
|
|
80
|
+
/** Changes the config: the host in the lobby, or a developer. */
|
|
81
|
+
configure(config) {
|
|
82
|
+
return this.runtime.call(this.nodeType, this.key, 'configure', config, { action: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { HubState, PrivateState } from '../mirror.js';
|
|
2
|
+
import type { KitRuntime } from '../runtime.js';
|
|
3
|
+
/** The last thing done at a table. */
|
|
4
|
+
export interface CardPlayed {
|
|
5
|
+
player: number;
|
|
6
|
+
card: string | null;
|
|
7
|
+
/** `play`, `draw` or `pass`. */
|
|
8
|
+
action: string;
|
|
9
|
+
}
|
|
10
|
+
/** What the `table` topic carries: the public table, never a hand. */
|
|
11
|
+
export interface CardTableView {
|
|
12
|
+
phase: 'lobby' | 'playing' | 'finished';
|
|
13
|
+
players: number[];
|
|
14
|
+
host: number | null;
|
|
15
|
+
/** Cards in each player's hand, by player id (the keys are the ids as text). */
|
|
16
|
+
hand_counts: Record<string, number>;
|
|
17
|
+
top: string | null;
|
|
18
|
+
/** The suit in play (an eight names one). */
|
|
19
|
+
suit: string | null;
|
|
20
|
+
draw_count: number;
|
|
21
|
+
turn: number | null;
|
|
22
|
+
turn_ends: number | null;
|
|
23
|
+
last: CardPlayed | null;
|
|
24
|
+
winner: number | null;
|
|
25
|
+
scores: Record<string, number>;
|
|
26
|
+
}
|
|
27
|
+
/** The caller's hand, as `hand` answers it. */
|
|
28
|
+
export interface CardHand {
|
|
29
|
+
cards: string[];
|
|
30
|
+
/** The cards the rule lets them play now. */
|
|
31
|
+
playable: string[];
|
|
32
|
+
/** Whether they drew this turn (and may pass). */
|
|
33
|
+
drawn: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Card tables (the `deck` starter): {@link table} by key. */
|
|
36
|
+
export declare class TablesKit {
|
|
37
|
+
private readonly runtime;
|
|
38
|
+
private readonly nodeType;
|
|
39
|
+
private readonly tables;
|
|
40
|
+
constructor(runtime: KitRuntime, nodeType?: string);
|
|
41
|
+
table(key: string): CardTable;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One table with true hidden information: the deck and every hand live in the hub, and a player
|
|
45
|
+
* reads only their own hand ({@link hand}, read again whenever the hub's `changed` message names
|
|
46
|
+
* them). The starter's game is crazy eights.
|
|
47
|
+
*/
|
|
48
|
+
export declare class CardTable {
|
|
49
|
+
private readonly runtime;
|
|
50
|
+
readonly nodeType: string;
|
|
51
|
+
readonly key: string;
|
|
52
|
+
/** The `table` topic: the public table. */
|
|
53
|
+
readonly table: HubState<CardTableView>;
|
|
54
|
+
/** The caller's hand: never on the topic. */
|
|
55
|
+
readonly hand: PrivateState<CardHand>;
|
|
56
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string);
|
|
57
|
+
/** Mirrors the table and the caller's hand. */
|
|
58
|
+
watch(): Promise<CardTableView>;
|
|
59
|
+
unwatch(): Promise<void>;
|
|
60
|
+
/** @throws {KitError} `started`, `full`. */
|
|
61
|
+
join(): Promise<boolean>;
|
|
62
|
+
leave(): Promise<boolean>;
|
|
63
|
+
/** Deals (the host, from two players). @throws {KitError} `forbidden`, `too_few_players`, `started`. */
|
|
64
|
+
start(): Promise<boolean>;
|
|
65
|
+
/**
|
|
66
|
+
* Plays a card from the caller's hand; an eight names `suit` (`S`, `H`, `D` or `C`).
|
|
67
|
+
* @throws {KitError} `not_your_turn`, `not_found` (not in the hand), `not_playable` (the rule),
|
|
68
|
+
* `bad_request` (an eight without a suit).
|
|
69
|
+
*/
|
|
70
|
+
play(card: string, suit?: string): Promise<boolean>;
|
|
71
|
+
/** Draws a card (once a turn). @throws {KitError} `not_your_turn`, `conflict`, `empty`. */
|
|
72
|
+
draw(): Promise<boolean>;
|
|
73
|
+
/** Passes, after drawing. @throws {KitError} `conflict` (draw first). */
|
|
74
|
+
pass(): Promise<boolean>;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../src/kit/modules/tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,sCAAsC;AACtC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,+CAA+C;AAC/C,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kDAAkD;IAClD,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,8DAA8D;AAC9D,qBAAa,SAAS;IAIlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;gBAGpC,OAAO,EAAE,UAAU,EACnB,QAAQ,SAAS;IAGpC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;CAQ9B;AAED;;;;GAIG;AACH,qBAAa,SAAS;IAOlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM;IARtB,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IACxC,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAGnB,OAAO,EAAE,UAAU,EAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM;IAYtB,+CAA+C;IACzC,KAAK,IAAI,OAAO,CAAC,aAAa,CAAC;IAK/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,4CAA4C;IAC5C,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxB,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzB,wGAAwG;IACxG,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzB;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD,2FAA2F;IAC3F,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxB,yEAAyE;IACzE,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;CAGzB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { HubState, PrivateState } from '../mirror.js';
|
|
2
|
+
/** Card tables (the `deck` starter): {@link table} by key. */
|
|
3
|
+
export class TablesKit {
|
|
4
|
+
constructor(runtime, nodeType = 'deck') {
|
|
5
|
+
this.runtime = runtime;
|
|
6
|
+
this.nodeType = nodeType;
|
|
7
|
+
this.tables = new Map();
|
|
8
|
+
}
|
|
9
|
+
table(key) {
|
|
10
|
+
let t = this.tables.get(key);
|
|
11
|
+
if (!t) {
|
|
12
|
+
t = new CardTable(this.runtime, this.nodeType, key);
|
|
13
|
+
this.tables.set(key, t);
|
|
14
|
+
}
|
|
15
|
+
return t;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* One table with true hidden information: the deck and every hand live in the hub, and a player
|
|
20
|
+
* reads only their own hand ({@link hand}, read again whenever the hub's `changed` message names
|
|
21
|
+
* them). The starter's game is crazy eights.
|
|
22
|
+
*/
|
|
23
|
+
export class CardTable {
|
|
24
|
+
constructor(runtime, nodeType, key) {
|
|
25
|
+
this.runtime = runtime;
|
|
26
|
+
this.nodeType = nodeType;
|
|
27
|
+
this.key = key;
|
|
28
|
+
this.table = new HubState(runtime, nodeType, key, 'table');
|
|
29
|
+
this.hand = new PrivateState(runtime, {
|
|
30
|
+
nodeType,
|
|
31
|
+
key,
|
|
32
|
+
topic: 'table',
|
|
33
|
+
what: 'hand',
|
|
34
|
+
read: () => runtime.call(nodeType, key, 'hand'),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/** Mirrors the table and the caller's hand. */
|
|
38
|
+
async watch() {
|
|
39
|
+
const [view] = await Promise.all([this.table.start(), this.hand.start()]);
|
|
40
|
+
return view;
|
|
41
|
+
}
|
|
42
|
+
async unwatch() {
|
|
43
|
+
await Promise.all([this.table.stop(), this.hand.stop()]);
|
|
44
|
+
}
|
|
45
|
+
/** @throws {KitError} `started`, `full`. */
|
|
46
|
+
join() {
|
|
47
|
+
return this.runtime.call(this.nodeType, this.key, 'join', null, { action: true });
|
|
48
|
+
}
|
|
49
|
+
leave() {
|
|
50
|
+
return this.runtime.call(this.nodeType, this.key, 'leave', null, { action: true });
|
|
51
|
+
}
|
|
52
|
+
/** Deals (the host, from two players). @throws {KitError} `forbidden`, `too_few_players`, `started`. */
|
|
53
|
+
start() {
|
|
54
|
+
return this.runtime.call(this.nodeType, this.key, 'start', null, { action: true });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Plays a card from the caller's hand; an eight names `suit` (`S`, `H`, `D` or `C`).
|
|
58
|
+
* @throws {KitError} `not_your_turn`, `not_found` (not in the hand), `not_playable` (the rule),
|
|
59
|
+
* `bad_request` (an eight without a suit).
|
|
60
|
+
*/
|
|
61
|
+
play(card, suit) {
|
|
62
|
+
const args = suit === undefined ? { card } : { card, suit };
|
|
63
|
+
return this.runtime.call(this.nodeType, this.key, 'play', args, { action: true });
|
|
64
|
+
}
|
|
65
|
+
/** Draws a card (once a turn). @throws {KitError} `not_your_turn`, `conflict`, `empty`. */
|
|
66
|
+
draw() {
|
|
67
|
+
return this.runtime.call(this.nodeType, this.key, 'draw', null, { action: true });
|
|
68
|
+
}
|
|
69
|
+
/** Passes, after drawing. @throws {KitError} `conflict` (draw first). */
|
|
70
|
+
pass() {
|
|
71
|
+
return this.runtime.call(this.nodeType, this.key, 'pass', null, { action: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
import type { KitRuntime } from '../runtime.js';
|
|
3
|
+
export interface TacticsUnitStats {
|
|
4
|
+
hp: number;
|
|
5
|
+
attack: number;
|
|
6
|
+
defense: number;
|
|
7
|
+
moves: number;
|
|
8
|
+
range: number;
|
|
9
|
+
}
|
|
10
|
+
export interface TacticsUnit {
|
|
11
|
+
id: number;
|
|
12
|
+
team: 'players' | 'enemies';
|
|
13
|
+
/** The player it belongs to; null for the hub's. */
|
|
14
|
+
owner: number | null;
|
|
15
|
+
x: number;
|
|
16
|
+
z: number;
|
|
17
|
+
hp: number;
|
|
18
|
+
stats: TacticsUnitStats;
|
|
19
|
+
moved: boolean;
|
|
20
|
+
acted: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** What the `battle` topic carries. */
|
|
23
|
+
export interface TacticsBattleView {
|
|
24
|
+
phase: 'lobby' | 'players' | 'enemies' | 'won' | 'lost';
|
|
25
|
+
round: number;
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
obstacles: Array<[number, number]>;
|
|
29
|
+
units: TacticsUnit[];
|
|
30
|
+
players: number[];
|
|
31
|
+
host: number | null;
|
|
32
|
+
/** Players who ended this phase's turn. */
|
|
33
|
+
ended_turn: number[];
|
|
34
|
+
/** When the players' phase runs out (Unix ms). */
|
|
35
|
+
phase_ends: number | null;
|
|
36
|
+
log: string[];
|
|
37
|
+
}
|
|
38
|
+
/** Grid battles (the `tactics` starter): {@link battle} by key. */
|
|
39
|
+
export declare class TacticsKit {
|
|
40
|
+
private readonly runtime;
|
|
41
|
+
private readonly nodeType;
|
|
42
|
+
private readonly battles;
|
|
43
|
+
constructor(runtime: KitRuntime, nodeType?: string);
|
|
44
|
+
battle(key: string): TacticsBattle;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One battle in turns: each player's unit moves (within its move points, around obstacles and
|
|
48
|
+
* units) and attacks once a phase; when every player ends their turn, or the phase's time runs
|
|
49
|
+
* out, the hub's units act. The battle ends when one side has no unit standing.
|
|
50
|
+
*/
|
|
51
|
+
export declare class TacticsBattle {
|
|
52
|
+
private readonly runtime;
|
|
53
|
+
readonly nodeType: string;
|
|
54
|
+
readonly key: string;
|
|
55
|
+
/** The `battle` topic: the whole battle. */
|
|
56
|
+
readonly battle: HubState<TacticsBattleView>;
|
|
57
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string);
|
|
58
|
+
watch(): Promise<TacticsBattleView>;
|
|
59
|
+
unwatch(): Promise<void>;
|
|
60
|
+
/** The player's units, as the mirror last saw them. */
|
|
61
|
+
myUnits(): Promise<TacticsUnit[]>;
|
|
62
|
+
/** @throws {KitError} `started`, `full`. */
|
|
63
|
+
join(): Promise<boolean>;
|
|
64
|
+
/** @throws {KitError} `forbidden` (the host starts it), `started`. */
|
|
65
|
+
start(): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Moves the player's unit to `(x, z)`.
|
|
68
|
+
* @throws {KitError} `not_your_turn`, `conflict` (it moved), `out_of_reach`, `not_member`.
|
|
69
|
+
*/
|
|
70
|
+
move(x: number, z: number): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Attacks unit `target` with the player's unit.
|
|
73
|
+
* @throws {KitError} `not_your_turn`, `conflict` (it attacked), `out_of_reach`, `forbidden` (its own side), `not_found`.
|
|
74
|
+
*/
|
|
75
|
+
attack(target: number): Promise<boolean>;
|
|
76
|
+
/** Ends the player's turn this phase. */
|
|
77
|
+
endTurn(): Promise<boolean>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=tactics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tactics.d.ts","sourceRoot":"","sources":["../../../src/kit/modules/tactics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,oDAAoD;IACpD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,kDAAkD;IAClD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AAED,mEAAmE;AACnE,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;gBAGzC,OAAO,EAAE,UAAU,EACnB,QAAQ,SAAY;IAGvC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;CAQnC;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAKtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM;IANtB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;gBAG1B,OAAO,EAAE,UAAU,EAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM;IAKtB,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAInC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,uDAAuD;IACjD,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAKvC,4CAA4C;IAC5C,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxB,sEAAsE;IACtE,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzB;;;OAGG;IACH,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5C;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,yCAAyC;IACzC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;CAG5B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
/** Grid battles (the `tactics` starter): {@link battle} by key. */
|
|
3
|
+
export class TacticsKit {
|
|
4
|
+
constructor(runtime, nodeType = 'tactics') {
|
|
5
|
+
this.runtime = runtime;
|
|
6
|
+
this.nodeType = nodeType;
|
|
7
|
+
this.battles = new Map();
|
|
8
|
+
}
|
|
9
|
+
battle(key) {
|
|
10
|
+
let b = this.battles.get(key);
|
|
11
|
+
if (!b) {
|
|
12
|
+
b = new TacticsBattle(this.runtime, this.nodeType, key);
|
|
13
|
+
this.battles.set(key, b);
|
|
14
|
+
}
|
|
15
|
+
return b;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* One battle in turns: each player's unit moves (within its move points, around obstacles and
|
|
20
|
+
* units) and attacks once a phase; when every player ends their turn, or the phase's time runs
|
|
21
|
+
* out, the hub's units act. The battle ends when one side has no unit standing.
|
|
22
|
+
*/
|
|
23
|
+
export class TacticsBattle {
|
|
24
|
+
constructor(runtime, nodeType, key) {
|
|
25
|
+
this.runtime = runtime;
|
|
26
|
+
this.nodeType = nodeType;
|
|
27
|
+
this.key = key;
|
|
28
|
+
this.battle = new HubState(runtime, nodeType, key, 'battle');
|
|
29
|
+
}
|
|
30
|
+
watch() {
|
|
31
|
+
return this.battle.start();
|
|
32
|
+
}
|
|
33
|
+
unwatch() {
|
|
34
|
+
return this.battle.stop();
|
|
35
|
+
}
|
|
36
|
+
/** The player's units, as the mirror last saw them. */
|
|
37
|
+
async myUnits() {
|
|
38
|
+
const me = await this.runtime.playerId();
|
|
39
|
+
return (this.battle.get()?.units ?? []).filter((u) => me !== null && String(u.owner) === me);
|
|
40
|
+
}
|
|
41
|
+
/** @throws {KitError} `started`, `full`. */
|
|
42
|
+
join() {
|
|
43
|
+
return this.runtime.call(this.nodeType, this.key, 'join', null, { action: true });
|
|
44
|
+
}
|
|
45
|
+
/** @throws {KitError} `forbidden` (the host starts it), `started`. */
|
|
46
|
+
start() {
|
|
47
|
+
return this.runtime.call(this.nodeType, this.key, 'start', null, { action: true });
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Moves the player's unit to `(x, z)`.
|
|
51
|
+
* @throws {KitError} `not_your_turn`, `conflict` (it moved), `out_of_reach`, `not_member`.
|
|
52
|
+
*/
|
|
53
|
+
move(x, z) {
|
|
54
|
+
return this.runtime.call(this.nodeType, this.key, 'move', { x, z }, { action: true });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Attacks unit `target` with the player's unit.
|
|
58
|
+
* @throws {KitError} `not_your_turn`, `conflict` (it attacked), `out_of_reach`, `forbidden` (its own side), `not_found`.
|
|
59
|
+
*/
|
|
60
|
+
attack(target) {
|
|
61
|
+
return this.runtime.call(this.nodeType, this.key, 'attack', { target }, { action: true });
|
|
62
|
+
}
|
|
63
|
+
/** Ends the player's turn this phase. */
|
|
64
|
+
endTurn() {
|
|
65
|
+
return this.runtime.call(this.nodeType, this.key, 'end_turn', null, { action: true });
|
|
66
|
+
}
|
|
67
|
+
}
|