@adatechnology/game-player 0.3.0 → 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/README.md +4 -0
- package/dist/index.d.ts +68 -1
- package/dist/index.global.js +108 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +108 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -59,3 +59,7 @@ carries it too.
|
|
|
59
59
|
## Pushing furniture
|
|
60
60
|
|
|
61
61
|
`createFurniturePush(rules?)` is the logic of pushing a heavy thing on foot: `begin({ massKg, material })`, then `update(dt, { walk, isFree })` every frame answers the step and says the `phase` (`grip` while the hold timer fills, `push`, `strain`), the `progress` of the timer and the `block` (`too_heavy`, `obstacle`). The host answers `isFree` by sweeping the volume of the piece and draws the rest. `gripSecondsFor`, `pushSpeedFor` and `canPush` give the numbers; the force of the legs, the friction by material, the times and the speeds are all in `FurniturePushRules` (`FURNITURE_PUSH_DEFAULTS`).
|
|
62
|
+
|
|
63
|
+
## Opening doors by hand
|
|
64
|
+
|
|
65
|
+
`createDoorUse(rules?)` is the logic of working a door, lid or drawer by hand: `begin({ opening, massKg, locked })` answers where the leaf goes (1 open, 0 closed; a second `begin` in the middle of a swing turns it round) or null for a locked leaf, which only rattles. `step(dt, isFree)` then goes through `reach` (the hand to the handle), `swing` (the leaf follows the hand, a little at a time so it stops at the first thing in its way, `block: 'obstacle'`) and `release`. A heavier leaf swings slower (`swingSecondsFor`); every number is in `DoorUseRules` (`DOOR_USE_DEFAULTS`).
|
package/dist/index.d.ts
CHANGED
|
@@ -484,4 +484,71 @@ type PlayerTestModule = TestableModule<PlayerScenarioLike> & {
|
|
|
484
484
|
*/
|
|
485
485
|
declare function createPlayerTestModule(options?: PlayerTestModuleOptions): PlayerTestModule;
|
|
486
486
|
|
|
487
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Opening and closing a hinged or sliding leaf by hand (a house door, a wardrobe or fridge door, a drawer, a car door, a hood): the hand reaches the handle, the leaf
|
|
489
|
+
* follows the hand at a speed set by its mass, stops where something is in its way, and a locked leaf only rattles.
|
|
490
|
+
* Engine-agnostic: the host says how open the leaf is (0 closed, 1 open) and answers whether it can go to a new opening; this says how far it goes each step.
|
|
491
|
+
*/
|
|
492
|
+
type DoorUsePhase = 'idle' | 'reach' | 'swing' | 'release' | 'rattle';
|
|
493
|
+
type DoorUseBlock = 'locked' | 'obstacle' | null;
|
|
494
|
+
/** Every number of the use; a game overrides what it wants (`createDoorUse(rules)`). */
|
|
495
|
+
interface DoorUseRules {
|
|
496
|
+
/** The hand going to the handle before the leaf moves, seconds. */
|
|
497
|
+
readonly reachSeconds: number;
|
|
498
|
+
/** A full swing: `base + perKg * mass` of the leaf, between `min` and `max` seconds (a fridge door is quick, a car door slower, a steel door slower still). */
|
|
499
|
+
readonly swingSeconds: {
|
|
500
|
+
readonly base: number;
|
|
501
|
+
readonly perKg: number;
|
|
502
|
+
readonly min: number;
|
|
503
|
+
readonly max: number;
|
|
504
|
+
};
|
|
505
|
+
/** The hand letting go and coming back, seconds. */
|
|
506
|
+
readonly releaseSeconds: number;
|
|
507
|
+
/** How long a locked leaf is shaken before the hand gives up, seconds. */
|
|
508
|
+
readonly rattleSeconds: number;
|
|
509
|
+
/** The part of the swing tested at a time against what is around (smaller is finer and costs more). */
|
|
510
|
+
readonly stepFraction: number;
|
|
511
|
+
/** Within this of closed or open a leaf counts as there: the last use's way no longer turns it round. */
|
|
512
|
+
readonly endTolerance: number;
|
|
513
|
+
}
|
|
514
|
+
declare const DOOR_USE_DEFAULTS: DoorUseRules;
|
|
515
|
+
type DoorUseTarget = {
|
|
516
|
+
/** How open the leaf is now, 0 closed to 1 open. */
|
|
517
|
+
readonly opening: number;
|
|
518
|
+
readonly massKg: number;
|
|
519
|
+
readonly locked?: boolean;
|
|
520
|
+
/** Where the last use sent this leaf: a leaf stopped between closed and open goes the other way (a door blocked half open closes). */
|
|
521
|
+
readonly lastGoal?: 0 | 1;
|
|
522
|
+
};
|
|
523
|
+
type DoorUseStep = {
|
|
524
|
+
readonly phase: DoorUsePhase;
|
|
525
|
+
/** How open the leaf is after the step. */
|
|
526
|
+
readonly opening: number;
|
|
527
|
+
/** The phase's own progress, 0 to 1 (how far the hand is to the handle, how far the swing went, how much of the rattle is left). */
|
|
528
|
+
readonly progress: number;
|
|
529
|
+
/** Set on the step the leaf stopped short or would not move. */
|
|
530
|
+
readonly block: DoorUseBlock;
|
|
531
|
+
/** The leaf got where it was going on this step (fully open or fully closed). */
|
|
532
|
+
readonly arrived: boolean;
|
|
533
|
+
};
|
|
534
|
+
/** Seconds of a full swing for a leaf of this mass. */
|
|
535
|
+
declare const swingSecondsFor: (massKg: number, rules?: DoorUseRules) => number;
|
|
536
|
+
interface DoorUse {
|
|
537
|
+
readonly phase: DoorUsePhase;
|
|
538
|
+
/** Where the leaf is going: 1 opening, 0 closing. */
|
|
539
|
+
readonly goal: 0 | 1;
|
|
540
|
+
readonly opening: number;
|
|
541
|
+
/**
|
|
542
|
+
* Starts using a leaf: the other way from where the last use sent it (`lastGoal`), else a closed or half-open leaf opens and an open
|
|
543
|
+
* one closes. A use in the middle of a swing turns it round.
|
|
544
|
+
* Answers the goal (1 open, 0 closed), or null when the leaf is locked (it rattles instead).
|
|
545
|
+
*/
|
|
546
|
+
begin(target: DoorUseTarget): 0 | 1 | null;
|
|
547
|
+
/** Advances the use; `isFree(from, to)` answers whether the leaf can go from one opening to the other (nothing in the way of its sweep). */
|
|
548
|
+
step(dt: number, isFree: (from: number, to: number) => boolean): DoorUseStep;
|
|
549
|
+
/** Lets go at once. */
|
|
550
|
+
cancel(): void;
|
|
551
|
+
}
|
|
552
|
+
declare function createDoorUse(rules?: Partial<DoorUseRules>): DoorUse;
|
|
553
|
+
|
|
554
|
+
export { type AttributeId, type Attributes, DOOR_USE_DEFAULTS, type DeathCause, type Derived, type DerivedFormulas, type DerivedId, type DoorUse, type DoorUseBlock, type DoorUsePhase, type DoorUseRules, type DoorUseStep, type DoorUseTarget, FURNITURE_PUSH_DEFAULTS, type FurniturePush, type FurniturePushBlock, type FurniturePushInput, type FurniturePushPhase, type FurniturePushRules, type FurniturePushStep, type FurniturePushTarget, type InfectionRule, type KnownDeathCause, type LinearFormula, type MeleeItem, PLAYER_DEFAULTS, PLAYER_TEXT, type Player, type PlayerAction, type PlayerCrowd, type PlayerEvent, type PlayerEventMap, type PlayerEventName, type PlayerInput, type PlayerMessageId, type PlayerNumbers, type PlayerOptions, type PlayerSave, type PlayerSoundId, type PlayerState, type PlayerTestModule, type PlayerTestModuleOptions, type PlayerText, type PlayerWorld, type WoundRule, type WoundTable, canPush, createDoorUse, createFurniturePush, createPlayer, createPlayerTestModule, createRng, deriveVitals, drawWound, gripSecondsFor, playerTextOf, pushSpeedFor, swingSecondsFor };
|
package/dist/index.global.js
CHANGED
|
@@ -21,10 +21,12 @@ var GamePlayer = (() => {
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
DOOR_USE_DEFAULTS: () => DOOR_USE_DEFAULTS,
|
|
24
25
|
FURNITURE_PUSH_DEFAULTS: () => FURNITURE_PUSH_DEFAULTS,
|
|
25
26
|
PLAYER_DEFAULTS: () => PLAYER_DEFAULTS,
|
|
26
27
|
PLAYER_TEXT: () => PLAYER_TEXT,
|
|
27
28
|
canPush: () => canPush,
|
|
29
|
+
createDoorUse: () => createDoorUse,
|
|
28
30
|
createFurniturePush: () => createFurniturePush,
|
|
29
31
|
createPlayer: () => createPlayer,
|
|
30
32
|
createPlayerTestModule: () => createPlayerTestModule,
|
|
@@ -33,7 +35,8 @@ var GamePlayer = (() => {
|
|
|
33
35
|
drawWound: () => drawWound,
|
|
34
36
|
gripSecondsFor: () => gripSecondsFor,
|
|
35
37
|
playerTextOf: () => playerTextOf,
|
|
36
|
-
pushSpeedFor: () => pushSpeedFor
|
|
38
|
+
pushSpeedFor: () => pushSpeedFor,
|
|
39
|
+
swingSecondsFor: () => swingSecondsFor
|
|
37
40
|
});
|
|
38
41
|
|
|
39
42
|
// src/data/defaults.json
|
|
@@ -1156,6 +1159,110 @@ var GamePlayer = (() => {
|
|
|
1156
1159
|
};
|
|
1157
1160
|
return module;
|
|
1158
1161
|
}
|
|
1162
|
+
|
|
1163
|
+
// src/door-use.ts
|
|
1164
|
+
var DOOR_USE_DEFAULTS = {
|
|
1165
|
+
reachSeconds: 0.3,
|
|
1166
|
+
swingSeconds: { base: 0.45, perKg: 0.02, min: 0.45, max: 1.6 },
|
|
1167
|
+
releaseSeconds: 0.22,
|
|
1168
|
+
rattleSeconds: 0.6,
|
|
1169
|
+
stepFraction: 0.04,
|
|
1170
|
+
endTolerance: 0.02
|
|
1171
|
+
};
|
|
1172
|
+
var swingSecondsFor = (massKg, rules = DOOR_USE_DEFAULTS) => Math.min(rules.swingSeconds.max, Math.max(rules.swingSeconds.min, rules.swingSeconds.base + rules.swingSeconds.perKg * Math.max(0, massKg)));
|
|
1173
|
+
function createDoorUse(rules = {}) {
|
|
1174
|
+
const r = { ...DOOR_USE_DEFAULTS, ...rules, swingSeconds: { ...DOOR_USE_DEFAULTS.swingSeconds, ...rules.swingSeconds } };
|
|
1175
|
+
let phase = "idle";
|
|
1176
|
+
let goal = 1;
|
|
1177
|
+
let opening = 0;
|
|
1178
|
+
let time = 0;
|
|
1179
|
+
let swing = 1;
|
|
1180
|
+
let start = 0;
|
|
1181
|
+
const out = (progress, arrived = false, b = null) => ({ phase, opening, progress: Math.max(0, Math.min(1, progress)), block: b, arrived });
|
|
1182
|
+
return {
|
|
1183
|
+
get phase() {
|
|
1184
|
+
return phase;
|
|
1185
|
+
},
|
|
1186
|
+
get goal() {
|
|
1187
|
+
return goal;
|
|
1188
|
+
},
|
|
1189
|
+
get opening() {
|
|
1190
|
+
return opening;
|
|
1191
|
+
},
|
|
1192
|
+
begin(target) {
|
|
1193
|
+
opening = Math.max(0, Math.min(1, target.opening));
|
|
1194
|
+
time = 0;
|
|
1195
|
+
if (target.locked) {
|
|
1196
|
+
phase = "rattle";
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
if (phase === "swing" || phase === "reach") goal = goal === 1 ? 0 : 1;
|
|
1200
|
+
else if (target.lastGoal !== void 0 && opening > r.endTolerance && opening < 1 - r.endTolerance) goal = target.lastGoal === 1 ? 0 : 1;
|
|
1201
|
+
else goal = opening > 0.5 ? 0 : 1;
|
|
1202
|
+
swing = swingSecondsFor(target.massKg, r);
|
|
1203
|
+
start = opening;
|
|
1204
|
+
phase = phase === "swing" ? "swing" : "reach";
|
|
1205
|
+
return goal;
|
|
1206
|
+
},
|
|
1207
|
+
step(dt, isFree) {
|
|
1208
|
+
if (phase === "idle") return out(0);
|
|
1209
|
+
time += dt;
|
|
1210
|
+
if (phase === "rattle") {
|
|
1211
|
+
const p2 = time / r.rattleSeconds;
|
|
1212
|
+
if (p2 >= 1) {
|
|
1213
|
+
phase = "idle";
|
|
1214
|
+
return out(1, false, "locked");
|
|
1215
|
+
}
|
|
1216
|
+
return out(p2, false, time === dt ? "locked" : null);
|
|
1217
|
+
}
|
|
1218
|
+
if (phase === "reach") {
|
|
1219
|
+
const p2 = time / r.reachSeconds;
|
|
1220
|
+
if (p2 < 1) return out(p2);
|
|
1221
|
+
phase = "swing";
|
|
1222
|
+
time = 0;
|
|
1223
|
+
start = opening;
|
|
1224
|
+
}
|
|
1225
|
+
if (phase === "swing") {
|
|
1226
|
+
const want = goal - opening;
|
|
1227
|
+
const reach = Math.min(Math.abs(want), dt / swing);
|
|
1228
|
+
let moved = 0;
|
|
1229
|
+
let stopped = false;
|
|
1230
|
+
while (moved < reach - 1e-9) {
|
|
1231
|
+
const inc = Math.min(r.stepFraction, reach - moved);
|
|
1232
|
+
const next = opening + Math.sign(want) * inc;
|
|
1233
|
+
if (!isFree(opening, next)) {
|
|
1234
|
+
stopped = true;
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
opening = next;
|
|
1238
|
+
moved += inc;
|
|
1239
|
+
}
|
|
1240
|
+
const travel = Math.abs(goal - start) || 1;
|
|
1241
|
+
if (stopped) {
|
|
1242
|
+
phase = "release";
|
|
1243
|
+
time = 0;
|
|
1244
|
+
return out(1 - Math.abs(goal - opening) / travel, false, "obstacle");
|
|
1245
|
+
}
|
|
1246
|
+
if (Math.abs(goal - opening) < 1e-6) {
|
|
1247
|
+
opening = goal;
|
|
1248
|
+
phase = "release";
|
|
1249
|
+
time = 0;
|
|
1250
|
+
return out(1, true);
|
|
1251
|
+
}
|
|
1252
|
+
return out(1 - Math.abs(goal - opening) / travel);
|
|
1253
|
+
}
|
|
1254
|
+
const p = time / r.releaseSeconds;
|
|
1255
|
+
if (p >= 1) {
|
|
1256
|
+
phase = "idle";
|
|
1257
|
+
}
|
|
1258
|
+
return out(p, false, null);
|
|
1259
|
+
},
|
|
1260
|
+
cancel() {
|
|
1261
|
+
phase = "idle";
|
|
1262
|
+
time = 0;
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1159
1266
|
return __toCommonJS(index_exports);
|
|
1160
1267
|
})();
|
|
1161
1268
|
//# sourceMappingURL=index.global.js.map
|