@voltro/game-stack 0.0.0-stage → 0.87.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 +24329 -0
- package/LICENSE +61 -0
- package/README.md +224 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +16333 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +13 -0
- package/dist/fight.d.ts +239 -0
- package/dist/fight.js +5 -0
- package/dist/fightRoom.js +44 -0
- package/dist/fightRules.js +66 -0
- package/dist/fightSchema.js +81 -0
- package/dist/fightSimulation.js +220 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.js +92 -0
- package/dist/resourceBindings.js +75 -0
- package/dist/systems.d.ts +44 -0
- package/dist/systems.js +162 -0
- package/package.json +72 -4
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { YieldableError } from 'effect/Cause';
|
|
3
|
+
|
|
4
|
+
export declare class GameStackError extends GameStackError_base {
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare const GameStackError_base: Schema.Class<GameStackError, Schema.TaggedStruct<"GameStackError", {
|
|
8
|
+
readonly code: Schema.Literals<readonly ["invalid_configuration", "missing_configuration", "duplicate_plugin"]>;
|
|
9
|
+
readonly systems: Schema.$Array<Schema.Literals<readonly ["auth", "players", "sanctions", "social", "chat", "guilds", "inventory", "economy", "progression", "liveops", "saves", "rooms", "matchmaking", "leaderboards"]>>;
|
|
10
|
+
}>, YieldableError>;
|
|
11
|
+
|
|
12
|
+
export { }
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GameSystemId as e } from "./systems.js";
|
|
2
|
+
import { Schema as t } from "effect";
|
|
3
|
+
//#region src/errors.ts
|
|
4
|
+
var n = class extends t.TaggedError()("GameStackError", {
|
|
5
|
+
code: t.Literals([
|
|
6
|
+
"invalid_configuration",
|
|
7
|
+
"missing_configuration",
|
|
8
|
+
"duplicate_plugin"
|
|
9
|
+
]),
|
|
10
|
+
systems: t.Array(e)
|
|
11
|
+
}) {};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { n as GameStackError };
|
package/dist/fight.d.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { DeclaredAccess } from '@voltro/protocol';
|
|
2
|
+
import { RoomBudgets } from '@voltro/plugin-rooms/room';
|
|
3
|
+
import { RoomLockstepDescriptor } from '@voltro/plugin-rooms/room';
|
|
4
|
+
import { RoomLockstepOptions } from '@voltro/plugin-rooms/room';
|
|
5
|
+
import { Schema } from 'effect';
|
|
6
|
+
import { Simulation } from '@voltro/plugin-rooms/simulation';
|
|
7
|
+
|
|
8
|
+
/** Capture the shared explicit two-player init material. No Room/Seat allocation. */
|
|
9
|
+
export declare const createFightOptions: (playerIds: ReadonlyArray<number>, rules?: Partial<FightRules>) => FightOptions;
|
|
10
|
+
|
|
11
|
+
/** Pure two-player reference Room declaration, referee by default. Actual app
|
|
12
|
+
* catalogue, bindings, Subject/Seat admission and owner ports remain external. */
|
|
13
|
+
export declare const createFightRoom: (offered: FightRoomOptions) => RoomLockstepDescriptor<typeof FightStateSchema, typeof FightInputSchema, typeof FightOptionsSchema>;
|
|
14
|
+
|
|
15
|
+
/** Optional pure gameplay reference. Does not install a plugin, Room, client,
|
|
16
|
+
* host, preset, actor, event bus, port, store, clock or private RNG. */
|
|
17
|
+
export declare const createFightSimulation: (offered?: FightSimulationOptions) => FightSimulation;
|
|
18
|
+
|
|
19
|
+
/** Default exported pure slot; both attacks are decided before either is applied. */
|
|
20
|
+
export declare const defaultFightHit: (context: FightHitContext) => FightHitResult | null;
|
|
21
|
+
|
|
22
|
+
/** Default exported pure slot. Q16.16 positions/velocities; no render conversion. */
|
|
23
|
+
export declare const defaultFightMovement: (context: FightMovementContext) => FightMovementResult;
|
|
24
|
+
|
|
25
|
+
export declare const defaultFightRules: FightRules;
|
|
26
|
+
|
|
27
|
+
/** Actual descriptor identity only, without options/state/participant data. */
|
|
28
|
+
export declare const describeFightStrategies: (simulation: FightSimulation) => FightStrategySelection;
|
|
29
|
+
|
|
30
|
+
export declare type FightEvent = Schema.Schema.Type<typeof FightEventSchema>;
|
|
31
|
+
|
|
32
|
+
export declare const FightEventSchema: Schema.Struct<{
|
|
33
|
+
readonly kind: Schema.Literals<readonly ["jump", "strike", "hit", "block", "finish"]>;
|
|
34
|
+
readonly actorSlot: Schema.Int;
|
|
35
|
+
readonly targetSlot: Schema.Int;
|
|
36
|
+
readonly amount: Schema.Int;
|
|
37
|
+
}>;
|
|
38
|
+
|
|
39
|
+
export declare type FightFighter = Schema.Schema.Type<typeof FightFighterSchema>;
|
|
40
|
+
|
|
41
|
+
export declare const FightFighterSchema: Schema.Struct<{
|
|
42
|
+
readonly playerId: Schema.Struct<{
|
|
43
|
+
readonly hi: Schema.Int;
|
|
44
|
+
readonly lo: Schema.Int;
|
|
45
|
+
}>;
|
|
46
|
+
readonly xRaw: Schema.Int;
|
|
47
|
+
readonly yRaw: Schema.Int;
|
|
48
|
+
readonly verticalSpeedRaw: Schema.Int;
|
|
49
|
+
readonly facing: Schema.Literals<readonly [-1, 1]>;
|
|
50
|
+
readonly health: Schema.Int;
|
|
51
|
+
readonly stamina: Schema.Int;
|
|
52
|
+
readonly cooldownTicks: Schema.Int;
|
|
53
|
+
readonly stunTicks: Schema.Int;
|
|
54
|
+
readonly strikeHeld: Schema.Boolean;
|
|
55
|
+
readonly jumpHeld: Schema.Boolean;
|
|
56
|
+
readonly blocking: Schema.Boolean;
|
|
57
|
+
}>;
|
|
58
|
+
|
|
59
|
+
export declare interface FightHitContext {
|
|
60
|
+
readonly attacker: FightFighter;
|
|
61
|
+
readonly defender: FightFighter;
|
|
62
|
+
readonly rules: FightRules;
|
|
63
|
+
readonly tick: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export declare interface FightHitResult {
|
|
67
|
+
readonly damage: number;
|
|
68
|
+
readonly stunTicks: number;
|
|
69
|
+
readonly knockbackRaw: number;
|
|
70
|
+
readonly blocked: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export declare type FightInput = Schema.Schema.Type<typeof FightInputSchema>;
|
|
74
|
+
|
|
75
|
+
export declare const FightInputSchema: Schema.Struct<{
|
|
76
|
+
readonly move: Schema.Literals<readonly [-1, 0, 1]>;
|
|
77
|
+
readonly jump: Schema.Boolean;
|
|
78
|
+
readonly strike: Schema.Boolean;
|
|
79
|
+
readonly block: Schema.Boolean;
|
|
80
|
+
}>;
|
|
81
|
+
|
|
82
|
+
export declare interface FightMovementContext {
|
|
83
|
+
readonly fighter: FightFighter;
|
|
84
|
+
readonly intent: FightInput;
|
|
85
|
+
readonly rules: FightRules;
|
|
86
|
+
readonly canAct: boolean;
|
|
87
|
+
readonly jump: boolean;
|
|
88
|
+
readonly tick: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export declare interface FightMovementResult {
|
|
92
|
+
readonly xRaw: number;
|
|
93
|
+
readonly yRaw: number;
|
|
94
|
+
readonly verticalSpeedRaw: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export declare type FightOptions = Schema.Schema.Type<typeof FightOptionsSchema>;
|
|
98
|
+
|
|
99
|
+
export declare const FightOptionsSchema: Schema.Struct<{
|
|
100
|
+
readonly playerIds: Schema.$Array<Schema.Int>;
|
|
101
|
+
readonly rules: Schema.Struct<{
|
|
102
|
+
readonly stageHalfWidthRaw: Schema.Int;
|
|
103
|
+
readonly fighterRadiusRaw: Schema.Int;
|
|
104
|
+
readonly spawnDistanceRaw: Schema.Int;
|
|
105
|
+
readonly maxHeightRaw: Schema.Int;
|
|
106
|
+
readonly moveSpeedRaw: Schema.Int;
|
|
107
|
+
readonly jumpVelocityRaw: Schema.Int;
|
|
108
|
+
readonly gravityRaw: Schema.Int;
|
|
109
|
+
readonly strikeRangeRaw: Schema.Int;
|
|
110
|
+
readonly strikeHeightRaw: Schema.Int;
|
|
111
|
+
readonly knockbackRaw: Schema.Int;
|
|
112
|
+
readonly maxHealth: Schema.Int;
|
|
113
|
+
readonly damage: Schema.Int;
|
|
114
|
+
readonly maxStamina: Schema.Int;
|
|
115
|
+
readonly strikeCost: Schema.Int;
|
|
116
|
+
readonly blockCost: Schema.Int;
|
|
117
|
+
readonly staminaRegen: Schema.Int;
|
|
118
|
+
readonly cooldownTicks: Schema.Int;
|
|
119
|
+
readonly stunTicks: Schema.Int;
|
|
120
|
+
readonly roundTicks: Schema.Int;
|
|
121
|
+
readonly blockDamage: Schema.Int;
|
|
122
|
+
}>;
|
|
123
|
+
}>;
|
|
124
|
+
|
|
125
|
+
export declare const FightPlayerIdSchema: Schema.Struct<{
|
|
126
|
+
readonly hi: Schema.Int;
|
|
127
|
+
readonly lo: Schema.Int;
|
|
128
|
+
}>;
|
|
129
|
+
|
|
130
|
+
export declare interface FightRoomOptions {
|
|
131
|
+
readonly name?: string;
|
|
132
|
+
/** Actual application access declaration; no successful guard is fabricated. */
|
|
133
|
+
readonly guards: DeclaredAccess;
|
|
134
|
+
readonly simulation?: FightSimulation;
|
|
135
|
+
readonly strategies?: FightStrategies;
|
|
136
|
+
readonly lockstep?: RoomLockstepOptions<typeof FightInputSchema>;
|
|
137
|
+
readonly tickRate?: number;
|
|
138
|
+
readonly budgets?: Partial<RoomBudgets>;
|
|
139
|
+
readonly reconnect?: {
|
|
140
|
+
readonly graceMs: number;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export declare type FightRules = Schema.Schema.Type<typeof FightRulesSchema>;
|
|
145
|
+
|
|
146
|
+
/** Every authoritative value is an integer/raw fixed word or an explicit enum. */
|
|
147
|
+
export declare const FightRulesSchema: Schema.Struct<{
|
|
148
|
+
readonly stageHalfWidthRaw: Schema.Int;
|
|
149
|
+
readonly fighterRadiusRaw: Schema.Int;
|
|
150
|
+
readonly spawnDistanceRaw: Schema.Int;
|
|
151
|
+
readonly maxHeightRaw: Schema.Int;
|
|
152
|
+
readonly moveSpeedRaw: Schema.Int;
|
|
153
|
+
readonly jumpVelocityRaw: Schema.Int;
|
|
154
|
+
readonly gravityRaw: Schema.Int;
|
|
155
|
+
readonly strikeRangeRaw: Schema.Int;
|
|
156
|
+
readonly strikeHeightRaw: Schema.Int;
|
|
157
|
+
readonly knockbackRaw: Schema.Int;
|
|
158
|
+
readonly maxHealth: Schema.Int;
|
|
159
|
+
readonly damage: Schema.Int;
|
|
160
|
+
readonly maxStamina: Schema.Int;
|
|
161
|
+
readonly strikeCost: Schema.Int;
|
|
162
|
+
readonly blockCost: Schema.Int;
|
|
163
|
+
readonly staminaRegen: Schema.Int;
|
|
164
|
+
readonly cooldownTicks: Schema.Int;
|
|
165
|
+
readonly stunTicks: Schema.Int;
|
|
166
|
+
readonly roundTicks: Schema.Int;
|
|
167
|
+
readonly blockDamage: Schema.Int;
|
|
168
|
+
}>;
|
|
169
|
+
|
|
170
|
+
export declare type FightSimulation = Simulation<typeof FightStateSchema, typeof FightInputSchema, typeof FightOptionsSchema>;
|
|
171
|
+
|
|
172
|
+
export declare interface FightSimulationOptions {
|
|
173
|
+
readonly strategies?: FightStrategies;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export declare type FightState = Schema.Schema.Type<typeof FightStateSchema>;
|
|
177
|
+
|
|
178
|
+
export declare const FightStateSchema: Schema.Struct<{
|
|
179
|
+
readonly elapsedTicks: Schema.Int;
|
|
180
|
+
readonly status: Schema.Literals<readonly ["running", "win", "draw"]>;
|
|
181
|
+
readonly winnerSlot: Schema.Literals<readonly [-1, 0, 1]>;
|
|
182
|
+
readonly rules: Schema.Struct<{
|
|
183
|
+
readonly stageHalfWidthRaw: Schema.Int;
|
|
184
|
+
readonly fighterRadiusRaw: Schema.Int;
|
|
185
|
+
readonly spawnDistanceRaw: Schema.Int;
|
|
186
|
+
readonly maxHeightRaw: Schema.Int;
|
|
187
|
+
readonly moveSpeedRaw: Schema.Int;
|
|
188
|
+
readonly jumpVelocityRaw: Schema.Int;
|
|
189
|
+
readonly gravityRaw: Schema.Int;
|
|
190
|
+
readonly strikeRangeRaw: Schema.Int;
|
|
191
|
+
readonly strikeHeightRaw: Schema.Int;
|
|
192
|
+
readonly knockbackRaw: Schema.Int;
|
|
193
|
+
readonly maxHealth: Schema.Int;
|
|
194
|
+
readonly damage: Schema.Int;
|
|
195
|
+
readonly maxStamina: Schema.Int;
|
|
196
|
+
readonly strikeCost: Schema.Int;
|
|
197
|
+
readonly blockCost: Schema.Int;
|
|
198
|
+
readonly staminaRegen: Schema.Int;
|
|
199
|
+
readonly cooldownTicks: Schema.Int;
|
|
200
|
+
readonly stunTicks: Schema.Int;
|
|
201
|
+
readonly roundTicks: Schema.Int;
|
|
202
|
+
readonly blockDamage: Schema.Int;
|
|
203
|
+
}>;
|
|
204
|
+
readonly fighters: Schema.$Array<Schema.Struct<{
|
|
205
|
+
readonly playerId: Schema.Struct<{
|
|
206
|
+
readonly hi: Schema.Int;
|
|
207
|
+
readonly lo: Schema.Int;
|
|
208
|
+
}>;
|
|
209
|
+
readonly xRaw: Schema.Int;
|
|
210
|
+
readonly yRaw: Schema.Int;
|
|
211
|
+
readonly verticalSpeedRaw: Schema.Int;
|
|
212
|
+
readonly facing: Schema.Literals<readonly [-1, 1]>;
|
|
213
|
+
readonly health: Schema.Int;
|
|
214
|
+
readonly stamina: Schema.Int;
|
|
215
|
+
readonly cooldownTicks: Schema.Int;
|
|
216
|
+
readonly stunTicks: Schema.Int;
|
|
217
|
+
readonly strikeHeld: Schema.Boolean;
|
|
218
|
+
readonly jumpHeld: Schema.Boolean;
|
|
219
|
+
readonly blocking: Schema.Boolean;
|
|
220
|
+
}>>;
|
|
221
|
+
readonly events: Schema.$Array<Schema.Struct<{
|
|
222
|
+
readonly kind: Schema.Literals<readonly ["jump", "strike", "hit", "block", "finish"]>;
|
|
223
|
+
readonly actorSlot: Schema.Int;
|
|
224
|
+
readonly targetSlot: Schema.Int;
|
|
225
|
+
readonly amount: Schema.Int;
|
|
226
|
+
}>>;
|
|
227
|
+
}>;
|
|
228
|
+
|
|
229
|
+
export declare interface FightStrategies {
|
|
230
|
+
readonly movement?: (context: FightMovementContext) => FightMovementResult;
|
|
231
|
+
readonly hit?: (context: FightHitContext) => FightHitResult | null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export declare interface FightStrategySelection {
|
|
235
|
+
readonly movement: 'default' | 'custom';
|
|
236
|
+
readonly hit: 'default' | 'custom';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export { }
|
package/dist/fight.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FightEventSchema as e, FightFighterSchema as t, FightInputSchema as n, FightOptionsSchema as r, FightPlayerIdSchema as i, FightRulesSchema as a, FightStateSchema as o } from "./fightSchema.js";
|
|
2
|
+
import { createFightOptions as s, defaultFightRules as c } from "./fightRules.js";
|
|
3
|
+
import { createFightSimulation as l, defaultFightHit as u, defaultFightMovement as d, describeFightStrategies as f } from "./fightSimulation.js";
|
|
4
|
+
import { createFightRoom as p } from "./fightRoom.js";
|
|
5
|
+
export { e as FightEventSchema, t as FightFighterSchema, n as FightInputSchema, r as FightOptionsSchema, i as FightPlayerIdSchema, a as FightRulesSchema, o as FightStateSchema, s as createFightOptions, p as createFightRoom, l as createFightSimulation, u as defaultFightHit, d as defaultFightMovement, c as defaultFightRules, f as describeFightStrategies };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { fightOwnData as e, fightRefusal as t } from "./fightRules.js";
|
|
2
|
+
import { createFightSimulation as n } from "./fightSimulation.js";
|
|
3
|
+
import { defineRoom as r } from "@voltro/plugin-rooms/room";
|
|
4
|
+
//#region src/fightRoom.ts
|
|
5
|
+
var i = (i) => {
|
|
6
|
+
let a = e(i, [
|
|
7
|
+
"name",
|
|
8
|
+
"guards",
|
|
9
|
+
"simulation",
|
|
10
|
+
"strategies",
|
|
11
|
+
"lockstep",
|
|
12
|
+
"tickRate",
|
|
13
|
+
"budgets",
|
|
14
|
+
"reconnect"
|
|
15
|
+
], ["guards"]);
|
|
16
|
+
if (!Array.isArray(a.guards) || !a.guards.length || a.simulation !== void 0 && a.strategies !== void 0) return t();
|
|
17
|
+
let o = a.lockstep === void 0 ? {} : a.lockstep;
|
|
18
|
+
if (!o || typeof o != "object" || Object.getPrototypeOf(o) !== Object.prototype) return t();
|
|
19
|
+
let s = {};
|
|
20
|
+
for (let e of Reflect.ownKeys(o)) {
|
|
21
|
+
let n = Object.getOwnPropertyDescriptor(o, e);
|
|
22
|
+
if (typeof e != "string" || !n?.enumerable || !("value" in n)) return t();
|
|
23
|
+
Object.defineProperty(s, e, {
|
|
24
|
+
value: n.value,
|
|
25
|
+
enumerable: !0
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return Object.hasOwn(s, "maxPlayers") && s.maxPlayers !== 2 ? t() : r({
|
|
29
|
+
name: a.name === void 0 ? "fight" : a.name,
|
|
30
|
+
mode: "lockstep",
|
|
31
|
+
simulation: a.simulation === void 0 ? n(a.strategies === void 0 ? {} : { strategies: a.strategies }) : a.simulation,
|
|
32
|
+
guards: a.guards,
|
|
33
|
+
lockstep: {
|
|
34
|
+
serverRole: "referee",
|
|
35
|
+
maxPlayers: 2,
|
|
36
|
+
...s
|
|
37
|
+
},
|
|
38
|
+
...a.tickRate === void 0 ? {} : { tickRate: a.tickRate },
|
|
39
|
+
...a.budgets === void 0 ? {} : { budgets: a.budgets },
|
|
40
|
+
...a.reconnect === void 0 ? {} : { reconnect: a.reconnect }
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { i as createFightRoom };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createFixed as e } from "@voltro/plugin-rooms/fixed";
|
|
2
|
+
import { RoomAuthoringError as t } from "@voltro/plugin-rooms/errors";
|
|
3
|
+
//#region src/fightRules.ts
|
|
4
|
+
var n = e(), r = Object.freeze({
|
|
5
|
+
stageHalfWidthRaw: n.fromInt(8),
|
|
6
|
+
fighterRadiusRaw: n.fromRatio(1, 2),
|
|
7
|
+
spawnDistanceRaw: n.fromInt(3),
|
|
8
|
+
maxHeightRaw: n.fromInt(4),
|
|
9
|
+
moveSpeedRaw: n.fromRatio(1, 16),
|
|
10
|
+
jumpVelocityRaw: n.fromRatio(1, 4),
|
|
11
|
+
gravityRaw: n.fromRatio(1, 64),
|
|
12
|
+
strikeRangeRaw: n.fromRatio(3, 2),
|
|
13
|
+
strikeHeightRaw: n.fromRatio(3, 4),
|
|
14
|
+
knockbackRaw: n.fromRatio(1, 4),
|
|
15
|
+
maxHealth: 100,
|
|
16
|
+
damage: 10,
|
|
17
|
+
maxStamina: 100,
|
|
18
|
+
strikeCost: 10,
|
|
19
|
+
blockCost: 3,
|
|
20
|
+
staminaRegen: 1,
|
|
21
|
+
cooldownTicks: 18,
|
|
22
|
+
stunTicks: 6,
|
|
23
|
+
roundTicks: 3600,
|
|
24
|
+
blockDamage: 0
|
|
25
|
+
}), i = () => {
|
|
26
|
+
throw new t({
|
|
27
|
+
room: "fight",
|
|
28
|
+
detail: "Fight source configuration, callback output or intent roster could not be admitted"
|
|
29
|
+
});
|
|
30
|
+
}, a = (e, t, n) => {
|
|
31
|
+
if (!e || typeof e != "object" || Object.getPrototypeOf(e) !== Object.prototype) return i();
|
|
32
|
+
let r = {};
|
|
33
|
+
for (let n of Reflect.ownKeys(e)) {
|
|
34
|
+
let a = Object.getOwnPropertyDescriptor(e, n);
|
|
35
|
+
if (typeof n != "string" || !t.includes(n) || !a?.enumerable || !("value" in a)) return i();
|
|
36
|
+
Object.defineProperty(r, n, {
|
|
37
|
+
value: a.value,
|
|
38
|
+
enumerable: !0
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return n.some((e) => !Object.hasOwn(r, e)) ? i() : Object.freeze(r);
|
|
42
|
+
}, o = (e, t, n) => typeof e == "number" && Number.isSafeInteger(e) && e >= t && e <= n ? e === 0 ? 0 : e : i(), s = Object.freeze(Object.keys(r)), c = (e, t = !1) => {
|
|
43
|
+
let i = a(e, s, t ? [] : s), c = {
|
|
44
|
+
...r,
|
|
45
|
+
...i
|
|
46
|
+
}, l = o(c.stageHalfWidthRaw, n.fromInt(1), n.fromInt(128)), u = o(c.fighterRadiusRaw, 1, Math.floor(l / 4));
|
|
47
|
+
o(c.spawnDistanceRaw, u * 2, l - u);
|
|
48
|
+
let d = o(c.maxHeightRaw, 1, n.fromInt(32)), f = o(c.jumpVelocityRaw, 1, Math.min(n.scale, d));
|
|
49
|
+
o(c.moveSpeedRaw, 1, Math.min(n.scale, l - u)), o(c.gravityRaw, 1, f), o(c.strikeRangeRaw, u * 2, l * 2), o(c.strikeHeightRaw, 1, d), o(c.knockbackRaw, 0, l * 2);
|
|
50
|
+
let p = o(c.maxHealth, 1, 1e6), m = o(c.maxStamina, 1, 1e6), h = o(c.damage, 1, p);
|
|
51
|
+
return o(c.blockDamage, 0, h), o(c.strikeCost, 1, m), o(c.blockCost, 1, m), o(c.staminaRegen, 0, m), o(c.cooldownTicks, 1, 1024), o(c.stunTicks, 0, 1024), o(c.roundTicks, 1, 65536), Object.freeze(c);
|
|
52
|
+
}, l = (e) => {
|
|
53
|
+
if (!Array.isArray(e) || e.length !== 2 || Reflect.ownKeys(e).length !== 3) return i();
|
|
54
|
+
let t = [];
|
|
55
|
+
for (let n = 0; n < 2; n++) {
|
|
56
|
+
let r = Object.getOwnPropertyDescriptor(e, String(n));
|
|
57
|
+
if (!r?.enumerable || !("value" in r)) return i();
|
|
58
|
+
t.push(o(r.value, 0, 4294967295));
|
|
59
|
+
}
|
|
60
|
+
return t[0] === t[1] ? i() : (t.sort((e, t) => e - t), Object.freeze([t[0], t[1]]));
|
|
61
|
+
}, u = (e, t = {}) => Object.freeze({
|
|
62
|
+
playerIds: l(e),
|
|
63
|
+
rules: c(t, !0)
|
|
64
|
+
});
|
|
65
|
+
//#endregion
|
|
66
|
+
export { l as captureFightPlayerIds, c as captureFightRules, u as createFightOptions, r as defaultFightRules, a as fightOwnData, i as fightRefusal };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
import { fieldId as t } from "@voltro/plugin-rooms/room";
|
|
3
|
+
//#region src/fightSchema.ts
|
|
4
|
+
var n = e.Struct({
|
|
5
|
+
stageHalfWidthRaw: e.Int.pipe(t(1)),
|
|
6
|
+
fighterRadiusRaw: e.Int.pipe(t(2)),
|
|
7
|
+
spawnDistanceRaw: e.Int.pipe(t(3)),
|
|
8
|
+
maxHeightRaw: e.Int.pipe(t(4)),
|
|
9
|
+
moveSpeedRaw: e.Int.pipe(t(5)),
|
|
10
|
+
jumpVelocityRaw: e.Int.pipe(t(6)),
|
|
11
|
+
gravityRaw: e.Int.pipe(t(7)),
|
|
12
|
+
strikeRangeRaw: e.Int.pipe(t(8)),
|
|
13
|
+
strikeHeightRaw: e.Int.pipe(t(9)),
|
|
14
|
+
knockbackRaw: e.Int.pipe(t(10)),
|
|
15
|
+
maxHealth: e.Int.pipe(t(11)),
|
|
16
|
+
damage: e.Int.pipe(t(12)),
|
|
17
|
+
maxStamina: e.Int.pipe(t(13)),
|
|
18
|
+
strikeCost: e.Int.pipe(t(14)),
|
|
19
|
+
blockCost: e.Int.pipe(t(15)),
|
|
20
|
+
staminaRegen: e.Int.pipe(t(16)),
|
|
21
|
+
cooldownTicks: e.Int.pipe(t(17)),
|
|
22
|
+
stunTicks: e.Int.pipe(t(18)),
|
|
23
|
+
roundTicks: e.Int.pipe(t(19)),
|
|
24
|
+
blockDamage: e.Int.pipe(t(20))
|
|
25
|
+
}), r = e.Struct({
|
|
26
|
+
hi: e.Int.pipe(t(1)),
|
|
27
|
+
lo: e.Int.pipe(t(2))
|
|
28
|
+
}), i = e.Struct({
|
|
29
|
+
playerId: r.pipe(t(1)),
|
|
30
|
+
xRaw: e.Int.pipe(t(2)),
|
|
31
|
+
yRaw: e.Int.pipe(t(3)),
|
|
32
|
+
verticalSpeedRaw: e.Int.pipe(t(4)),
|
|
33
|
+
facing: e.Literals([-1, 1]).pipe(t(5)),
|
|
34
|
+
health: e.Int.pipe(t(6)),
|
|
35
|
+
stamina: e.Int.pipe(t(7)),
|
|
36
|
+
cooldownTicks: e.Int.pipe(t(8)),
|
|
37
|
+
stunTicks: e.Int.pipe(t(9)),
|
|
38
|
+
strikeHeld: e.Boolean.pipe(t(10)),
|
|
39
|
+
jumpHeld: e.Boolean.pipe(t(11)),
|
|
40
|
+
blocking: e.Boolean.pipe(t(12))
|
|
41
|
+
}), a = e.Struct({
|
|
42
|
+
move: e.Literals([
|
|
43
|
+
-1,
|
|
44
|
+
0,
|
|
45
|
+
1
|
|
46
|
+
]),
|
|
47
|
+
jump: e.Boolean,
|
|
48
|
+
strike: e.Boolean,
|
|
49
|
+
block: e.Boolean
|
|
50
|
+
}), o = e.Struct({
|
|
51
|
+
kind: e.Literals([
|
|
52
|
+
"jump",
|
|
53
|
+
"strike",
|
|
54
|
+
"hit",
|
|
55
|
+
"block",
|
|
56
|
+
"finish"
|
|
57
|
+
]).pipe(t(1)),
|
|
58
|
+
actorSlot: e.Int.pipe(t(2)),
|
|
59
|
+
targetSlot: e.Int.pipe(t(3)),
|
|
60
|
+
amount: e.Int.pipe(t(4))
|
|
61
|
+
}), s = e.Struct({
|
|
62
|
+
elapsedTicks: e.Int.pipe(t(1)),
|
|
63
|
+
status: e.Literals([
|
|
64
|
+
"running",
|
|
65
|
+
"win",
|
|
66
|
+
"draw"
|
|
67
|
+
]).pipe(t(2)),
|
|
68
|
+
winnerSlot: e.Literals([
|
|
69
|
+
-1,
|
|
70
|
+
0,
|
|
71
|
+
1
|
|
72
|
+
]).pipe(t(3)),
|
|
73
|
+
rules: n.pipe(t(4)),
|
|
74
|
+
fighters: e.Array(i).pipe(t(5)),
|
|
75
|
+
events: e.Array(o).pipe(t(6))
|
|
76
|
+
}), c = e.Struct({
|
|
77
|
+
playerIds: e.Array(e.Int),
|
|
78
|
+
rules: n
|
|
79
|
+
});
|
|
80
|
+
//#endregion
|
|
81
|
+
export { o as FightEventSchema, i as FightFighterSchema, a as FightInputSchema, c as FightOptionsSchema, r as FightPlayerIdSchema, n as FightRulesSchema, s as FightStateSchema };
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { FightInputSchema as e, FightOptionsSchema as t, FightStateSchema as n } from "./fightSchema.js";
|
|
2
|
+
import { captureFightPlayerIds as r, captureFightRules as i, fightOwnData as a, fightRefusal as o } from "./fightRules.js";
|
|
3
|
+
import { createFixed as s } from "@voltro/plugin-rooms/fixed";
|
|
4
|
+
import { defineSimulation as c } from "@voltro/plugin-rooms/simulation";
|
|
5
|
+
//#region src/fightSimulation.ts
|
|
6
|
+
var l = s(), u = /* @__PURE__ */ new WeakMap(), d = (e) => {
|
|
7
|
+
let { fighter: t, intent: n, rules: r } = e, i = r.stageHalfWidthRaw - r.fighterRadiusRaw, a = e.canAct && !t.blocking ? n.move * r.moveSpeedRaw : 0, o = l.clamp(l.add(l.raw(t.xRaw), l.raw(a)), l.raw(-i), l.raw(i)), s = e.jump ? r.jumpVelocityRaw : t.verticalSpeedRaw;
|
|
8
|
+
s = !e.jump && t.yRaw === 0 ? 0 : Math.max(-r.maxHeightRaw, l.sub(l.raw(s), l.raw(r.gravityRaw)));
|
|
9
|
+
let c = l.add(l.raw(t.yRaw), l.raw(s)), u = l.clamp(c, l.raw(0), l.raw(r.maxHeightRaw));
|
|
10
|
+
return (c <= 0 || c >= r.maxHeightRaw) && (s = 0), Object.freeze({
|
|
11
|
+
xRaw: o,
|
|
12
|
+
yRaw: u,
|
|
13
|
+
verticalSpeedRaw: s
|
|
14
|
+
});
|
|
15
|
+
}, f = (e) => {
|
|
16
|
+
let { attacker: t, defender: n, rules: r } = e, i = l.sub(l.raw(n.xRaw), l.raw(t.xRaw)), a = l.abs(l.sub(l.raw(n.yRaw), l.raw(t.yRaw)));
|
|
17
|
+
if (l.abs(i) > r.strikeRangeRaw || a > r.strikeHeightRaw || i !== 0 && Math.sign(i) !== t.facing) return null;
|
|
18
|
+
let o = n.blocking && n.stamina >= r.blockCost;
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
damage: o ? r.blockDamage : r.damage,
|
|
21
|
+
stunTicks: o ? 0 : r.stunTicks,
|
|
22
|
+
knockbackRaw: o ? 0 : r.knockbackRaw,
|
|
23
|
+
blocked: o
|
|
24
|
+
});
|
|
25
|
+
}, p = (e) => Object.freeze({
|
|
26
|
+
...e,
|
|
27
|
+
playerId: Object.freeze({ ...e.playerId })
|
|
28
|
+
}), m = (e) => e.playerId.hi * 65536 + e.playerId.lo, h = (e, t, n) => typeof e == "number" && Number.isSafeInteger(e) && e >= t && e <= n ? e === 0 ? 0 : e : o(), g = (e) => e instanceof Promise ? (Promise.prototype.then.call(e, void 0, () => void 0), o()) : e, _ = (e, t) => {
|
|
29
|
+
let n = a(e, [
|
|
30
|
+
"xRaw",
|
|
31
|
+
"yRaw",
|
|
32
|
+
"verticalSpeedRaw"
|
|
33
|
+
], [
|
|
34
|
+
"xRaw",
|
|
35
|
+
"yRaw",
|
|
36
|
+
"verticalSpeedRaw"
|
|
37
|
+
]), r = t.stageHalfWidthRaw - t.fighterRadiusRaw;
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
xRaw: h(n.xRaw, -r, r),
|
|
40
|
+
yRaw: h(n.yRaw, 0, t.maxHeightRaw),
|
|
41
|
+
verticalSpeedRaw: h(n.verticalSpeedRaw, -t.maxHeightRaw, t.maxHeightRaw)
|
|
42
|
+
});
|
|
43
|
+
}, v = (e, t) => {
|
|
44
|
+
if (e === null) return null;
|
|
45
|
+
let n = a(e, [
|
|
46
|
+
"damage",
|
|
47
|
+
"stunTicks",
|
|
48
|
+
"knockbackRaw",
|
|
49
|
+
"blocked"
|
|
50
|
+
], [
|
|
51
|
+
"damage",
|
|
52
|
+
"stunTicks",
|
|
53
|
+
"knockbackRaw",
|
|
54
|
+
"blocked"
|
|
55
|
+
]);
|
|
56
|
+
return typeof n.blocked == "boolean" ? Object.freeze({
|
|
57
|
+
damage: h(n.damage, 0, t.maxHealth),
|
|
58
|
+
stunTicks: h(n.stunTicks, 0, 1024),
|
|
59
|
+
knockbackRaw: h(n.knockbackRaw, 0, t.stageHalfWidthRaw * 2),
|
|
60
|
+
blocked: n.blocked
|
|
61
|
+
}) : o();
|
|
62
|
+
}, y = (e, t) => {
|
|
63
|
+
if (e.length !== 2 || t.length !== 2 || m(e[0]) === m(e[1])) return o();
|
|
64
|
+
let n = [];
|
|
65
|
+
for (let r of e) {
|
|
66
|
+
let e = m(r), i = t.filter((t) => t.playerId === e);
|
|
67
|
+
if (i.length !== 1) return o();
|
|
68
|
+
let s = a(i[0].input, [
|
|
69
|
+
"move",
|
|
70
|
+
"jump",
|
|
71
|
+
"strike",
|
|
72
|
+
"block"
|
|
73
|
+
], [
|
|
74
|
+
"move",
|
|
75
|
+
"jump",
|
|
76
|
+
"strike",
|
|
77
|
+
"block"
|
|
78
|
+
]);
|
|
79
|
+
if (![
|
|
80
|
+
-1,
|
|
81
|
+
0,
|
|
82
|
+
1
|
|
83
|
+
].includes(s.move) || typeof s.jump != "boolean" || typeof s.strike != "boolean" || typeof s.block != "boolean") return o();
|
|
84
|
+
n.push(Object.freeze({
|
|
85
|
+
move: s.move,
|
|
86
|
+
jump: s.jump,
|
|
87
|
+
strike: s.strike,
|
|
88
|
+
block: s.block
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
return Object.freeze([n[0], n[1]]);
|
|
92
|
+
}, b = (e, t, n) => {
|
|
93
|
+
let r = n.fighterRadiusRaw * 2;
|
|
94
|
+
if (Math.abs(e[0].yRaw - e[1].yRaw) >= r) return;
|
|
95
|
+
let i = t.fighters[0].xRaw <= t.fighters[1].xRaw ? 0 : 1, a = +(i === 0), o = e[i], s = e[a];
|
|
96
|
+
if (s.xRaw - o.xRaw >= r) return;
|
|
97
|
+
let c = n.stageHalfWidthRaw - n.fighterRadiusRaw, u = l.div(l.add(l.raw(o.xRaw), l.raw(s.xRaw)), l.fromInt(2)), d = l.clamp(u, l.raw(-c + n.fighterRadiusRaw), l.raw(c - n.fighterRadiusRaw));
|
|
98
|
+
o.xRaw = l.sub(d, l.raw(n.fighterRadiusRaw)), s.xRaw = l.add(d, l.raw(n.fighterRadiusRaw));
|
|
99
|
+
}, x = (e, t, n, r) => {
|
|
100
|
+
if (t[0].health > 0 && t[1].health > 0 && n < e.rules.roundTicks) return {
|
|
101
|
+
...e,
|
|
102
|
+
elapsedTicks: n,
|
|
103
|
+
fighters: t,
|
|
104
|
+
events: r
|
|
105
|
+
};
|
|
106
|
+
let i = t[0].health === t[1].health ? -1 : t[0].health > t[1].health ? 0 : 1;
|
|
107
|
+
return r.push({
|
|
108
|
+
kind: "finish",
|
|
109
|
+
actorSlot: i,
|
|
110
|
+
targetSlot: -1,
|
|
111
|
+
amount: n
|
|
112
|
+
}), {
|
|
113
|
+
...e,
|
|
114
|
+
elapsedTicks: n,
|
|
115
|
+
status: i === -1 ? "draw" : "win",
|
|
116
|
+
winnerSlot: i,
|
|
117
|
+
fighters: t,
|
|
118
|
+
events: r
|
|
119
|
+
};
|
|
120
|
+
}, S = (s = {}) => {
|
|
121
|
+
let m = a(s, ["strategies"], []), h = m.strategies === void 0 ? {} : a(m.strategies, ["movement", "hit"], []), S = Object.hasOwn(h, "movement") ? h.movement : d, C = Object.hasOwn(h, "hit") ? h.hit : f;
|
|
122
|
+
if (typeof S != "function" || typeof C != "function") return o();
|
|
123
|
+
let w = S, T = C, E = c({
|
|
124
|
+
state: n,
|
|
125
|
+
input: e,
|
|
126
|
+
options: t,
|
|
127
|
+
limits: {
|
|
128
|
+
maxCollectionLength: 16,
|
|
129
|
+
maxNodes: 4096,
|
|
130
|
+
maxBytes: 65536,
|
|
131
|
+
maxFieldId: 64
|
|
132
|
+
},
|
|
133
|
+
init: (e, t) => {
|
|
134
|
+
let n = i(t.rules);
|
|
135
|
+
return {
|
|
136
|
+
elapsedTicks: 0,
|
|
137
|
+
status: "running",
|
|
138
|
+
winnerSlot: -1,
|
|
139
|
+
rules: n,
|
|
140
|
+
fighters: r(t.playerIds).map((e, t) => ({
|
|
141
|
+
playerId: {
|
|
142
|
+
hi: e >>> 16,
|
|
143
|
+
lo: e & 65535
|
|
144
|
+
},
|
|
145
|
+
xRaw: t === 0 ? -n.spawnDistanceRaw : n.spawnDistanceRaw,
|
|
146
|
+
yRaw: 0,
|
|
147
|
+
verticalSpeedRaw: 0,
|
|
148
|
+
facing: t === 0 ? 1 : -1,
|
|
149
|
+
health: n.maxHealth,
|
|
150
|
+
stamina: n.maxStamina,
|
|
151
|
+
cooldownTicks: 0,
|
|
152
|
+
stunTicks: 0,
|
|
153
|
+
strikeHeld: !1,
|
|
154
|
+
jumpHeld: !1,
|
|
155
|
+
blocking: !1
|
|
156
|
+
})),
|
|
157
|
+
events: []
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
step: (e, t, n) => {
|
|
161
|
+
if (e.status !== "running") return e;
|
|
162
|
+
let r = e.rules, i = y(e.fighters, t), a = [], s = [!1, !1], c = [{ ...e.fighters[0] }, { ...e.fighters[1] }];
|
|
163
|
+
for (let t of [0, 1]) {
|
|
164
|
+
let o = c[t], l = e.fighters[t], u = i[t], d = l.stunTicks === 0;
|
|
165
|
+
o.stamina = Math.min(r.maxStamina, l.stamina + r.staminaRegen), o.cooldownTicks = Math.max(0, l.cooldownTicks - 1), o.stunTicks = Math.max(0, l.stunTicks - 1), o.blocking = d && u.block && l.yRaw === 0 && o.stamina >= r.blockCost;
|
|
166
|
+
let f = d && !o.blocking && u.jump && !l.jumpHeld && l.yRaw === 0, m = _(g(w(Object.freeze({
|
|
167
|
+
fighter: p(o),
|
|
168
|
+
intent: u,
|
|
169
|
+
rules: Object.freeze({ ...r }),
|
|
170
|
+
canAct: d,
|
|
171
|
+
jump: f,
|
|
172
|
+
tick: n
|
|
173
|
+
}))), r);
|
|
174
|
+
Object.assign(o, m), f && m.yRaw > 0 && a.push({
|
|
175
|
+
kind: "jump",
|
|
176
|
+
actorSlot: t,
|
|
177
|
+
targetSlot: -1,
|
|
178
|
+
amount: m.yRaw
|
|
179
|
+
}), o.strikeHeld = u.strike, o.jumpHeld = u.jump, s[t] = d && !o.blocking && l.cooldownTicks === 0 && u.strike && !l.strikeHeld && o.stamina >= r.strikeCost, s[t] && (o.stamina -= r.strikeCost, o.cooldownTicks = r.cooldownTicks, a.push({
|
|
180
|
+
kind: "strike",
|
|
181
|
+
actorSlot: t,
|
|
182
|
+
targetSlot: 1 - t,
|
|
183
|
+
amount: r.strikeCost
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
b(c, e, r), c[0].xRaw !== c[1].xRaw && (c[0].facing = c[0].xRaw < c[1].xRaw ? 1 : -1, c[1].facing = c[0].facing === 1 ? -1 : 1);
|
|
187
|
+
let u = [null, null];
|
|
188
|
+
for (let e of [0, 1]) s[e] && (u[e] = v(g(T(Object.freeze({
|
|
189
|
+
attacker: p(c[e]),
|
|
190
|
+
defender: p(c[+(e === 0)]),
|
|
191
|
+
rules: Object.freeze({ ...r }),
|
|
192
|
+
tick: n
|
|
193
|
+
}))), r));
|
|
194
|
+
for (let e of [0, 1]) {
|
|
195
|
+
let t = u[e];
|
|
196
|
+
if (!t) continue;
|
|
197
|
+
let n = c[+(e === 0)];
|
|
198
|
+
if (t.blocked) {
|
|
199
|
+
if (!n.blocking || n.stamina < r.blockCost) return o();
|
|
200
|
+
n.stamina -= r.blockCost;
|
|
201
|
+
}
|
|
202
|
+
n.health = Math.max(0, n.health - t.damage), n.stunTicks = Math.max(n.stunTicks, t.stunTicks);
|
|
203
|
+
let i = r.stageHalfWidthRaw - r.fighterRadiusRaw, s = c[e].facing;
|
|
204
|
+
n.xRaw = l.clamp(l.add(l.raw(n.xRaw), l.raw(s * t.knockbackRaw)), l.raw(-i), l.raw(i)), a.push({
|
|
205
|
+
kind: t.blocked ? "block" : "hit",
|
|
206
|
+
actorSlot: e,
|
|
207
|
+
targetSlot: +(e === 0),
|
|
208
|
+
amount: t.damage
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return b(c, e, r), x(e, c, e.elapsedTicks + 1, a);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return u.set(E, Object.freeze({
|
|
215
|
+
movement: w === d ? "default" : "custom",
|
|
216
|
+
hit: T === f ? "default" : "custom"
|
|
217
|
+
})), E;
|
|
218
|
+
}, C = (e) => u.get(e) ?? o();
|
|
219
|
+
//#endregion
|
|
220
|
+
export { S as createFightSimulation, f as defaultFightHit, d as defaultFightMovement, C as describeFightStrategies };
|