gamesteel 0.6.0 → 0.6.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/README.md +1 -1
- package/dist/THIRD-PARTY-NOTICES.txt +0 -24
- package/dist/gamesteel.js +9787 -9574
- package/dist/pkgs/audio/dist/worker.js +299 -137
- package/dist/pkgs/model/dist/worker.js +299 -137
- package/dist/pkgs/paint/dist/worker.js +299 -137
- package/dist/pkgs/studio/dist/exported-player-25d.js +14738 -14572
- package/dist/pkgs/studio/dist/exported-player-3d.js +18160 -17957
- package/dist/pkgs/studio/dist/exported-player.js +11501 -11335
- package/dist/pkgs/studio/dist/program-look-page.js +1 -1
- package/dist/pkgs/studio/dist/server/playtest-worker.js +10491 -10306
- package/package.json +9 -9
|
@@ -24846,7 +24846,7 @@ function shouldInterruptAfterDeadline(deadline) {
|
|
|
24846
24846
|
var import_sucrase3 = __toESM(require_dist2(), 1);
|
|
24847
24847
|
|
|
24848
24848
|
// ../core/dist/version.js
|
|
24849
|
-
var ENGINE_VERSION = "0.10.
|
|
24849
|
+
var ENGINE_VERSION = "0.10.1";
|
|
24850
24850
|
|
|
24851
24851
|
// ../core/dist/sim/trig-table.js
|
|
24852
24852
|
var QUARTER_WAVE = [
|
|
@@ -26111,6 +26111,10 @@ function ticksOf(d, line = 1, column = 1) {
|
|
|
26111
26111
|
return ticks;
|
|
26112
26112
|
}
|
|
26113
26113
|
|
|
26114
|
+
// ../core/dist/script/scan.js
|
|
26115
|
+
var import_parser = __toESM(require_parser(), 1);
|
|
26116
|
+
var import_types = __toESM(require_types(), 1);
|
|
26117
|
+
|
|
26114
26118
|
// ../core/dist/home.js
|
|
26115
26119
|
import { existsSync, renameSync, symlinkSync } from "node:fs";
|
|
26116
26120
|
import { homedir } from "node:os";
|
|
@@ -27968,7 +27972,7 @@ var Parser = class {
|
|
|
27968
27972
|
return clauses;
|
|
27969
27973
|
}
|
|
27970
27974
|
};
|
|
27971
|
-
function
|
|
27975
|
+
function parse2(source) {
|
|
27972
27976
|
return new Parser(tokenize(source)).parse();
|
|
27973
27977
|
}
|
|
27974
27978
|
|
|
@@ -29274,7 +29278,7 @@ function checkStrings(ast, out) {
|
|
|
29274
29278
|
function checkControls(ast, out) {
|
|
29275
29279
|
const lines = ast.controls?.bindings ?? [];
|
|
29276
29280
|
const players = playerButtonsOf(ast);
|
|
29277
|
-
const mouse = ["click", "right_click", "middle_click"];
|
|
29281
|
+
const mouse = ["click", "right_click", "middle_click", "menu"];
|
|
29278
29282
|
const builtIn = /* @__PURE__ */ new Set([...BUTTONS, ...mouse, ...players]);
|
|
29279
29283
|
const buttons = [...BUTTONS, ...mouse, ...players, ...lines.map((l) => l.button).filter((b) => !builtIn.has(b))];
|
|
29280
29284
|
const own2 = lines.filter((l) => !builtIn.has(l.button));
|
|
@@ -29535,6 +29539,27 @@ function numericProps(props) {
|
|
|
29535
29539
|
return out;
|
|
29536
29540
|
}
|
|
29537
29541
|
|
|
29542
|
+
// ../core/dist/script/prefs.js
|
|
29543
|
+
var NO_PREFS_YET = { sound: true, music: true, fullscreen: false, canFullscreen: false, touch: false };
|
|
29544
|
+
var usesPrefs = (sources) => sources.some((source) => /\.\s*prefs\b/.test(source));
|
|
29545
|
+
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29546
|
+
function isPrefs(value) {
|
|
29547
|
+
return isObject(value) && Object.keys(value).length === 5 && typeof value["sound"] === "boolean" && typeof value["music"] === "boolean" && typeof value["fullscreen"] === "boolean" && typeof value["canFullscreen"] === "boolean" && typeof value["touch"] === "boolean";
|
|
29548
|
+
}
|
|
29549
|
+
var CHANGEABLE = ["sound", "music", "fullscreen"];
|
|
29550
|
+
function prefsChangeOf(raw) {
|
|
29551
|
+
const said2 = "prefs.set takes sound, music and fullscreen, each true or false, like prefs.set({ music: false })";
|
|
29552
|
+
if (!isObject(raw))
|
|
29553
|
+
throw new Error(said2);
|
|
29554
|
+
const out = {};
|
|
29555
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
29556
|
+
if (!CHANGEABLE.includes(key) || typeof value !== "boolean")
|
|
29557
|
+
throw new Error(`${said2}, not ${JSON.stringify({ [key]: value })}`);
|
|
29558
|
+
out[key] = value;
|
|
29559
|
+
}
|
|
29560
|
+
return out;
|
|
29561
|
+
}
|
|
29562
|
+
|
|
29538
29563
|
// ../core/dist/script/ads.js
|
|
29539
29564
|
var AD_KINDS = ["break", "reward"];
|
|
29540
29565
|
var NO_ADS_YET = { pending: null, lastBreak: null, lastPress: null, lastPlay: null, played: false, scene: null, ready: AD_KINDS, banner: null };
|
|
@@ -29591,7 +29616,7 @@ var ONLINE_LIMITS = {
|
|
|
29591
29616
|
downloadBytes: 1e6
|
|
29592
29617
|
};
|
|
29593
29618
|
var TOO_MANY_ASKS = `a tick asks the game's server at most ${String(ONLINE_LIMITS.asksPerTick)} times: spread the calls over ticks`;
|
|
29594
|
-
var
|
|
29619
|
+
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29595
29620
|
var jsonBytes = (data) => {
|
|
29596
29621
|
try {
|
|
29597
29622
|
const text2 = JSON.stringify(data);
|
|
@@ -29620,7 +29645,7 @@ var uploadProblem = (ask) => {
|
|
|
29620
29645
|
};
|
|
29621
29646
|
function askProblem(ask) {
|
|
29622
29647
|
const kinds = "an online ask is a call, join, leave, send, upload, download or askPush";
|
|
29623
|
-
if (!
|
|
29648
|
+
if (!isObject2(ask))
|
|
29624
29649
|
return kinds;
|
|
29625
29650
|
switch (ask["kind"]) {
|
|
29626
29651
|
case "call":
|
|
@@ -29644,7 +29669,7 @@ function askProblem(ask) {
|
|
|
29644
29669
|
}
|
|
29645
29670
|
var usesOnline = (sources) => sources.some((source) => /\bonline\s*\.\s*(call|join|leave|send|state|upload|uploadScreen|download|askPush)\b/.test(source));
|
|
29646
29671
|
function isOnlineHeard(value) {
|
|
29647
|
-
if (!
|
|
29672
|
+
if (!isObject2(value))
|
|
29648
29673
|
return false;
|
|
29649
29674
|
switch (value["kind"]) {
|
|
29650
29675
|
case "answer":
|
|
@@ -29671,9 +29696,9 @@ function isOnlineHeard(value) {
|
|
|
29671
29696
|
// ../core/dist/script/purchases.js
|
|
29672
29697
|
var PURCHASE_RESULTS = ["granted", "pending", "cancelled", "unavailable", "refused"];
|
|
29673
29698
|
var PRODUCT_ID = /^[a-z0-9][a-z0-9_.]{0,39}$/;
|
|
29674
|
-
var
|
|
29699
|
+
var isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29675
29700
|
function purchaseAskProblem(ask) {
|
|
29676
|
-
if (!
|
|
29701
|
+
if (!isObject3(ask))
|
|
29677
29702
|
return "a purchase ask is a buy of a product or a restore";
|
|
29678
29703
|
if (ask["kind"] === "restore")
|
|
29679
29704
|
return null;
|
|
@@ -29682,9 +29707,9 @@ function purchaseAskProblem(ask) {
|
|
|
29682
29707
|
const id = ask["id"];
|
|
29683
29708
|
return typeof id === "string" && PRODUCT_ID.test(id) ? null : `a product id is 1 to 40 lowercase letters, digits, _ and ., starting with a letter or digit, like coins_100, not ${JSON.stringify(id) ?? String(id)}`;
|
|
29684
29709
|
}
|
|
29685
|
-
var isProduct = (value) =>
|
|
29710
|
+
var isProduct = (value) => isObject3(value) && typeof value["id"] === "string" && typeof value["price"] === "string" && typeof value["name"] === "string";
|
|
29686
29711
|
function isPurchaseHeard(value) {
|
|
29687
|
-
if (!
|
|
29712
|
+
if (!isObject3(value))
|
|
29688
29713
|
return false;
|
|
29689
29714
|
switch (value["kind"]) {
|
|
29690
29715
|
case "products":
|
|
@@ -30721,6 +30746,16 @@ const __online = Object.freeze({
|
|
|
30721
30746
|
askPush() { __call('onlineAskPush', {}) },
|
|
30722
30747
|
get state() { return __call('onlineState', {}) },
|
|
30723
30748
|
})
|
|
30749
|
+
/** The player's settings (a game's own pause menu): read as the page last said them; a change asked of the page, which
|
|
30750
|
+
* makes it after the tick and says it on a later one. */
|
|
30751
|
+
const __prefs = Object.freeze({
|
|
30752
|
+
get sound() { return __call('prefs', {}).sound === true },
|
|
30753
|
+
get music() { return __call('prefs', {}).music === true },
|
|
30754
|
+
get fullscreen() { return __call('prefs', {}).fullscreen === true },
|
|
30755
|
+
get canFullscreen() { return __call('prefs', {}).canFullscreen === true },
|
|
30756
|
+
get touch() { return __call('prefs', {}).touch === true },
|
|
30757
|
+
set(change) { __call('prefsSet', { change: change === undefined ? null : change }) },
|
|
30758
|
+
})
|
|
30724
30759
|
/** Purchases (the online services spec, part 3): asked of the page, which asks the store; answered on a later tick as a
|
|
30725
30760
|
* 'purchase' event. */
|
|
30726
30761
|
const __purchases = Object.freeze({
|
|
@@ -31006,6 +31041,7 @@ const __game = {
|
|
|
31006
31041
|
get ui() { return __ui },
|
|
31007
31042
|
get ads() { return __ads },
|
|
31008
31043
|
get online() { return __online },
|
|
31044
|
+
get prefs() { return __prefs },
|
|
31009
31045
|
get purchases() { return __purchases },
|
|
31010
31046
|
/** The game script's own fields, read only; null without one. */
|
|
31011
31047
|
get script() { return __frozen(__revived(__call('gameScript', null))) },
|
|
@@ -31440,19 +31476,19 @@ globalThis.noise = noise
|
|
|
31440
31476
|
`;
|
|
31441
31477
|
|
|
31442
31478
|
// ../core/dist/script/top-level.js
|
|
31443
|
-
var
|
|
31479
|
+
var import_parser2 = __toESM(require_parser(), 1);
|
|
31444
31480
|
var import_tokenizer = __toESM(require_tokenizer(), 1);
|
|
31445
|
-
var
|
|
31481
|
+
var import_types2 = __toESM(require_types(), 1);
|
|
31446
31482
|
var import_keywords = __toESM(require_keywords(), 1);
|
|
31447
31483
|
var CHANGEABLE_TOP_LEVEL = "a script keeps its state in its fields: a top-level let or var lives outside the game, where a snapshot cannot hold it and a second play does not start from it; make it a field, or const if it never changes";
|
|
31448
31484
|
var PRIVATE_STATIC = "a private static field cannot be frozen, so it would keep state outside the game, shared by every object: make it an instance field, or a const at the top of the script";
|
|
31449
31485
|
var PRIVATE_MEMBER = "a private field or method (#name) cannot be kept between ticks: a script's state is its plain fields, and an object made again from them, after a restore or a failed call, has no private ones; drop the #, and keep it as a field or a method";
|
|
31450
|
-
var DECLARING = /* @__PURE__ */ new Set([
|
|
31451
|
-
var OPENING = /* @__PURE__ */ new Set([
|
|
31452
|
-
var CLOSING = /* @__PURE__ */ new Set([
|
|
31486
|
+
var DECLARING = /* @__PURE__ */ new Set([import_types2.TokenType._const, import_types2.TokenType._let, import_types2.TokenType._var, import_types2.TokenType._function, import_types2.TokenType._class, import_types2.TokenType._enum]);
|
|
31487
|
+
var OPENING = /* @__PURE__ */ new Set([import_types2.TokenType.parenL, import_types2.TokenType.bracketL, import_types2.TokenType.braceL, import_types2.TokenType.dollarBraceL]);
|
|
31488
|
+
var CLOSING = /* @__PURE__ */ new Set([import_types2.TokenType.parenR, import_types2.TokenType.bracketR, import_types2.TokenType.braceR]);
|
|
31453
31489
|
var lineAt = (source, offset) => source.slice(0, offset).split("\n").length;
|
|
31454
31490
|
function topLevelOf(source) {
|
|
31455
|
-
const { tokens, scopes } = (0,
|
|
31491
|
+
const { tokens, scopes } = (0, import_parser2.parse)(source, false, true, false);
|
|
31456
31492
|
const at2 = (i) => {
|
|
31457
31493
|
const token = tokens[i];
|
|
31458
31494
|
return { name: token === void 0 ? "" : source.slice(token.start, token.end), line: lineAt(source, token?.start ?? 0) };
|
|
@@ -31464,21 +31500,21 @@ function topLevelOf(source) {
|
|
|
31464
31500
|
const functions = scopes.filter((scope) => scope.isFunctionScope && !(scope.startTokenIndex === 0 && scope.endTokenIndex >= tokens.length));
|
|
31465
31501
|
const inFunction = (i) => functions.some((scope) => scope.startTokenIndex <= i && i < scope.endTokenIndex);
|
|
31466
31502
|
for (const [i, token] of tokens.entries()) {
|
|
31467
|
-
if (token.type ===
|
|
31503
|
+
if (token.type === import_types2.TokenType._var && !inFunction(i))
|
|
31468
31504
|
changeable.push({ ...at2(i + 1), line: at2(i).line });
|
|
31469
31505
|
if (token.contextualKeyword === import_keywords.ContextualKeyword._static) {
|
|
31470
31506
|
let next = i + 1;
|
|
31471
31507
|
while (tokens[next]?.contextualKeyword === import_keywords.ContextualKeyword._readonly)
|
|
31472
31508
|
next += 1;
|
|
31473
|
-
if (tokens[next]?.type ===
|
|
31509
|
+
if (tokens[next]?.type === import_types2.TokenType.hash && tokens[next + 2]?.type !== import_types2.TokenType.parenL)
|
|
31474
31510
|
privateStatics.push({ ...at2(next + 1), name: `#${at2(next + 1).name}` });
|
|
31475
31511
|
}
|
|
31476
|
-
if (token.type ===
|
|
31512
|
+
if (token.type === import_types2.TokenType.hash) {
|
|
31477
31513
|
let back2 = i - 1;
|
|
31478
31514
|
while (tokens[back2]?.contextualKeyword === import_keywords.ContextualKeyword._readonly)
|
|
31479
31515
|
back2 -= 1;
|
|
31480
31516
|
const before2 = tokens[back2];
|
|
31481
|
-
const used = before2 !== void 0 && (before2.type ===
|
|
31517
|
+
const used = before2 !== void 0 && (before2.type === import_types2.TokenType.dot || before2.type === import_types2.TokenType.questionDot);
|
|
31482
31518
|
if (!used && before2?.contextualKeyword !== import_keywords.ContextualKeyword._static)
|
|
31483
31519
|
privateMembers.push({ ...at2(i + 1), name: `#${at2(i + 1).name}` });
|
|
31484
31520
|
}
|
|
@@ -31498,7 +31534,7 @@ function topLevelOf(source) {
|
|
|
31498
31534
|
else if (depth <= 0 && DECLARING.has(type))
|
|
31499
31535
|
break;
|
|
31500
31536
|
}
|
|
31501
|
-
if (tokens[back]?.type ===
|
|
31537
|
+
if (tokens[back]?.type === import_types2.TokenType._let)
|
|
31502
31538
|
changeable.push(declared);
|
|
31503
31539
|
}
|
|
31504
31540
|
changeable.sort((a, b) => a.line - b.line);
|
|
@@ -32369,6 +32405,8 @@ function initialScripts(given, kinds, seed = 1, saved = {}, audio, fonts, adsOff
|
|
|
32369
32405
|
...adsOff === void 0 ? {} : { adsOff },
|
|
32370
32406
|
// A game whose scripts mention online keeps its state and call count from the first tick; any other never has them.
|
|
32371
32407
|
...usesOnline([...Object.values(sources.kinds), sources.game ?? "", ...Object.values(sources.modules ?? {})]) ? { onlineState: "off", nextCall: 1 } : {},
|
|
32408
|
+
// A game whose scripts mention the player's settings keeps them from the first tick; any other never has them.
|
|
32409
|
+
...usesPrefs([...Object.values(sources.kinds), sources.game ?? "", ...Object.values(sources.modules ?? {})]) ? { prefs: NO_PREFS_YET } : {},
|
|
32372
32410
|
// A game whose scripts mention purchases keeps its products from the first tick; any other never has them.
|
|
32373
32411
|
...usesPurchases([...Object.values(sources.kinds), sources.game ?? "", ...Object.values(sources.modules ?? {})]) ? { purchases: NO_PURCHASES_YET } : {}
|
|
32374
32412
|
};
|
|
@@ -33981,7 +34019,7 @@ type Side = 'above' | 'below' | 'side' | 'overlap'
|
|
|
33981
34019
|
type Colour = string | number
|
|
33982
34020
|
/* KindName, PropName and Button are each game's own (the whole-engine review, I11): its
|
|
33983
34021
|
* kinds; the properties it declares, or a rule or a script sets; and the buttons every game
|
|
33984
|
-
* has (left, right, up, down, jump, action,
|
|
34022
|
+
* has (left, right, up, down, jump, action, click, the left mouse button, and menu: see Prefs) with those its
|
|
33985
34023
|
* controls name. A name written
|
|
33986
34024
|
* in quotes that the game does not have is an error where it is written; a name held in a
|
|
33987
34025
|
* string (a field, a table) is checked as the game plays. */
|
|
@@ -34293,87 +34331,6 @@ interface Turns {
|
|
|
34293
34331
|
readonly round: number
|
|
34294
34332
|
}
|
|
34295
34333
|
|
|
34296
|
-
/** Where a UI element sits in what holds it (the screen, or the element its in names): one of nine points. */
|
|
34297
|
-
type UiAnchor = 'top-left' | 'top' | 'top-right' | 'left' | 'center' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right'
|
|
34298
|
-
/** A UI element, in design units (480 by 270 unless ui.design says), scaled to the real screen: the design's long side to
|
|
34299
|
-
* its long side, so a phone held either way shows it at the same size, and what is wider than a narrow screen is the
|
|
34300
|
-
* game's to lay out (a playtest look with ui: true says what runs off). */
|
|
34301
|
-
interface UiSpec {
|
|
34302
|
-
/** 'panel' when left out. Only a button is hovered, pressed, clicked and focused. A row or column places the elements
|
|
34303
|
-
* whose in names it, in the order they were first set, gap apart, and is as big as them unless given a size. */
|
|
34304
|
-
kind?: 'panel' | 'button' | 'label' | 'meter' | 'icon' | 'row' | 'column'
|
|
34305
|
-
/** The point of what holds it that it sits at; 'top-left' when left out. */
|
|
34306
|
-
anchor?: UiAnchor
|
|
34307
|
-
/** Design units in from the anchored edges: { anchor: 'top-right', at: [8, 8] } sits 8 in from the top and the
|
|
34308
|
-
* right; from a middle, x moves right and y down. [0, 0] when left out. A row or column places its own. */
|
|
34309
|
-
at?: [number, number]
|
|
34310
|
-
/** [width, height] in design units: needed, but for a row or column. */
|
|
34311
|
-
size?: [number, number]
|
|
34312
|
-
/** The element it sits inside, by name: anchored in its box, or placed by it when it is a row or column. */
|
|
34313
|
-
in?: string
|
|
34314
|
-
/** Design units between what a row or column holds. */
|
|
34315
|
-
gap?: number
|
|
34316
|
-
/** Design units inside a row's or column's edge. */
|
|
34317
|
-
pad?: number
|
|
34318
|
-
/** Where what a row or column holds sits across it; 'center' when left out. */
|
|
34319
|
-
align?: 'start' | 'center' | 'end'
|
|
34320
|
-
/** Never hovered, pressed, focused or clicked; its state reads 'disabled', to draw it greyed. */
|
|
34321
|
-
disabled?: boolean
|
|
34322
|
-
/** false starts it hidden, for ui.show to bring in. */
|
|
34323
|
-
shown?: boolean
|
|
34324
|
-
}
|
|
34325
|
-
/** An element as this screen shows it, in pixels as g.screen draws. */
|
|
34326
|
-
interface UiBox {
|
|
34327
|
-
readonly x: number
|
|
34328
|
-
readonly y: number
|
|
34329
|
-
readonly w: number
|
|
34330
|
-
readonly h: number
|
|
34331
|
-
/** 0 hidden to 1 shown, eased, as ui.show moves it: draw by it for a slide, a fade or a pop. */
|
|
34332
|
-
readonly shown: number
|
|
34333
|
-
readonly state: 'disabled' | 'pressed' | 'hover' | 'focused' | 'idle'
|
|
34334
|
-
/** Screen pixels in one design unit: size what is drawn in the box by it, as 14 * b.scale. */
|
|
34335
|
-
readonly scale: number
|
|
34336
|
-
}
|
|
34337
|
-
/** Text fitted to a box: the size it fits at, its lines, and whether what did not fit was cut. */
|
|
34338
|
-
interface UiFit { readonly size: number; readonly lines: string[]; readonly width: number; readonly height: number; readonly cut: boolean }
|
|
34339
|
-
/** game.ui: the game's UI elements. The engine places them on any screen, kept clear of a phone's notch and corners,
|
|
34340
|
-
* and tracks the pointer, a finger, presses, clicks and focus. It draws nothing: each element looks as the game's draw
|
|
34341
|
-
* draws it at ui.box, so make it look like this game and no other. Declare in start (or in update: the same again
|
|
34342
|
-
* changes nothing), read clicks in update or whilePaused, draw in draw. */
|
|
34343
|
-
interface Ui {
|
|
34344
|
-
/** Declares an element, or changes one; 64 at most. Never in draw. */
|
|
34345
|
-
set(name: string, spec: UiSpec): void
|
|
34346
|
-
/** Takes an element away, with those inside it. */
|
|
34347
|
-
remove(name: string): void
|
|
34348
|
-
/** Eases it in (true) or out (false) over the seconds (0.2 when left out), on the real clock, so a pause menu slides
|
|
34349
|
-
* in while the world holds. A hidden one is never clicked. */
|
|
34350
|
-
show(name: string, shown: boolean, seconds?: number): void
|
|
34351
|
-
/** Gives the keys to a menu: up, down, left and right move focus to the nearest button that way, and jump or action
|
|
34352
|
-
* clicks it. The game still sees those buttons, so hold its own controls while ui.focused is not null; null gives
|
|
34353
|
-
* them back. */
|
|
34354
|
-
focus(name: string | null): void
|
|
34355
|
-
/** The size the elements are written in, 480 by 270 when never set. */
|
|
34356
|
-
design(width: number, height: number): void
|
|
34357
|
-
/** Where it is on this screen and how it stands; null for a name never set. update reads it at the screen played,
|
|
34358
|
-
* draw at the screen drawn. */
|
|
34359
|
-
box(name: string): UiBox | null
|
|
34360
|
-
/** How it stands: idle, hover, pressed, focused or disabled; null for a name not set, as box gives. */
|
|
34361
|
-
state(name: string): UiBox['state'] | null
|
|
34362
|
-
/** Clicked since the last update: pressed and let go on it, by the pointer or a finger, or jump or action on its
|
|
34363
|
-
* focus. Heard once. A press on a button is the UI's: input.pressed('click') never sees it. False for a name not
|
|
34364
|
-
* set, so a game reading one it removed runs on. */
|
|
34365
|
-
clicked(name: string): boolean
|
|
34366
|
-
/** The largest size, from size (16 when left out) down to min (8), at which the text wrapped to the box fits it; at
|
|
34367
|
-
* min, what still does not fit is cut, ending in an ellipsis. text's fit option does this as it draws. */
|
|
34368
|
-
fit(text: string | number, box: { w: number; h: number }, options?: { size?: number; min?: number; font?: FontName; lineHeight?: number }): UiFit
|
|
34369
|
-
/** The focused button, or null. */
|
|
34370
|
-
readonly focused: string | null
|
|
34371
|
-
/** Screen pixels in one design unit on the screen played. */
|
|
34372
|
-
readonly scale: number
|
|
34373
|
-
/** The names set, in the order they were first set. */
|
|
34374
|
-
readonly names: readonly string[]
|
|
34375
|
-
}
|
|
34376
|
-
|
|
34377
34334
|
/** A level's back layer, drawn behind the objects and darker, or its front layer, drawn over them; neither collides. */
|
|
34378
34335
|
interface TileLayerOption { layer?: 'back' | 'front' }
|
|
34379
34336
|
|
|
@@ -34683,6 +34640,8 @@ interface Game {
|
|
|
34683
34640
|
readonly ads: Ads
|
|
34684
34641
|
/** The game's own server: calls and live rooms. */
|
|
34685
34642
|
readonly online: Online
|
|
34643
|
+
/** The player's sound, music and fullscreen, for the game's own pause menu. */
|
|
34644
|
+
readonly prefs: Prefs
|
|
34686
34645
|
/** Real-money purchases in the game's store app. */
|
|
34687
34646
|
readonly purchases: Purchases
|
|
34688
34647
|
/** Opens the game's privacy policy, as a game on the app stores must from inside itself: call it when the player
|
|
@@ -34742,6 +34701,116 @@ interface Game {
|
|
|
34742
34701
|
burst(x: In3D<number, 'burst(x, y, z)'>, y: number, z: number, options?: BurstOptions): void
|
|
34743
34702
|
}
|
|
34744
34703
|
|
|
34704
|
+
/** The player's own settings, which the page keeps for every game they play: what a game's own pause menu shows and
|
|
34705
|
+
* changes. A game whose scripts read the button 'menu' draws its own pause menu: the page presses menu where it would
|
|
34706
|
+
* open its own (P, Escape, a pad's Start, the touch pause button, an app's back button), and the game pauses itself
|
|
34707
|
+
* with game.pause() and resumes with game.resume(). Read as the page last said; a change is asked of the page, which
|
|
34708
|
+
* makes it after the tick and says it on a later one. */
|
|
34709
|
+
interface Prefs {
|
|
34710
|
+
/** Sound effects on. */
|
|
34711
|
+
readonly sound: boolean
|
|
34712
|
+
/** Music on. */
|
|
34713
|
+
readonly music: boolean
|
|
34714
|
+
/** The page fills the screen. */
|
|
34715
|
+
readonly fullscreen: boolean
|
|
34716
|
+
/** The page may fill the screen: false where it cannot (a frame that forbids it, most phones' browsers, an app), so a
|
|
34717
|
+
* menu leaves its Fullscreen row out. */
|
|
34718
|
+
readonly canFullscreen: boolean
|
|
34719
|
+
/** The player is on a touch screen (a finger has touched it): show the game's own touch controls, the UI elements
|
|
34720
|
+
* with touch, and its sticks. */
|
|
34721
|
+
readonly touch: boolean
|
|
34722
|
+
/** Asks the page to change them: prefs.set({ music: !this.game.prefs.music }). Fullscreen only on a press or a click,
|
|
34723
|
+
* as browsers allow it. Never from draw. */
|
|
34724
|
+
set(change: { sound?: boolean; music?: boolean; fullscreen?: boolean }): void
|
|
34725
|
+
}
|
|
34726
|
+
|
|
34727
|
+
/** Where a UI element sits in what holds it (the screen, or the element its in names): one of nine points. */
|
|
34728
|
+
type UiAnchor = 'top-left' | 'top' | 'top-right' | 'left' | 'center' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right'
|
|
34729
|
+
/** A UI element, in design units (480 by 270 unless ui.design says), scaled to the real screen: the design's long side to
|
|
34730
|
+
* its long side, so a phone held either way shows it at the same size, and what is wider than a narrow screen is the
|
|
34731
|
+
* game's to lay out (a playtest look with ui: true says what runs off). */
|
|
34732
|
+
interface UiSpec {
|
|
34733
|
+
/** 'panel' when left out. Only a button is hovered, pressed, clicked and focused. A row or column places the elements
|
|
34734
|
+
* whose in names it, in the order they were first set, gap apart, and is as big as them unless given a size. A stick
|
|
34735
|
+
* is a touch control: a finger on it (or that drifts off its edge) is player 1's left stick, -1 to 1 each way from its
|
|
34736
|
+
* middle, as input.stick('left') reads it. */
|
|
34737
|
+
kind?: 'panel' | 'button' | 'label' | 'meter' | 'icon' | 'row' | 'column' | 'stick'
|
|
34738
|
+
/** The point of what holds it that it sits at; 'top-left' when left out. */
|
|
34739
|
+
anchor?: UiAnchor
|
|
34740
|
+
/** Design units in from the anchored edges: { anchor: 'top-right', at: [8, 8] } sits 8 in from the top and the
|
|
34741
|
+
* right; from a middle, x moves right and y down. [0, 0] when left out. A row or column places its own. */
|
|
34742
|
+
at?: [number, number]
|
|
34743
|
+
/** [width, height] in design units: needed, but for a row or column. */
|
|
34744
|
+
size?: [number, number]
|
|
34745
|
+
/** The element it sits inside, by name: anchored in its box, or placed by it when it is a row or column. */
|
|
34746
|
+
in?: string
|
|
34747
|
+
/** Design units between what a row or column holds. */
|
|
34748
|
+
gap?: number
|
|
34749
|
+
/** Design units inside a row's or column's edge. */
|
|
34750
|
+
pad?: number
|
|
34751
|
+
/** Where what a row or column holds sits across it; 'center' when left out. */
|
|
34752
|
+
align?: 'start' | 'center' | 'end'
|
|
34753
|
+
/** Never hovered, pressed, focused or clicked; its state reads 'disabled', to draw it greyed. */
|
|
34754
|
+
disabled?: boolean
|
|
34755
|
+
/** false starts it hidden, for ui.show to bring in. */
|
|
34756
|
+
shown?: boolean
|
|
34757
|
+
/** A touch control (the game's own, which the page then draws none of): the button a finger on it holds, as a key
|
|
34758
|
+
* would, read by input.held and input.pressed; a finger on it, or on a stick, is never a click. Declare touch
|
|
34759
|
+
* controls shown: false and ui.show them while game.prefs.touch: a hidden one takes no finger. */
|
|
34760
|
+
touch?: Buttons
|
|
34761
|
+
}
|
|
34762
|
+
/** An element as this screen shows it, in pixels as g.screen draws. */
|
|
34763
|
+
interface UiBox {
|
|
34764
|
+
readonly x: number
|
|
34765
|
+
readonly y: number
|
|
34766
|
+
readonly w: number
|
|
34767
|
+
readonly h: number
|
|
34768
|
+
/** 0 hidden to 1 shown, eased, as ui.show moves it: draw by it for a slide, a fade or a pop. */
|
|
34769
|
+
readonly shown: number
|
|
34770
|
+
readonly state: 'disabled' | 'pressed' | 'hover' | 'focused' | 'idle'
|
|
34771
|
+
/** Screen pixels in one design unit: size what is drawn in the box by it, as 14 * b.scale. */
|
|
34772
|
+
readonly scale: number
|
|
34773
|
+
}
|
|
34774
|
+
/** Text fitted to a box: the size it fits at, its lines, and whether what did not fit was cut. */
|
|
34775
|
+
interface UiFit { readonly size: number; readonly lines: string[]; readonly width: number; readonly height: number; readonly cut: boolean }
|
|
34776
|
+
/** game.ui: the game's UI elements. The engine places them on any screen, kept clear of a phone's notch and corners,
|
|
34777
|
+
* and tracks the pointer, a finger, presses, clicks and focus. It draws nothing: each element looks as the game's draw
|
|
34778
|
+
* draws it at ui.box, so make it look like this game and no other. Declare in start (or in update: the same again
|
|
34779
|
+
* changes nothing), read clicks in update or whilePaused, draw in draw. */
|
|
34780
|
+
interface Ui {
|
|
34781
|
+
/** Declares an element, or changes one; 64 at most. Never in draw. */
|
|
34782
|
+
set(name: string, spec: UiSpec): void
|
|
34783
|
+
/** Takes an element away, with those inside it. */
|
|
34784
|
+
remove(name: string): void
|
|
34785
|
+
/** Eases it in (true) or out (false) over the seconds (0.2 when left out), on the real clock, so a pause menu slides
|
|
34786
|
+
* in while the world holds. A hidden one is never clicked. */
|
|
34787
|
+
show(name: string, shown: boolean, seconds?: number): void
|
|
34788
|
+
/** Gives the keys to a menu: up, down, left and right move focus to the nearest button that way, and jump or action
|
|
34789
|
+
* clicks it. The game still sees those buttons, so hold its own controls while ui.focused is not null; null gives
|
|
34790
|
+
* them back. */
|
|
34791
|
+
focus(name: string | null): void
|
|
34792
|
+
/** The size the elements are written in, 480 by 270 when never set. */
|
|
34793
|
+
design(width: number, height: number): void
|
|
34794
|
+
/** Where it is on this screen and how it stands; null for a name never set. update reads it at the screen played,
|
|
34795
|
+
* draw at the screen drawn. */
|
|
34796
|
+
box(name: string): UiBox | null
|
|
34797
|
+
/** How it stands: idle, hover, pressed, focused or disabled; null for a name not set, as box gives. */
|
|
34798
|
+
state(name: string): UiBox['state'] | null
|
|
34799
|
+
/** Clicked since the last update: pressed and let go on it, by the pointer or a finger, or jump or action on its
|
|
34800
|
+
* focus. Heard once. A press on a button is the UI's: input.pressed('click') never sees it, nor the game's own button
|
|
34801
|
+
* bound to Click. False for a name not set, so a game reading one it removed runs on. */
|
|
34802
|
+
clicked(name: string): boolean
|
|
34803
|
+
/** The largest size, from size (16 when left out) down to min (8), at which the text wrapped to the box fits it; at
|
|
34804
|
+
* min, what still does not fit is cut, ending in an ellipsis. text's fit option does this as it draws. */
|
|
34805
|
+
fit(text: string | number, box: { w: number; h: number }, options?: { size?: number; min?: number; font?: FontName; lineHeight?: number }): UiFit
|
|
34806
|
+
/** The focused button, or null. */
|
|
34807
|
+
readonly focused: string | null
|
|
34808
|
+
/** Screen pixels in one design unit on the screen played. */
|
|
34809
|
+
readonly scale: number
|
|
34810
|
+
/** The names set, in the order they were first set. */
|
|
34811
|
+
readonly names: readonly string[]
|
|
34812
|
+
}
|
|
34813
|
+
|
|
34745
34814
|
/** On g.screen, a drawing placed from a point of the world, a Thing's middle or a point in tiles: its x and y are then
|
|
34746
34815
|
* pixels from where that point is on the screen this frame (a health bar over an enemy, a name over a character).
|
|
34747
34816
|
* edge keeps it inside the screen by that many pixels, as an arrow to something off it does. The world layers take
|
|
@@ -34905,10 +34974,6 @@ declare const console: {
|
|
|
34905
34974
|
}
|
|
34906
34975
|
`;
|
|
34907
34976
|
|
|
34908
|
-
// ../core/dist/script/scan.js
|
|
34909
|
-
var import_parser2 = __toESM(require_parser(), 1);
|
|
34910
|
-
var import_types2 = __toESM(require_types(), 1);
|
|
34911
|
-
|
|
34912
34977
|
// ../core/dist/script/shader-wrap.js
|
|
34913
34978
|
var UNIFORM_NUMBERS = {
|
|
34914
34979
|
float: 1,
|
|
@@ -49517,12 +49582,13 @@ function rayOf(text2) {
|
|
|
49517
49582
|
var LOOK_WORDS = "which must be <dx,dy in whole pixels from -4000 to 4000, like <12,-3";
|
|
49518
49583
|
var RAY_WORDS = "which must be ?ox,oy,oz,dx,dy,dz: its origin in 256ths of a block and its direction in 10,000ths, whole numbers, like ?1280,512,0,0,-7071,7071";
|
|
49519
49584
|
function onlineKey(input) {
|
|
49520
|
-
if (input.online === void 0 && input.onlineState === void 0 && input.purchases === void 0)
|
|
49585
|
+
if (input.online === void 0 && input.onlineState === void 0 && input.purchases === void 0 && input.prefs === void 0)
|
|
49521
49586
|
return "";
|
|
49522
49587
|
const json = JSON.stringify({
|
|
49523
49588
|
...input.online === void 0 ? {} : { h: input.online },
|
|
49524
49589
|
...input.onlineState === void 0 ? {} : { s: input.onlineState },
|
|
49525
|
-
...input.purchases === void 0 ? {} : { p: input.purchases }
|
|
49590
|
+
...input.purchases === void 0 ? {} : { p: input.purchases },
|
|
49591
|
+
...input.prefs === void 0 ? {} : { f: input.prefs }
|
|
49526
49592
|
});
|
|
49527
49593
|
let text2 = "";
|
|
49528
49594
|
for (const byte of new TextEncoder().encode(json))
|
|
@@ -49538,19 +49604,22 @@ function onlineOf(text2) {
|
|
|
49538
49604
|
const read2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
49539
49605
|
if (typeof read2 !== "object" || read2 === null || Array.isArray(read2))
|
|
49540
49606
|
return null;
|
|
49541
|
-
const { h, s, p } = read2;
|
|
49607
|
+
const { h, s, p, f: f2 } = read2;
|
|
49542
49608
|
if (h !== void 0 && (!Array.isArray(h) || !h.every(isOnlineHeard)))
|
|
49543
49609
|
return null;
|
|
49544
49610
|
if (s !== void 0 && !ONLINE_STATES.includes(s))
|
|
49545
49611
|
return null;
|
|
49546
49612
|
if (p !== void 0 && (!Array.isArray(p) || !p.every(isPurchaseHeard)))
|
|
49547
49613
|
return null;
|
|
49548
|
-
if (
|
|
49614
|
+
if (f2 !== void 0 && !isPrefs(f2))
|
|
49615
|
+
return null;
|
|
49616
|
+
if (h === void 0 && s === void 0 && p === void 0 && f2 === void 0)
|
|
49549
49617
|
return null;
|
|
49550
49618
|
return {
|
|
49551
49619
|
...h === void 0 ? {} : { online: h },
|
|
49552
49620
|
...s === void 0 ? {} : { onlineState: s },
|
|
49553
|
-
...p === void 0 ? {} : { purchases: p }
|
|
49621
|
+
...p === void 0 ? {} : { purchases: p },
|
|
49622
|
+
...f2 === void 0 ? {} : { prefs: f2 }
|
|
49554
49623
|
};
|
|
49555
49624
|
} catch {
|
|
49556
49625
|
return null;
|
|
@@ -51209,7 +51278,7 @@ function nextStepOf(tiles, field2, from, size2, diagonal) {
|
|
|
51209
51278
|
}
|
|
51210
51279
|
|
|
51211
51280
|
// ../core/dist/script/ui.js
|
|
51212
|
-
var UI_KINDS = ["panel", "button", "label", "meter", "icon", "row", "column"];
|
|
51281
|
+
var UI_KINDS = ["panel", "button", "label", "meter", "icon", "row", "column", "stick"];
|
|
51213
51282
|
var UI_ANCHORS = ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right"];
|
|
51214
51283
|
var MAX_UI_ELEMENTS = 64;
|
|
51215
51284
|
var MOST_CLICKED = 16;
|
|
@@ -51227,7 +51296,7 @@ var NO_UI = {
|
|
|
51227
51296
|
clicked: []
|
|
51228
51297
|
};
|
|
51229
51298
|
var NAME2 = /^[a-z][a-z0-9_-]{0,39}$/;
|
|
51230
|
-
var KEYS = ["kind", "anchor", "at", "size", "in", "gap", "pad", "align", "disabled", "shown"];
|
|
51299
|
+
var KEYS = ["kind", "anchor", "at", "size", "in", "gap", "pad", "align", "disabled", "shown", "touch"];
|
|
51231
51300
|
var words = (list2) => `${list2.slice(0, -1).map((w) => `'${w}'`).join(", ")} or '${list2[list2.length - 1] ?? ""}'`;
|
|
51232
51301
|
function uiName(raw, what) {
|
|
51233
51302
|
if (typeof raw === "string" && NAME2.test(raw))
|
|
@@ -51276,6 +51345,14 @@ function uiSpecOf(raw, name) {
|
|
|
51276
51345
|
throw new Error(`${label}: disabled is true or false`);
|
|
51277
51346
|
if (o.shown !== void 0 && typeof o.shown !== "boolean")
|
|
51278
51347
|
throw new Error(`${label}: shown is true or false`);
|
|
51348
|
+
const touch = o.touch === void 0 || o.touch === null ? null : o.touch;
|
|
51349
|
+
if (touch !== null && (typeof touch !== "string" || !/^[a-z][a-z0-9_]{0,39}$/.test(touch))) {
|
|
51350
|
+
throw new Error(`${label}: touch is the name of a button a finger on it holds, like 'jump' or 'fire', not ${JSON.stringify(touch)}`);
|
|
51351
|
+
}
|
|
51352
|
+
if (touch !== null && kind === "stick")
|
|
51353
|
+
throw new Error(`${label}: a stick moves the stick, player 1's left; it holds no button, so it takes no touch`);
|
|
51354
|
+
if (touch !== null && container)
|
|
51355
|
+
throw new Error(`${label}: a row or a column holds its elements, not a button; give touch to an element inside it`);
|
|
51279
51356
|
return {
|
|
51280
51357
|
kind,
|
|
51281
51358
|
anchor,
|
|
@@ -51285,7 +51362,8 @@ function uiSpecOf(raw, name) {
|
|
|
51285
51362
|
gap: units(o.gap, `${label}: gap`, 0),
|
|
51286
51363
|
pad: units(o.pad, `${label}: pad`, 0),
|
|
51287
51364
|
align,
|
|
51288
|
-
disabled: o.disabled === true
|
|
51365
|
+
disabled: o.disabled === true,
|
|
51366
|
+
...touch === null ? {} : { touch }
|
|
51289
51367
|
};
|
|
51290
51368
|
}
|
|
51291
51369
|
function safeInsets(screen) {
|
|
@@ -51427,24 +51505,55 @@ function uiStateOf(ui, name) {
|
|
|
51427
51505
|
var sameList = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
51428
51506
|
var FOCUS_KEYS = ["left", "right", "up", "down", "jump", "action"];
|
|
51429
51507
|
var uiFrom = (input) => ({ ...NO_UI, down: input.buttons?.includes("click") === true, keys: FOCUS_KEYS.filter((key) => input[key]) });
|
|
51430
|
-
function
|
|
51508
|
+
function pressable(ui, name, real) {
|
|
51509
|
+
const spec = ui.elements[name];
|
|
51510
|
+
return spec !== void 0 && spec.kind === "button" && !spec.disabled && shownOf2(ui, name, real) > 0;
|
|
51511
|
+
}
|
|
51512
|
+
function boxHolds(boxes, name, p) {
|
|
51513
|
+
const b = boxes.get(name);
|
|
51514
|
+
return b !== void 0 && p.x >= b.x && p.x < b.x + b.w && p.y >= b.y && p.y < b.y + b.h;
|
|
51515
|
+
}
|
|
51516
|
+
function uiButtonAt(ui, point, screen, real, boxes = layoutUi(ui, screen)) {
|
|
51517
|
+
let found = null;
|
|
51518
|
+
for (const name of ui.order)
|
|
51519
|
+
if (pressable(ui, name, real) && boxHolds(boxes, name, point))
|
|
51520
|
+
found = name;
|
|
51521
|
+
return found;
|
|
51522
|
+
}
|
|
51523
|
+
function stickHolds(box, p) {
|
|
51524
|
+
return p.x >= box.x - box.w / 2 && p.x <= box.x + box.w * 1.5 && p.y >= box.y - box.h / 2 && p.y <= box.y + box.h * 1.5;
|
|
51525
|
+
}
|
|
51526
|
+
function touchedOf(ui, input, real) {
|
|
51527
|
+
const fingers = input.touches ?? [];
|
|
51528
|
+
if (fingers.length === 0)
|
|
51529
|
+
return { held: [], stick: null };
|
|
51431
51530
|
const screen = { width: input.screenSize?.w ?? SCREEN_DEFAULT.w, height: input.screenSize?.h ?? SCREEN_DEFAULT.h };
|
|
51432
51531
|
const boxes = layoutUi(ui, screen);
|
|
51433
|
-
const
|
|
51532
|
+
const held = [];
|
|
51533
|
+
let stick = null;
|
|
51534
|
+
const clamp4 = (n) => Math.max(-1, Math.min(1, Math.round(n * 100) / 100)) + 0;
|
|
51535
|
+
for (const name of ui.order) {
|
|
51434
51536
|
const spec = ui.elements[name];
|
|
51435
|
-
|
|
51436
|
-
|
|
51437
|
-
|
|
51438
|
-
|
|
51439
|
-
|
|
51440
|
-
|
|
51441
|
-
|
|
51442
|
-
|
|
51443
|
-
|
|
51444
|
-
|
|
51445
|
-
if (eligible(name) && holds2(name, pointer))
|
|
51446
|
-
hover = name;
|
|
51537
|
+
const box = boxes.get(name);
|
|
51538
|
+
if (spec === void 0 || box === void 0 || spec.disabled || shownOf2(ui, name, real) <= 0)
|
|
51539
|
+
continue;
|
|
51540
|
+
if (spec.touch !== void 0 && !held.includes(spec.touch) && fingers.some((f2) => boxHolds(boxes, name, f2)))
|
|
51541
|
+
held.push(spec.touch);
|
|
51542
|
+
if (spec.kind === "stick" && stick === null) {
|
|
51543
|
+
const finger = fingers.find((f2) => stickHolds(box, f2));
|
|
51544
|
+
if (finger !== void 0)
|
|
51545
|
+
stick = { x: clamp4((finger.x - (box.x + box.w / 2)) / (box.w / 2)), y: clamp4((finger.y - (box.y + box.h / 2)) / (box.h / 2)) };
|
|
51546
|
+
}
|
|
51447
51547
|
}
|
|
51548
|
+
return { held, stick };
|
|
51549
|
+
}
|
|
51550
|
+
function stepUi(ui, input, real) {
|
|
51551
|
+
const screen = { width: input.screenSize?.w ?? SCREEN_DEFAULT.w, height: input.screenSize?.h ?? SCREEN_DEFAULT.h };
|
|
51552
|
+
const boxes = layoutUi(ui, screen);
|
|
51553
|
+
const eligible = (name) => pressable(ui, name, real);
|
|
51554
|
+
const holds2 = (name, p) => boxHolds(boxes, name, p);
|
|
51555
|
+
const pointer = input.screenPointer ?? input.touches?.[0] ?? null;
|
|
51556
|
+
const hover = pointer === null ? null : uiButtonAt(ui, pointer, screen, real, boxes);
|
|
51448
51557
|
const held = input.buttons?.includes("click") === true;
|
|
51449
51558
|
const clicked = [...ui.clicked];
|
|
51450
51559
|
const click = (name) => {
|
|
@@ -51845,7 +51954,7 @@ var OWN_AUDIO = /^own:[a-z0-9][a-z0-9-]{0,39}$/;
|
|
|
51845
51954
|
var LIB_AUDIO = /^lib:[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/;
|
|
51846
51955
|
var MOST_BURSTS = 64;
|
|
51847
51956
|
var MOST_DEFERRED = 1024;
|
|
51848
|
-
var DRAW_OPS = /* @__PURE__ */ new Set(["thing", "data", "fields", "shown", "find", "findAll", "findIn", "touching", "tileAt", "tileIn", "tileUnder", "raycast", "path", "setting", "load", "random", "log", "drawn", "clock", "running", "measure", "bodyOf", "nextStep", "vars", "shared", "gameScript", "parentOf", "levelSize", "moving", "turns", "mixer", "typing", "t", "language", "languages", "ui", "uiFit", "onlineState", "purchaseProducts", "overlap3", "blockAt", "toScreen"]);
|
|
51957
|
+
var DRAW_OPS = /* @__PURE__ */ new Set(["thing", "data", "fields", "shown", "find", "findAll", "findIn", "touching", "tileAt", "tileIn", "tileUnder", "raycast", "path", "setting", "load", "random", "log", "drawn", "clock", "running", "measure", "bodyOf", "nextStep", "vars", "shared", "gameScript", "parentOf", "levelSize", "moving", "turns", "mixer", "typing", "t", "language", "languages", "ui", "uiFit", "onlineState", "prefs", "purchaseProducts", "overlap3", "blockAt", "toScreen"]);
|
|
51849
51958
|
function screenOf2(input) {
|
|
51850
51959
|
const size2 = input.screenSize ?? SCREEN_DEFAULT;
|
|
51851
51960
|
return { screen: input.screenPointer ?? null, size: { width: size2.w, height: size2.h } };
|
|
@@ -51914,7 +52023,7 @@ function fromUnit(raw, unit, what) {
|
|
|
51914
52023
|
return fromInt(Math.round(value));
|
|
51915
52024
|
}
|
|
51916
52025
|
}
|
|
51917
|
-
var BOT_OPS = /* @__PURE__ */ new Set(["thing", "data", "fields", "shown", "find", "findAll", "findIn", "touching", "tileAt", "tileIn", "tileUnder", "raycast", "path", "setting", "load", "random", "log", "clock", "running", "measure", "bodyOf", "nextStep", "vars", "shared", "gameScript", "parentOf", "levelSize", "moving", "turns", "mixer", "typing", "t", "language", "languages", "ui", "uiFit", "onlineState", "purchaseProducts", "overlap3", "blockAt"]);
|
|
52026
|
+
var BOT_OPS = /* @__PURE__ */ new Set(["thing", "data", "fields", "shown", "find", "findAll", "findIn", "touching", "tileAt", "tileIn", "tileUnder", "raycast", "path", "setting", "load", "random", "log", "clock", "running", "measure", "bodyOf", "nextStep", "vars", "shared", "gameScript", "parentOf", "levelSize", "moving", "turns", "mixer", "typing", "t", "language", "languages", "ui", "uiFit", "onlineState", "prefs", "purchaseProducts", "overlap3", "blockAt"]);
|
|
51918
52027
|
function sidesOf(normal) {
|
|
51919
52028
|
switch (normal) {
|
|
51920
52029
|
case "a_above_b":
|
|
@@ -52035,6 +52144,9 @@ var Turn = class {
|
|
|
52035
52144
|
* game that never mentions online (the online services spec). */
|
|
52036
52145
|
online;
|
|
52037
52146
|
onlineState;
|
|
52147
|
+
/** The player's settings as the input last said, and the change of them asked this tick (a game's own pause menu). */
|
|
52148
|
+
prefs;
|
|
52149
|
+
prefsAsk;
|
|
52038
52150
|
nextCall;
|
|
52039
52151
|
/** What the scripts asked of the store this tick, and what the game keeps of its purchases, absent for a game that
|
|
52040
52152
|
* never mentions them (the online services spec, part 3). */
|
|
@@ -52106,6 +52218,8 @@ var Turn = class {
|
|
|
52106
52218
|
this.online = fresh || scripts.online === void 0 ? void 0 : [...scripts.online];
|
|
52107
52219
|
this.onlineState = scripts.onlineState;
|
|
52108
52220
|
this.nextCall = scripts.nextCall;
|
|
52221
|
+
this.prefs = scripts.prefs;
|
|
52222
|
+
this.prefsAsk = fresh ? void 0 : scripts.prefsAsk;
|
|
52109
52223
|
this.purchase = fresh ? void 0 : scripts.purchase;
|
|
52110
52224
|
this.purchases = scripts.purchases;
|
|
52111
52225
|
this.music = scripts.music;
|
|
@@ -52261,6 +52375,8 @@ var Turn = class {
|
|
|
52261
52375
|
nextCall: this.nextCall,
|
|
52262
52376
|
purchase: this.purchase,
|
|
52263
52377
|
purchases: this.purchases,
|
|
52378
|
+
prefs: this.prefs,
|
|
52379
|
+
prefsAsk: this.prefsAsk,
|
|
52264
52380
|
bursts: this.bursts.length === 0 ? [] : [...this.bursts],
|
|
52265
52381
|
screen: this.screen,
|
|
52266
52382
|
waiting: this.waiting,
|
|
@@ -52320,6 +52436,8 @@ var Turn = class {
|
|
|
52320
52436
|
this.online = was.online;
|
|
52321
52437
|
this.onlineState = was.onlineState;
|
|
52322
52438
|
this.nextCall = was.nextCall;
|
|
52439
|
+
this.prefs = was.prefs;
|
|
52440
|
+
this.prefsAsk = was.prefsAsk;
|
|
52323
52441
|
this.purchase = was.purchase;
|
|
52324
52442
|
this.purchases = was.purchases;
|
|
52325
52443
|
this.bursts = was.bursts;
|
|
@@ -53661,6 +53779,14 @@ var Turn = class {
|
|
|
53661
53779
|
return null;
|
|
53662
53780
|
case "onlineState":
|
|
53663
53781
|
return this.onlineState ?? "off";
|
|
53782
|
+
// The player's settings (a game's own pause menu): read as the input last said; a change asked of the page, which
|
|
53783
|
+
// makes it after the tick and says it on a later one.
|
|
53784
|
+
case "prefs":
|
|
53785
|
+
return this.prefs ?? NO_PREFS_YET;
|
|
53786
|
+
case "prefsSet": {
|
|
53787
|
+
this.prefsAsk = { ...this.prefsAsk ?? {}, ...prefsChangeOf(args.change) };
|
|
53788
|
+
return null;
|
|
53789
|
+
}
|
|
53664
53790
|
// Purchases (the online services spec, part 3): asked of the page, which asks the store; the answer is heard on a
|
|
53665
53791
|
// later tick, as input.
|
|
53666
53792
|
case "purchaseBuy":
|
|
@@ -54352,6 +54478,17 @@ var Turn = class {
|
|
|
54352
54478
|
throw new Error(TOO_MANY_ASKS);
|
|
54353
54479
|
this.online = [...asked, ask];
|
|
54354
54480
|
}
|
|
54481
|
+
/** The player's settings as the page said them this tick, for a game that keeps them. A tick with a finger on the
|
|
54482
|
+
* screen that says nothing of them says a touch screen, as the page does on that tick: a playtest's track, which no
|
|
54483
|
+
* page plays, shows the game's touch controls to its look. */
|
|
54484
|
+
takePrefsInput() {
|
|
54485
|
+
if (this.prefs === void 0)
|
|
54486
|
+
return;
|
|
54487
|
+
if (this.input.prefs !== void 0)
|
|
54488
|
+
this.prefs = this.input.prefs;
|
|
54489
|
+
else if (!this.prefs.touch && (this.input.touches?.length ?? 0) > 0)
|
|
54490
|
+
this.prefs = { ...this.prefs, touch: true };
|
|
54491
|
+
}
|
|
54355
54492
|
/** What the page brought back from the game's server, and whether it is online, for a game that keeps online: each
|
|
54356
54493
|
* heard as an 'online' event. A game that never mentions online ignores it. */
|
|
54357
54494
|
takeOnlineInput() {
|
|
@@ -54409,7 +54546,7 @@ var Turn = class {
|
|
|
54409
54546
|
this.events.push(name);
|
|
54410
54547
|
}
|
|
54411
54548
|
scriptsState() {
|
|
54412
|
-
const { starting: _was, loops: _loops, mixer: _mixer, typing: _typing, ui: _ui, privacy: _privacy, ad: _ad, ads: _ads, online: _online, onlineState: _onlineState, nextCall: _nextCall, purchase: _purchase, purchases: _purchases, ...before2 } = this.scripts;
|
|
54549
|
+
const { starting: _was, loops: _loops, mixer: _mixer, typing: _typing, ui: _ui, privacy: _privacy, ad: _ad, ads: _ads, online: _online, onlineState: _onlineState, nextCall: _nextCall, purchase: _purchase, purchases: _purchases, prefs: _prefs, prefsAsk: _prefsAsk, ...before2 } = this.scripts;
|
|
54413
54550
|
return {
|
|
54414
54551
|
...before2,
|
|
54415
54552
|
fields: this.fields,
|
|
@@ -54441,6 +54578,8 @@ var Turn = class {
|
|
|
54441
54578
|
...this.online === void 0 || this.online.length === 0 ? {} : { online: this.online },
|
|
54442
54579
|
...this.onlineState === void 0 ? {} : { onlineState: this.onlineState },
|
|
54443
54580
|
...this.nextCall === void 0 ? {} : { nextCall: this.nextCall },
|
|
54581
|
+
...this.prefs === void 0 ? {} : { prefs: this.prefs },
|
|
54582
|
+
...this.prefsAsk === void 0 ? {} : { prefsAsk: this.prefsAsk },
|
|
54444
54583
|
...this.purchase === void 0 ? {} : { purchase: this.purchase },
|
|
54445
54584
|
...this.purchases === void 0 ? {} : { purchases: this.purchases },
|
|
54446
54585
|
music: this.music,
|
|
@@ -54483,12 +54622,33 @@ function uiTick(state, input) {
|
|
|
54483
54622
|
return { state, input };
|
|
54484
54623
|
const stepped = stepUi(ui, input, scripts.clock.real);
|
|
54485
54624
|
const next = stepped.ui === ui ? state : { ...state, scripts: { ...scripts, ui: stepped.ui } };
|
|
54486
|
-
|
|
54487
|
-
|
|
54488
|
-
|
|
54625
|
+
const touched = withTouched(input, touchedOf(stepped.ui, input, scripts.clock.real));
|
|
54626
|
+
if (!stepped.swallow || touched.buttons?.includes("click") !== true)
|
|
54627
|
+
return { state: next, input: touched };
|
|
54628
|
+
const { buttons, ...rest } = touched;
|
|
54489
54629
|
const kept = (buttons ?? []).filter((button) => button !== "click");
|
|
54490
54630
|
return { state: next, input: kept.length === 0 ? rest : { ...rest, buttons: kept } };
|
|
54491
54631
|
}
|
|
54632
|
+
var SIX2 = /* @__PURE__ */ new Set(["left", "right", "up", "down", "jump", "action"]);
|
|
54633
|
+
function withTouched(input, touched) {
|
|
54634
|
+
if (touched.held.length === 0 && touched.stick === null)
|
|
54635
|
+
return input;
|
|
54636
|
+
let out = input;
|
|
54637
|
+
const own2 = new Set(input.buttons ?? []);
|
|
54638
|
+
for (const button of touched.held) {
|
|
54639
|
+
if (SIX2.has(button))
|
|
54640
|
+
out = { ...out, [button]: true };
|
|
54641
|
+
else
|
|
54642
|
+
own2.add(button);
|
|
54643
|
+
}
|
|
54644
|
+
if (own2.size !== (input.buttons ?? []).length)
|
|
54645
|
+
out = { ...out, buttons: [...own2].sort() };
|
|
54646
|
+
if (touched.stick !== null) {
|
|
54647
|
+
const [first, ...rest] = input.sticks ?? [];
|
|
54648
|
+
out = { ...out, sticks: [{ rx: first?.rx ?? 0, ry: first?.ry ?? 0, lx: Math.round(touched.stick.x * 100) + 0, ly: Math.round(touched.stick.y * 100) + 0 }, ...rest] };
|
|
54649
|
+
}
|
|
54650
|
+
return out;
|
|
54651
|
+
}
|
|
54492
54652
|
function heardClicks(turn) {
|
|
54493
54653
|
if (turn.ui !== void 0 && turn.ui.clicked.length > 0)
|
|
54494
54654
|
turn.ui = { ...turn.ui, clicked: [] };
|
|
@@ -54514,6 +54674,7 @@ function beginScripts(state, input) {
|
|
|
54514
54674
|
function beginTurn(turn, scripts) {
|
|
54515
54675
|
turn.takeAdInput();
|
|
54516
54676
|
turn.takeOnlineInput();
|
|
54677
|
+
turn.takePrefsInput();
|
|
54517
54678
|
turn.takePurchaseInput();
|
|
54518
54679
|
turn.bursts = turn.bursts.filter((b) => turn.now - b.step < b.life * 60);
|
|
54519
54680
|
dueChange(turn);
|
|
@@ -54625,6 +54786,7 @@ function pausedScripts(state, input) {
|
|
|
54625
54786
|
function pausedTurn(turn, scripts) {
|
|
54626
54787
|
turn.takeAdInput();
|
|
54627
54788
|
turn.takeOnlineInput();
|
|
54789
|
+
turn.takePrefsInput();
|
|
54628
54790
|
turn.takePurchaseInput();
|
|
54629
54791
|
dueChange(turn);
|
|
54630
54792
|
turn.call(null, "whilePaused", [DT]);
|
|
@@ -57901,7 +58063,7 @@ function listGames(args = {}, fallback = LIBRARY_ROOT) {
|
|
|
57901
58063
|
// ../core/dist/surface/tools/inspect.js
|
|
57902
58064
|
function compile(args) {
|
|
57903
58065
|
try {
|
|
57904
|
-
const compiled = load(
|
|
58066
|
+
const compiled = load(parse2(args.source));
|
|
57905
58067
|
const first = compiled.levels[0];
|
|
57906
58068
|
if (first === void 0)
|
|
57907
58069
|
return fail("this spec declares no levels");
|
|
@@ -57914,7 +58076,7 @@ var isResult = (v) => typeof v === "object" && v !== null && "content" in v;
|
|
|
57914
58076
|
function validate(args) {
|
|
57915
58077
|
let ast;
|
|
57916
58078
|
try {
|
|
57917
|
-
ast =
|
|
58079
|
+
ast = parse2(args.source);
|
|
57918
58080
|
} catch (e) {
|
|
57919
58081
|
const err = e;
|
|
57920
58082
|
return ok({
|