@starweb-libs/menus 0.0.2 → 0.0.3

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 CHANGED
@@ -14,6 +14,12 @@ Composable canvas menu screens for browser games, built with TypeScript.
14
14
  | Module | Description |
15
15
  | ------ | ----------- |
16
16
  | `transition.js` | Plays the button sound, runs an optional callback, and flushes pointer + keyboard input between screens. |
17
+ | `title.js` | Title screen with START and SETTINGS buttons; takes a level count and an optional on-start callback. |
18
+ | `levels.js` | Level-select grid (up to 9) with a Back button; takes the level list and a `selectLevel(index)` callback. |
19
+ | `settings.js` | Settings screen with mute toggle and volume slider; takes the current slider state and returns the updated state alongside the frame. |
20
+ | `pause.js` | Pause overlay with Resume / Restart / Quit buttons; takes a `resetPlayState` callback. |
21
+ | `complete.js` | Level-complete overlay with Restart / Quit buttons, plus Next when a further level exists; takes the level index/count and `selectLevel` + `resetPlayState` callbacks. |
22
+ | `failed.js` | Level-failed overlay with Restart / Quit buttons; takes a `resetPlayState` callback. |
17
23
 
18
24
  ## Installation
19
25
  ```bash
@@ -0,0 +1,8 @@
1
+ import { CompleteFrame, CompleteMenuState } from "./types.mjs";
2
+ import { Audio } from "@starweb-libs/audio/audio.js";
3
+ //#region src/complete.d.ts
4
+ declare function handleCompleteFrame(w: number, h: number, a: Audio, levelIndex: number, levelCount: number, selectLevel: (i: number) => void, resetPlayState: () => void): CompleteFrame;
5
+ declare function renderCompleteFrame(ctx: CanvasRenderingContext2D, ui: CompleteMenuState | null, w: number, h: number): void;
6
+ //#endregion
7
+ export { handleCompleteFrame, renderCompleteFrame };
8
+ //# sourceMappingURL=complete.d.mts.map
@@ -0,0 +1,90 @@
1
+ import { transition } from "./transition.mjs";
2
+ import { getPointer } from "@starweb-libs/engine/input/pointer.js";
3
+ import { drawTitle, getLayout } from "@starweb-libs/ui/layout.js";
4
+ import { drawButton, getButtonState } from "@starweb-libs/ui/button.js";
5
+ //#region src/complete.ts
6
+ function handleCompleteFrame(w, h, a, levelIndex, levelCount, selectLevel, resetPlayState) {
7
+ const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);
8
+ const hasNext = levelIndex < levelCount - 1;
9
+ const btnCount = hasNext ? 3 : 2;
10
+ const firstY = cy - (btnH * btnCount + gap * (btnCount - 1)) / 2;
11
+ const titleY = firstY - btnH * .5 - gap * 2;
12
+ const restartBtn = {
13
+ x: cx - btnW / 2,
14
+ y: firstY,
15
+ w: btnW,
16
+ h: btnH,
17
+ label: "Restart"
18
+ };
19
+ const quitBtn = {
20
+ x: cx - btnW / 2,
21
+ y: firstY + (btnH + gap) * (btnCount - 1),
22
+ w: btnW,
23
+ h: btnH,
24
+ label: "Quit"
25
+ };
26
+ const restart = {
27
+ btn: restartBtn,
28
+ state: getButtonState(restartBtn, getPointer())
29
+ };
30
+ const quit = {
31
+ btn: quitBtn,
32
+ state: getButtonState(quitBtn, getPointer())
33
+ };
34
+ let next = null;
35
+ if (hasNext) {
36
+ const nextBtn = {
37
+ x: cx - btnW / 2,
38
+ y: firstY + (btnH + gap),
39
+ w: btnW,
40
+ h: btnH,
41
+ label: "Next"
42
+ };
43
+ next = {
44
+ btn: nextBtn,
45
+ state: getButtonState(nextBtn, getPointer())
46
+ };
47
+ }
48
+ let action = null;
49
+ if (next?.state.clicked) action = "next";
50
+ if (restart.state.clicked) action = "restart";
51
+ if (quit.state.clicked) action = "quit";
52
+ const ui = {
53
+ cx,
54
+ scale,
55
+ titleY,
56
+ restart,
57
+ next,
58
+ quit,
59
+ action
60
+ };
61
+ if (ui.action === "quit") return transition({
62
+ game: "menu-title",
63
+ ui: null
64
+ }, a);
65
+ if (ui.action === "next") return transition({
66
+ game: "level-playing",
67
+ ui: null
68
+ }, a, () => selectLevel(levelIndex + 1));
69
+ if (ui.action === "restart") return transition({
70
+ game: "level-playing",
71
+ ui: null
72
+ }, a, () => resetPlayState());
73
+ return {
74
+ game: "level-complete",
75
+ ui
76
+ };
77
+ }
78
+ function renderCompleteFrame(ctx, ui, w, h) {
79
+ if (!ui) return;
80
+ ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
81
+ ctx.fillRect(0, 0, w, h);
82
+ drawTitle(ctx, "Level Complete!", ui.cx, ui.titleY, ui.scale);
83
+ drawButton(ctx, ui.restart.btn, ui.restart.state);
84
+ if (ui.next) drawButton(ctx, ui.next.btn, ui.next.state);
85
+ drawButton(ctx, ui.quit.btn, ui.quit.state);
86
+ }
87
+ //#endregion
88
+ export { handleCompleteFrame, renderCompleteFrame };
89
+
90
+ //# sourceMappingURL=complete.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complete.mjs","names":[],"sources":["../src/complete.ts"],"sourcesContent":["import type { Button } from \"@starweb-libs/ui/types.js\";\nimport type { Audio } from \"@starweb-libs/audio/audio.js\";\nimport { getPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { getLayout, drawTitle } from \"@starweb-libs/ui/layout.js\";\nimport { getButtonState, drawButton } from \"@starweb-libs/ui/button.js\";\nimport type { CompleteAction, CompleteFrame, CompleteMenuState } from \"./types.ts\";\nimport { transition } from \"./transition.ts\";\n\nexport function handleCompleteFrame(\n w: number, h: number, a: Audio,\n levelIndex: number,\n levelCount: number,\n selectLevel: (i: number) => void, resetPlayState: () => void,\n): CompleteFrame {\n const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);\n const hasNext = levelIndex < levelCount - 1\n const btnCount = hasNext ? 3 : 2;\n const totalH = btnH * btnCount + gap * (btnCount - 1);\n const firstY = cy - totalH / 2;\n const titleY = firstY - btnH * 0.5 - gap * 2;\n\n const restartBtn: Button = { x: cx - btnW/2, y: firstY, w: btnW, h: btnH, label: \"Restart\" };\n const quitBtn: Button = { x: cx - btnW/2, y: firstY + (btnH + gap) * (btnCount - 1), w: btnW, h: btnH, label: \"Quit\" };\n\n const restart = { btn: restartBtn, state: getButtonState(restartBtn, getPointer()) };\n const quit = { btn: quitBtn, state: getButtonState(quitBtn, getPointer()) };\n\n let next: CompleteMenuState[\"next\"] = null;\n if (hasNext) {\n const nextBtn: Button = { x: cx - btnW/2, y: firstY + (btnH + gap), w: btnW, h: btnH, label: \"Next\" };\n next = { btn: nextBtn, state: getButtonState(nextBtn, getPointer()) };\n }\n\n let action: CompleteAction = null;\n if (next?.state.clicked) action = \"next\";\n if (restart.state.clicked) action = \"restart\";\n if (quit.state.clicked) action = \"quit\";\n\n const ui = { cx, scale, titleY, restart, next, quit, action };\n\n if (ui.action === \"quit\") return transition({ game: \"menu-title\", ui: null }, a);\n if (ui.action === \"next\") return transition(\n { game: \"level-playing\", ui: null },\n a,\n () => selectLevel(levelIndex + 1)\n );\n if (ui.action === \"restart\") return transition(\n { game: \"level-playing\", ui: null },\n a,\n () => resetPlayState()\n );\n return { game: \"level-complete\", ui };\n}\n\nexport function renderCompleteFrame(\n ctx: CanvasRenderingContext2D,\n ui: CompleteMenuState | null,\n w: number, h: number,\n): void {\n if (!ui) return;\n\n ctx.fillStyle = \"rgba(0, 0, 0, 0.6)\";\n ctx.fillRect(0, 0, w, h);\n drawTitle(ctx, \"Level Complete!\", ui.cx, ui.titleY, ui.scale);\n drawButton(ctx, ui.restart.btn, ui.restart.state);\n if (ui.next) drawButton(ctx, ui.next.btn, ui.next.state);\n drawButton(ctx, ui.quit.btn, ui.quit.state);\n}\n\n\n"],"mappings":";;;;;AAQA,SAAgB,oBACd,GAAW,GAAW,GACtB,YACA,YACA,aAAkC,gBACnB;CACf,MAAM,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,SAAS,UAAU,GAAG,CAAC;CACzD,MAAM,UAAU,aAAa,aAAa;CAC1C,MAAM,WAAW,UAAU,IAAI;CAE/B,MAAM,SAAS,MADA,OAAO,WAAW,OAAO,WAAW,MACtB;CAC7B,MAAM,SAAS,SAAS,OAAO,KAAM,MAAM;CAE3C,MAAM,aAAqB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG;EAAwC,GAAG;EAAM,GAAG;EAAM,OAAO;CAAU;CAC3H,MAAM,UAAqB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,UAAU,OAAO,QAAQ,WAAW;EAAI,GAAG;EAAM,GAAG;EAAM,OAAO;CAAU;CAE3H,MAAM,UAAU;EAAE,KAAK;EAAY,OAAO,eAAe,YAAY,WAAW,CAAC;CAAE;CACnF,MAAM,OAAU;EAAE,KAAK;EAAY,OAAO,eAAe,SAAY,WAAW,CAAC;CAAE;CAEnF,IAAI,OAAkC;CACtC,IAAI,SAAS;EACX,MAAM,UAAkB;GAAE,GAAG,KAAK,OAAK;GAAG,GAAG,UAAU,OAAO;GAAM,GAAG;GAAM,GAAG;GAAM,OAAO;EAAO;EACpG,OAAO;GAAE,KAAK;GAAS,OAAO,eAAe,SAAS,WAAW,CAAC;EAAE;CACtE;CAEA,IAAI,SAAyB;CAC7B,IAAI,MAAM,MAAM,SAAW,SAAS;CACpC,IAAI,QAAQ,MAAM,SAAS,SAAS;CACpC,IAAI,KAAK,MAAM,SAAY,SAAS;CAEpC,MAAM,KAAK;EAAE;EAAI;EAAO;EAAQ;EAAS;EAAM;EAAM;CAAO;CAE5D,IAAI,GAAG,WAAW,QAAW,OAAO,WAAW;EAAE,MAAM;EAAc,IAAI;CAAK,GAAG,CAAC;CAClF,IAAI,GAAG,WAAW,QAAW,OAAO,WAClC;EAAE,MAAM;EAAiB,IAAI;CAAK,GAClC,SACM,YAAY,aAAa,CAAC,CAClC;CACA,IAAI,GAAG,WAAW,WAAW,OAAO,WAClC;EAAE,MAAM;EAAiB,IAAI;CAAK,GAClC,SACM,eAAe,CACvB;CACA,OAAO;EAAE,MAAM;EAAkB;CAAG;AACtC;AAEA,SAAgB,oBACd,KACA,IACA,GAAW,GACL;CACN,IAAI,CAAC,IAAI;CAET,IAAI,YAAY;CAChB,IAAI,SAAS,GAAG,GAAG,GAAG,CAAC;CACvB,UAAU,KAAK,mBAAmB,GAAG,IAAI,GAAG,QAAQ,GAAG,KAAK;CAC5D,WAAW,KAAK,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK;CAChD,IAAI,GAAG,MAAM,WAAW,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK;CACvD,WAAW,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK;AAC5C"}
@@ -0,0 +1,8 @@
1
+ import { FailedFrame, FailedMenuState } from "./types.mjs";
2
+ import { Audio } from "@starweb-libs/audio/audio.js";
3
+ //#region src/failed.d.ts
4
+ declare function handleFailedFrame(w: number, h: number, a: Audio, resetPlayState: () => void): FailedFrame;
5
+ declare function renderFailedFrame(ctx: CanvasRenderingContext2D, ui: FailedMenuState | null, w: number, h: number): void;
6
+ //#endregion
7
+ export { handleFailedFrame, renderFailedFrame };
8
+ //# sourceMappingURL=failed.d.mts.map
@@ -0,0 +1,67 @@
1
+ import { transition } from "./transition.mjs";
2
+ import { getPointer } from "@starweb-libs/engine/input/pointer.js";
3
+ import { drawTitle, getLayout } from "@starweb-libs/ui/layout.js";
4
+ import { drawButton, getButtonState } from "@starweb-libs/ui/button.js";
5
+ //#region src/failed.ts
6
+ function handleFailedFrame(w, h, a, resetPlayState) {
7
+ const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);
8
+ const firstY = cy - (btnH * 2 + gap) / 2;
9
+ const titleY = firstY - btnH * .5 - gap * 2;
10
+ const restartBtn = {
11
+ x: cx - btnW / 2,
12
+ y: firstY,
13
+ w: btnW,
14
+ h: btnH,
15
+ label: "Restart"
16
+ };
17
+ const quitBtn = {
18
+ x: cx - btnW / 2,
19
+ y: firstY + btnH + gap,
20
+ w: btnW,
21
+ h: btnH,
22
+ label: "Quit"
23
+ };
24
+ const restart = {
25
+ btn: restartBtn,
26
+ state: getButtonState(restartBtn, getPointer())
27
+ };
28
+ const quit = {
29
+ btn: quitBtn,
30
+ state: getButtonState(quitBtn, getPointer())
31
+ };
32
+ let action = null;
33
+ if (restart.state.clicked) action = "restart";
34
+ if (quit.state.clicked) action = "quit";
35
+ const ui = {
36
+ cx,
37
+ scale,
38
+ titleY,
39
+ restart,
40
+ quit,
41
+ action
42
+ };
43
+ if (ui.action === "quit") return transition({
44
+ game: "menu-title",
45
+ ui: null
46
+ }, a);
47
+ if (ui.action === "restart") return transition({
48
+ game: "level-playing",
49
+ ui: null
50
+ }, a, () => resetPlayState());
51
+ return {
52
+ game: "level-failed",
53
+ ui
54
+ };
55
+ }
56
+ function renderFailedFrame(ctx, ui, w, h) {
57
+ if (!ui) return;
58
+ ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
59
+ ctx.fillRect(0, 0, w, h);
60
+ drawTitle(ctx, "Level Failed!", ui.cx, ui.titleY, ui.scale);
61
+ drawButton(ctx, ui.restart.btn, ui.restart.state);
62
+ drawButton(ctx, ui.quit.btn, ui.quit.state);
63
+ }
64
+ //#endregion
65
+ export { handleFailedFrame, renderFailedFrame };
66
+
67
+ //# sourceMappingURL=failed.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failed.mjs","names":[],"sources":["../src/failed.ts"],"sourcesContent":["import type { Button } from \"@starweb-libs/ui/types.js\";\nimport type { Audio } from \"@starweb-libs/audio/audio.js\";\nimport { getPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { getLayout, drawTitle } from \"@starweb-libs/ui/layout.js\";\nimport { getButtonState, drawButton } from \"@starweb-libs/ui/button.js\";\nimport type { FailedAction, FailedFrame, FailedMenuState } from \"./types.ts\";\nimport { transition } from \"./transition.ts\";\n\nexport function handleFailedFrame(\n w: number, h: number, a: Audio,\n resetPlayState: () => void\n): FailedFrame {\n const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);\n const totalH = btnH * 2 + gap;\n const firstY = cy - totalH / 2;\n const titleY = firstY - btnH * 0.5 - gap * 2;\n\n const restartBtn: Button = { x: cx - btnW/2, y: firstY, w: btnW, h: btnH, label: \"Restart\" };\n const quitBtn: Button = { x: cx - btnW/2, y: firstY + btnH + gap, w: btnW, h: btnH, label: \"Quit\" };\n\n const restart = { btn: restartBtn, state: getButtonState(restartBtn, getPointer()) };\n const quit = { btn: quitBtn, state: getButtonState(quitBtn, getPointer()) };\n\n let action: FailedAction = null;\n if (restart.state.clicked) action = \"restart\";\n if (quit.state.clicked) action = \"quit\";\n\n const ui = { cx, scale, titleY, restart, quit, action };\n\n if (ui.action === \"quit\") return transition({ game: \"menu-title\", ui: null }, a);\n if (ui.action === \"restart\") return transition(\n { game: \"level-playing\", ui: null },\n a,\n () => resetPlayState()\n );\n return { game: \"level-failed\", ui };\n}\n\nexport function renderFailedFrame(\n ctx: CanvasRenderingContext2D,\n ui: FailedMenuState | null,\n w: number, h: number\n): void {\n if (!ui) return;\n\n ctx.fillStyle = \"rgba(0, 0, 0, 0.6)\";\n ctx.fillRect(0, 0, w, h);\n drawTitle(ctx, \"Level Failed!\", ui.cx, ui.titleY, ui.scale);\n drawButton(ctx, ui.restart.btn, ui.restart.state);\n drawButton(ctx, ui.quit.btn, ui.quit.state);\n}\n"],"mappings":";;;;;AAQA,SAAgB,kBACd,GAAW,GAAW,GACtB,gBACa;CACb,MAAM,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,SAAS,UAAU,GAAG,CAAC;CAEzD,MAAM,SAAU,MADA,OAAO,IAAI,OACG;CAC9B,MAAM,SAAS,SAAS,OAAO,KAAM,MAAM;CAE3C,MAAM,aAAqB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG;EAAwC,GAAG;EAAM,GAAG;EAAM,OAAO;CAAU;CAC3H,MAAM,UAAkB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,SAAS,OAAO;EAAK,GAAG;EAAM,GAAG;EAAM,OAAO;CAAO;CAElG,MAAM,UAAU;EAAE,KAAK;EAAY,OAAO,eAAe,YAAY,WAAW,CAAC;CAAE;CACnF,MAAM,OAAU;EAAE,KAAK;EAAY,OAAO,eAAe,SAAS,WAAW,CAAC;CAAK;CAEnF,IAAI,SAAuB;CAC3B,IAAI,QAAQ,MAAM,SAAS,SAAS;CACpC,IAAI,KAAK,MAAM,SAAY,SAAS;CAEpC,MAAM,KAAK;EAAE;EAAI;EAAO;EAAQ;EAAS;EAAM;CAAO;CAEtD,IAAI,GAAG,WAAW,QAAW,OAAO,WAAW;EAAE,MAAM;EAAc,IAAI;CAAK,GAAG,CAAC;CAClF,IAAI,GAAG,WAAW,WAAW,OAAO,WAClC;EAAE,MAAM;EAAiB,IAAI;CAAK,GAClC,SACM,eAAe,CACvB;CACA,OAAO;EAAE,MAAM;EAAgB;CAAG;AACpC;AAEA,SAAgB,kBACd,KACA,IACA,GAAW,GACL;CACN,IAAI,CAAC,IAAI;CAET,IAAI,YAAY;CAChB,IAAI,SAAS,GAAG,GAAG,GAAG,CAAC;CACvB,UAAU,KAAK,iBAAiB,GAAG,IAAI,GAAG,QAAQ,GAAG,KAAK;CAC1D,WAAW,KAAK,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK;CAChD,WAAW,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK;AAC5C"}
@@ -0,0 +1,10 @@
1
+ import { LevelSelectState, LevelsFrame } from "./types.mjs";
2
+ import { Audio } from "@starweb-libs/audio/audio.js";
3
+ //#region src/levels.d.ts
4
+ declare function handleLevelFrame(w: number, h: number, a: Audio, levels: {
5
+ name?: string;
6
+ }[], selectLevel: (i: number) => void): LevelsFrame;
7
+ declare function renderLevelFrame(ctx: CanvasRenderingContext2D, ui: LevelSelectState | null): void;
8
+ //#endregion
9
+ export { handleLevelFrame, renderLevelFrame };
10
+ //# sourceMappingURL=levels.d.mts.map
@@ -0,0 +1,76 @@
1
+ import { transition } from "./transition.mjs";
2
+ import { getPointer } from "@starweb-libs/engine/input/pointer.js";
3
+ import { drawTitle, getLayout } from "@starweb-libs/ui/layout.js";
4
+ import { drawButton, getButtonState } from "@starweb-libs/ui/button.js";
5
+ //#region src/levels.ts
6
+ function handleLevelFrame(w, h, a, levels, selectLevel) {
7
+ const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);
8
+ const titleY = scale * .15;
9
+ const cols = 3;
10
+ const cellW = btnH * 3;
11
+ const gridW = cellW * cols + gap * (cols - 1);
12
+ const gridH = btnH * 3 + gap * 2;
13
+ const gridX = cx - gridW / 2;
14
+ const gridY = cy - gridH / 2;
15
+ let clickedIndex = null;
16
+ const levelEntries = Array.from({ length: Math.min(levels.length, 9) }, (_, i) => {
17
+ const col = i % cols;
18
+ const row = Math.floor(i / cols);
19
+ const btn = {
20
+ x: gridX + col * (cellW + gap),
21
+ y: gridY + row * (btnH + gap),
22
+ w: cellW,
23
+ h: btnH,
24
+ label: levels[i]?.name ?? `Level ${i + 1}`
25
+ };
26
+ const state = getButtonState(btn, getPointer());
27
+ if (state.clicked) clickedIndex = i;
28
+ return {
29
+ btn,
30
+ state
31
+ };
32
+ });
33
+ const backBtn = {
34
+ x: cx - btnW / 2,
35
+ y: h - btnH - gap * 2,
36
+ w: btnW,
37
+ h: btnH,
38
+ label: "Back"
39
+ };
40
+ const ui = {
41
+ cx,
42
+ scale,
43
+ titleY,
44
+ levels: levelEntries,
45
+ back: {
46
+ btn: backBtn,
47
+ state: getButtonState(backBtn, getPointer())
48
+ },
49
+ clickedIndex
50
+ };
51
+ if (ui.back.state.clicked) return transition({
52
+ game: "menu-title",
53
+ ui: null
54
+ }, a);
55
+ if (ui.clickedIndex !== null) {
56
+ const index = ui.clickedIndex;
57
+ return transition({
58
+ game: "level-playing",
59
+ ui: null
60
+ }, a, () => selectLevel(index));
61
+ }
62
+ return {
63
+ game: "menu-levels",
64
+ ui
65
+ };
66
+ }
67
+ function renderLevelFrame(ctx, ui) {
68
+ if (!ui) return;
69
+ drawTitle(ctx, "Select Level", ui.cx, ui.titleY, ui.scale);
70
+ for (const entry of ui.levels) drawButton(ctx, entry.btn, entry.state);
71
+ drawButton(ctx, ui.back.btn, ui.back.state);
72
+ }
73
+ //#endregion
74
+ export { handleLevelFrame, renderLevelFrame };
75
+
76
+ //# sourceMappingURL=levels.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"levels.mjs","names":[],"sources":["../src/levels.ts"],"sourcesContent":["import type { Button } from \"@starweb-libs/ui/types.js\";\nimport type { Audio } from \"@starweb-libs/audio/audio.js\";\nimport { getPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { getLayout, drawTitle } from \"@starweb-libs/ui/layout.js\";\nimport { getButtonState, drawButton } from \"@starweb-libs/ui/button.js\";\nimport type { LevelSelectState, LevelsFrame } from \"./types.ts\";\nimport { transition } from \"./transition.ts\";\n\nexport function handleLevelFrame(\n w: number, h: number, a: Audio,\n levels: { name?: string }[],\n selectLevel: (i: number) => void,\n): LevelsFrame {\n const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);\n const titleY = scale * 0.15;\n\n const cols = 3;\n const cellW = btnH * 3;\n const gridW = cellW * cols + gap * (cols - 1);\n const gridH = btnH * 3 + gap * 2;\n const gridX = cx - gridW / 2;\n const gridY = cy - gridH / 2;\n\n let clickedIndex: number | null = null;\n const levelEntries = Array.from({ length: Math.min(levels.length, 9) }, (_, i) => {\n const col = i % cols;\n const row = Math.floor(i / cols);\n const btn: Button = {\n x: gridX + col * (cellW + gap),\n y: gridY + row * (btnH + gap),\n w: cellW, h: btnH,\n label: levels[i]?.name ?? `Level ${i + 1}`,\n };\n const state = getButtonState(btn, getPointer());\n if (state.clicked) clickedIndex = i;\n return { btn, state };\n });\n\n const backBtn: Button = { x: cx - btnW/2, y: h - btnH - gap*2, w: btnW, h: btnH, label: \"Back\" };\n const back = { btn: backBtn, state: getButtonState(backBtn, getPointer()) };\n\n const ui = { cx, scale, titleY, levels: levelEntries, back, clickedIndex };\n\n if (ui.back.state.clicked) return transition({ game: \"menu-title\", ui: null }, a);\n if (ui.clickedIndex !== null) {\n const index = ui.clickedIndex;\n return transition(\n { game: \"level-playing\", ui: null },\n a,\n () => selectLevel(index)\n );\n }\n return { game: \"menu-levels\", ui };\n}\n\nexport function renderLevelFrame(\n ctx: CanvasRenderingContext2D,\n ui: LevelSelectState | null\n): void {\n if (!ui) return;\n drawTitle(ctx, \"Select Level\", ui.cx, ui.titleY, ui.scale);\n for (const entry of ui.levels) drawButton(ctx, entry.btn, entry.state);\n drawButton(ctx, ui.back.btn, ui.back.state);\n}\n"],"mappings":";;;;;AAQA,SAAgB,iBACd,GAAW,GAAW,GACtB,QACA,aACa;CACb,MAAM,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,SAAS,UAAU,GAAG,CAAC;CACzD,MAAM,SAAS,QAAQ;CAEvB,MAAM,OAAQ;CACd,MAAM,QAAQ,OAAO;CACrB,MAAM,QAAQ,QAAQ,OAAO,OAAO,OAAO;CAC3C,MAAM,QAAQ,OAAO,IAAI,MAAM;CAC/B,MAAM,QAAQ,KAAK,QAAQ;CAC3B,MAAM,QAAQ,KAAK,QAAQ;CAE3B,IAAI,eAA8B;CAClC,MAAM,eAAe,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,OAAO,QAAQ,CAAC,EAAE,IAAI,GAAG,MAAM;EAChF,MAAM,MAAM,IAAI;EAChB,MAAM,MAAM,KAAK,MAAM,IAAI,IAAI;EAC/B,MAAM,MAAc;GAClB,GAAG,QAAQ,OAAO,QAAQ;GAC1B,GAAG,QAAQ,OAAO,OAAO;GACzB,GAAG;GAAO,GAAG;GACb,OAAO,OAAO,EAAE,EAAE,QAAQ,SAAS,IAAI;EACzC;EACA,MAAM,QAAQ,eAAe,KAAK,WAAW,CAAC;EAC9C,IAAI,MAAM,SAAS,eAAe;EAClC,OAAO;GAAE;GAAK;EAAM;CACtB,CAAC;CAED,MAAM,UAAkB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,IAAI,OAAO,MAAI;EAAG,GAAG;EAAM,GAAG;EAAM,OAAO;CAAO;CAG/F,MAAM,KAAK;EAAE;EAAI;EAAO;EAAQ,QAAQ;EAAc,MAAA;GAFvC,KAAK;GAAS,OAAO,eAAe,SAAS,WAAW,CAAC;EAEf;EAAG;CAAa;CAEzE,IAAI,GAAG,KAAK,MAAM,SAAS,OAAO,WAAW;EAAE,MAAM;EAAc,IAAI;CAAK,GAAG,CAAC;CAChF,IAAI,GAAG,iBAAiB,MAAM;EAC5B,MAAM,QAAQ,GAAG;EACjB,OAAO,WACL;GAAE,MAAM;GAAiB,IAAI;EAAK,GAClC,SACM,YAAY,KAAK,CACzB;CACF;CACA,OAAO;EAAE,MAAM;EAAe;CAAG;AACnC;AAEA,SAAgB,iBACd,KACA,IACM;CACN,IAAI,CAAC,IAAI;CACT,UAAU,KAAK,gBAAgB,GAAG,IAAI,GAAG,QAAQ,GAAG,KAAK;CACzD,KAAK,MAAM,SAAS,GAAG,QAAQ,WAAW,KAAK,MAAM,KAAK,MAAM,KAAK;CACrE,WAAW,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK;AAC5C"}
@@ -0,0 +1,8 @@
1
+ import { PauseFrame, PauseMenuState } from "./types.mjs";
2
+ import { Audio } from "@starweb-libs/audio/audio.js";
3
+ //#region src/pause.d.ts
4
+ declare function handlePauseFrame(w: number, h: number, a: Audio, resetPlayState: () => void): PauseFrame;
5
+ declare function renderPauseFrame(ctx: CanvasRenderingContext2D, ui: PauseMenuState | null, w: number, h: number): void;
6
+ //#endregion
7
+ export { handlePauseFrame, renderPauseFrame };
8
+ //# sourceMappingURL=pause.d.mts.map
package/dist/pause.mjs ADDED
@@ -0,0 +1,85 @@
1
+ import { transition } from "./transition.mjs";
2
+ import { getPointer } from "@starweb-libs/engine/input/pointer.js";
3
+ import { drawTitle, getLayout } from "@starweb-libs/ui/layout.js";
4
+ import { drawButton, getButtonState } from "@starweb-libs/ui/button.js";
5
+ //#region src/pause.ts
6
+ function handlePauseFrame(w, h, a, resetPlayState) {
7
+ const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);
8
+ const firstY = cy - (btnH * 3 + gap * 2) / 2;
9
+ const titleY = firstY - btnH * .5 - gap * 2;
10
+ const resumeBtn = {
11
+ x: cx - btnW / 2,
12
+ y: firstY,
13
+ w: btnW,
14
+ h: btnH,
15
+ label: "Resume"
16
+ };
17
+ const restartBtn = {
18
+ x: cx - btnW / 2,
19
+ y: firstY + (btnH + gap),
20
+ w: btnW,
21
+ h: btnH,
22
+ label: "Restart"
23
+ };
24
+ const quitBtn = {
25
+ x: cx - btnW / 2,
26
+ y: firstY + (btnH + gap) * 2,
27
+ w: btnW,
28
+ h: btnH,
29
+ label: "Quit"
30
+ };
31
+ const resume = {
32
+ btn: resumeBtn,
33
+ state: getButtonState(resumeBtn, getPointer())
34
+ };
35
+ const restart = {
36
+ btn: restartBtn,
37
+ state: getButtonState(restartBtn, getPointer())
38
+ };
39
+ const quit = {
40
+ btn: quitBtn,
41
+ state: getButtonState(quitBtn, getPointer())
42
+ };
43
+ let action = null;
44
+ if (resume.state.clicked) action = "resume";
45
+ if (restart.state.clicked) action = "restart";
46
+ if (quit.state.clicked) action = "quit";
47
+ const ui = {
48
+ cx,
49
+ scale,
50
+ titleY,
51
+ resume,
52
+ restart,
53
+ quit,
54
+ action
55
+ };
56
+ if (ui.action === "resume") return transition({
57
+ game: "level-playing",
58
+ ui: null
59
+ }, a);
60
+ if (ui.action === "quit") return transition({
61
+ game: "menu-title",
62
+ ui: null
63
+ }, a);
64
+ if (ui.action === "restart") return transition({
65
+ game: "level-playing",
66
+ ui: null
67
+ }, a, () => resetPlayState());
68
+ return {
69
+ game: "level-paused",
70
+ ui
71
+ };
72
+ }
73
+ function renderPauseFrame(ctx, ui, w, h) {
74
+ if (!ui) return;
75
+ ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
76
+ ctx.fillRect(0, 0, w, h);
77
+ drawTitle(ctx, "Paused", ui.cx, ui.titleY, ui.scale);
78
+ drawButton(ctx, ui.resume.btn, ui.resume.state);
79
+ drawButton(ctx, ui.restart.btn, ui.restart.state);
80
+ drawButton(ctx, ui.quit.btn, ui.quit.state);
81
+ }
82
+ //#endregion
83
+ export { handlePauseFrame, renderPauseFrame };
84
+
85
+ //# sourceMappingURL=pause.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pause.mjs","names":[],"sources":["../src/pause.ts"],"sourcesContent":["import type { Audio } from \"@starweb-libs/audio/audio.js\";\nimport { getPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { getLayout, drawTitle } from \"@starweb-libs/ui/layout.js\";\nimport { getButtonState, drawButton } from \"@starweb-libs/ui/button.js\";\nimport type { PauseAction, PauseFrame, PauseMenuState } from \"./types.ts\";\nimport { transition } from \"./transition.ts\";\n\nexport function handlePauseFrame(\n w: number, h: number, a: Audio,\n resetPlayState: () => void\n): PauseFrame {\n const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);\n const totalH = btnH * 3 + gap * 2;\n const firstY = cy - totalH / 2;\n const titleY = firstY - btnH * 0.5 - gap * 2;\n\n const resumeBtn = { x: cx - btnW/2, y: firstY, w: btnW, h: btnH, label: \"Resume\" };\n const restartBtn = { x: cx - btnW/2, y: firstY + (btnH + gap), w: btnW, h: btnH, label: \"Restart\" };\n const quitBtn = { x: cx - btnW/2, y: firstY + (btnH + gap) * 2, w: btnW, h: btnH, label: \"Quit\" };\n\n const resume = { btn: resumeBtn, state: getButtonState(resumeBtn, getPointer()) };\n const restart = { btn: restartBtn, state: getButtonState(restartBtn, getPointer()) };\n const quit = { btn: quitBtn, state: getButtonState(quitBtn, getPointer()) };\n\n let action: PauseAction = null;\n if (resume.state.clicked) action = \"resume\";\n if (restart.state.clicked) action = \"restart\";\n if (quit.state.clicked) action = \"quit\";\n\n const ui = { cx, scale, titleY, resume, restart, quit, action };\n\n if (ui.action === \"resume\") return transition({ game: \"level-playing\", ui: null }, a);\n if (ui.action === \"quit\") return transition({ game: \"menu-title\", ui: null }, a);\n if (ui.action === \"restart\") return transition(\n { game: \"level-playing\", ui: null },\n a,\n () => resetPlayState()\n );\n return { game: \"level-paused\", ui };\n}\n\nexport function renderPauseFrame(\n ctx: CanvasRenderingContext2D,\n ui: PauseMenuState | null,\n w: number, h: number,\n): void {\n if (!ui) return;\n ctx.fillStyle = \"rgba(0, 0, 0, 0.6)\";\n ctx.fillRect(0, 0, w, h);\n drawTitle(ctx, \"Paused\", ui.cx, ui.titleY, ui.scale);\n drawButton(ctx, ui.resume.btn, ui.resume.state);\n drawButton(ctx, ui.restart.btn, ui.restart.state);\n drawButton(ctx, ui.quit.btn, ui.quit.state);\n}\n"],"mappings":";;;;;AAOA,SAAgB,iBACd,GAAW,GAAW,GACtB,gBACY;CACZ,MAAM,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,SAAS,UAAU,GAAG,CAAC;CAEzD,MAAM,SAAS,MADA,OAAO,IAAI,MAAM,KACH;CAC7B,MAAM,SAAS,SAAS,OAAO,KAAM,MAAM;CAE3C,MAAM,YAAa;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG;EAA2B,GAAG;EAAM,GAAG;EAAM,OAAO;CAAU;CACtG,MAAM,aAAa;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,UAAU,OAAO;EAAU,GAAG;EAAM,GAAG;EAAM,OAAO;CAAU;CACtG,MAAM,UAAa;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,UAAU,OAAO,OAAO;EAAG,GAAG;EAAM,GAAG;EAAM,OAAO;CAAU;CAEtG,MAAM,SAAU;EAAE,KAAK;EAAY,OAAO,eAAe,WAAY,WAAW,CAAC;CAAE;CACnF,MAAM,UAAU;EAAE,KAAK;EAAY,OAAO,eAAe,YAAY,WAAW,CAAC;CAAE;CACnF,MAAM,OAAU;EAAE,KAAK;EAAY,OAAO,eAAe,SAAY,WAAW,CAAC;CAAE;CAEnF,IAAI,SAAsB;CAC1B,IAAI,OAAO,MAAM,SAAU,SAAS;CACpC,IAAI,QAAQ,MAAM,SAAS,SAAS;CACpC,IAAI,KAAK,MAAM,SAAY,SAAS;CAEpC,MAAM,KAAK;EAAE;EAAI;EAAO;EAAQ;EAAQ;EAAS;EAAM;CAAO;CAE9D,IAAI,GAAG,WAAW,UAAW,OAAO,WAAW;EAAE,MAAM;EAAiB,IAAI;CAAK,GAAG,CAAC;CACrF,IAAI,GAAG,WAAW,QAAW,OAAO,WAAW;EAAE,MAAM;EAAiB,IAAI;CAAK,GAAG,CAAC;CACrF,IAAI,GAAG,WAAW,WAAW,OAAO,WAClC;EAAE,MAAM;EAAiB,IAAI;CAAK,GAClC,SACM,eAAe,CACvB;CACA,OAAO;EAAE,MAAM;EAAgB;CAAG;AACpC;AAEA,SAAgB,iBACd,KACA,IACA,GAAW,GACL;CACN,IAAI,CAAC,IAAI;CACT,IAAI,YAAY;CAChB,IAAI,SAAS,GAAG,GAAG,GAAG,CAAC;CACvB,UAAU,KAAK,UAAU,GAAG,IAAI,GAAG,QAAQ,GAAG,KAAK;CACnD,WAAW,KAAK,GAAG,OAAO,KAAM,GAAG,OAAO,KAAK;CAC/C,WAAW,KAAK,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK;CAChD,WAAW,KAAK,GAAG,KAAK,KAAQ,GAAG,KAAK,KAAK;AAC/C"}
@@ -0,0 +1,12 @@
1
+ import { SettingsFrame, SettingsMenuState } from "./types.mjs";
2
+ import { Audio } from "@starweb-libs/audio/audio.js";
3
+ import { SliderState } from "@starweb-libs/ui/types.js";
4
+ //#region src/settings.d.ts
5
+ declare function handleSettingsFrame(w: number, h: number, volState: SliderState, a: Audio): {
6
+ frame: SettingsFrame;
7
+ volState: SliderState;
8
+ };
9
+ declare function renderSettingsFrame(ctx: CanvasRenderingContext2D, ui: SettingsMenuState | null): void;
10
+ //#endregion
11
+ export { handleSettingsFrame, renderSettingsFrame };
12
+ //# sourceMappingURL=settings.d.mts.map
@@ -0,0 +1,75 @@
1
+ import { transition } from "./transition.mjs";
2
+ import { getPointer } from "@starweb-libs/engine/input/pointer.js";
3
+ import { drawTitle, getLayout } from "@starweb-libs/ui/layout.js";
4
+ import { drawButton, getButtonState } from "@starweb-libs/ui/button.js";
5
+ import { drawSlider, updateSlider } from "@starweb-libs/ui/slider.js";
6
+ //#region src/settings.ts
7
+ function handleSettingsFrame(w, h, volState, a) {
8
+ const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);
9
+ const titleY = scale * .15;
10
+ const muteBtn = {
11
+ x: cx - btnW / 2,
12
+ y: cy - btnH / 2,
13
+ w: btnW,
14
+ h: btnH,
15
+ label: a.muted ? "Unmute" : "Mute"
16
+ };
17
+ const backBtn = {
18
+ x: cx - btnW / 2,
19
+ y: h - btnH - gap * 2,
20
+ w: btnW,
21
+ h: btnH,
22
+ label: "Back"
23
+ };
24
+ const volSlider = {
25
+ x: cx - btnW / 2,
26
+ y: cy + btnH / 2 + gap,
27
+ w: btnW,
28
+ h: btnH,
29
+ label: "Volume"
30
+ };
31
+ const mute = {
32
+ btn: muteBtn,
33
+ state: getButtonState(muteBtn, getPointer())
34
+ };
35
+ const back = {
36
+ btn: backBtn,
37
+ state: getButtonState(backBtn, getPointer())
38
+ };
39
+ if (mute.state.clicked) a.setMuted(!a.muted);
40
+ volState = updateSlider(volSlider, volState, getPointer());
41
+ a.setVolume(volState.value);
42
+ const ui = {
43
+ cx,
44
+ scale,
45
+ titleY,
46
+ mute,
47
+ back,
48
+ vol: {
49
+ slider: volSlider,
50
+ state: volState
51
+ },
52
+ muted: a.muted
53
+ };
54
+ return {
55
+ frame: ui.back.state.clicked ? transition({
56
+ game: "menu-title",
57
+ ui: null
58
+ }, a) : {
59
+ game: "menu-settings",
60
+ ui
61
+ },
62
+ volState
63
+ };
64
+ }
65
+ function renderSettingsFrame(ctx, ui) {
66
+ if (!ui) return;
67
+ drawTitle(ctx, "Settings", ui.cx, ui.titleY, ui.scale);
68
+ drawButton(ctx, ui.mute.btn, ui.mute.state);
69
+ drawSlider(ctx, ui.vol.slider, ui.vol.state.value);
70
+ drawButton(ctx, ui.back.btn, ui.back.state);
71
+ }
72
+ //#endregion
73
+ export { handleSettingsFrame, renderSettingsFrame };
74
+
75
+ //# sourceMappingURL=settings.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.mjs","names":[],"sources":["../src/settings.ts"],"sourcesContent":["import type { Button, Slider, SliderState } from \"@starweb-libs/ui/types.js\";\nimport type { Audio } from \"@starweb-libs/audio/audio.js\";\nimport { getPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { getLayout, drawTitle } from \"@starweb-libs/ui/layout.js\";\nimport { getButtonState, drawButton } from \"@starweb-libs/ui/button.js\";\nimport { updateSlider, drawSlider } from \"@starweb-libs/ui/slider.js\";\nimport type { SettingsFrame, SettingsMenuState } from \"./types.ts\";\nimport { transition } from \"./transition.ts\";\n\nexport function handleSettingsFrame(\n w: number, h: number, volState: SliderState, a: Audio,\n): { frame: SettingsFrame; volState: SliderState } {\n const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);\n const titleY = scale * 0.15;\n\n const muteBtn: Button = { x: cx - btnW/2, y: cy - btnH/2, w: btnW, h: btnH, label: a.muted ? \"Unmute\" : \"Mute\" };\n const backBtn: Button = { x: cx - btnW/2, y: h - btnH - gap*2, w: btnW, h: btnH, label: \"Back\" };\n const volSlider: Slider = { x: cx - btnW/2, y: cy + btnH/2 + gap, w: btnW, h: btnH, label: \"Volume\" };\n\n const mute = { btn: muteBtn, state: getButtonState(muteBtn, getPointer()) };\n const back = { btn: backBtn, state: getButtonState(backBtn, getPointer()) };\n\n if (mute.state.clicked) a.setMuted(!a.muted);\n volState = updateSlider(volSlider, volState, getPointer());\n a.setVolume(volState.value);\n\n const ui = { cx, scale, titleY, mute, back, vol: { slider: volSlider, state: volState }, muted: a.muted };\n const frame: SettingsFrame = ui.back.state.clicked\n ? transition({ game: \"menu-title\", ui: null }, a)\n : { game: \"menu-settings\", ui };\n return { frame, volState };\n}\n\nexport function renderSettingsFrame(\n ctx: CanvasRenderingContext2D,\n ui: SettingsMenuState | null\n): void {\n if (!ui) return;\n drawTitle(ctx, \"Settings\", ui.cx, ui.titleY, ui.scale);\n drawButton(ctx, ui.mute.btn, ui.mute.state);\n drawSlider(ctx, ui.vol.slider, ui.vol.state.value);\n drawButton(ctx, ui.back.btn, ui.back.state);\n}\n"],"mappings":";;;;;;AASA,SAAgB,oBACd,GAAW,GAAW,UAAuB,GACI;CACjD,MAAM,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,SAAS,UAAU,GAAG,CAAC;CACzD,MAAM,SAAS,QAAQ;CAEvB,MAAM,UAAoB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,KAAK,OAAK;EAAS,GAAG;EAAM,GAAG;EAAM,OAAO,EAAE,QAAQ,WAAW;CAAO;CACvH,MAAM,UAAoB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,IAAI,OAAO,MAAI;EAAI,GAAG;EAAM,GAAG;EAAM,OAAO;CAAO;CAClG,MAAM,YAAoB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,KAAK,OAAK,IAAI;EAAK,GAAG;EAAM,GAAG;EAAM,OAAO;CAAS;CAEpG,MAAM,OAAO;EAAE,KAAK;EAAS,OAAO,eAAe,SAAS,WAAW,CAAC;CAAE;CAC1E,MAAM,OAAO;EAAE,KAAK;EAAS,OAAO,eAAe,SAAS,WAAW,CAAC;CAAE;CAE1E,IAAI,KAAK,MAAM,SAAS,EAAE,SAAS,CAAC,EAAE,KAAK;CAC3C,WAAW,aAAa,WAAW,UAAU,WAAW,CAAC;CACzD,EAAE,UAAU,SAAS,KAAK;CAE1B,MAAM,KAAK;EAAE;EAAI;EAAO;EAAQ;EAAM;EAAM,KAAK;GAAE,QAAQ;GAAW,OAAO;EAAS;EAAG,OAAO,EAAE;CAAM;CAIxG,OAAO;EAAE,OAHoB,GAAG,KAAK,MAAM,UACvC,WAAW;GAAE,MAAM;GAAc,IAAI;EAAK,GAAG,CAAC,IAC9C;GAAE,MAAM;GAAiB;EAAG;EAChB;CAAS;AAC3B;AAEA,SAAgB,oBACd,KACA,IACM;CACN,IAAI,CAAC,IAAI;CACT,UAAU,KAAK,YAAY,GAAG,IAAI,GAAG,QAAQ,GAAG,KAAK;CACrD,WAAW,KAAK,GAAG,KAAK,KAAO,GAAG,KAAK,KAAK;CAC5C,WAAW,KAAK,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM,KAAK;CACjD,WAAW,KAAK,GAAG,KAAK,KAAO,GAAG,KAAK,KAAK;AAC9C"}
@@ -0,0 +1,8 @@
1
+ import { TitleFrame, TitleMenuState } from "./types.mjs";
2
+ import { Audio } from "@starweb-libs/audio/audio.js";
3
+ //#region src/title.d.ts
4
+ declare function handleTitleFrame(w: number, h: number, a: Audio, levelCount: number, onStart?: () => void): TitleFrame;
5
+ declare function renderTitleFrame(ctx: CanvasRenderingContext2D, ui: TitleMenuState | null, text: string, scaleFactor: number): void;
6
+ //#endregion
7
+ export { handleTitleFrame, renderTitleFrame };
8
+ //# sourceMappingURL=title.d.mts.map
package/dist/title.mjs ADDED
@@ -0,0 +1,61 @@
1
+ import { transition } from "./transition.mjs";
2
+ import { getPointer } from "@starweb-libs/engine/input/pointer.js";
3
+ import { drawTitle, getLayout } from "@starweb-libs/ui/layout.js";
4
+ import { drawButton, getButtonState } from "@starweb-libs/ui/button.js";
5
+ //#region src/title.ts
6
+ function handleTitleFrame(w, h, a, levelCount, onStart) {
7
+ const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);
8
+ const titleY = cy - btnH * 1.5 - gap * 2;
9
+ const startBtn = {
10
+ x: cx - btnW / 2,
11
+ y: cy - btnH / 2,
12
+ w: btnW,
13
+ h: btnH,
14
+ label: "START"
15
+ };
16
+ const settingsBtn = {
17
+ x: cx - btnW / 2,
18
+ y: cy + btnH / 2 + gap,
19
+ w: btnW,
20
+ h: btnH,
21
+ label: "SETTINGS"
22
+ };
23
+ const ui = {
24
+ cx,
25
+ scale,
26
+ titleY,
27
+ start: {
28
+ btn: startBtn,
29
+ state: getButtonState(startBtn, getPointer())
30
+ },
31
+ settings: {
32
+ btn: settingsBtn,
33
+ state: getButtonState(settingsBtn, getPointer())
34
+ }
35
+ };
36
+ if (ui.start.state.clicked) return transition(levelCount > 1 ? {
37
+ game: "menu-levels",
38
+ ui: null
39
+ } : {
40
+ game: "level-playing",
41
+ ui: null
42
+ }, a, onStart);
43
+ if (ui.settings.state.clicked) return transition({
44
+ game: "menu-settings",
45
+ ui: null
46
+ }, a);
47
+ return {
48
+ game: "menu-title",
49
+ ui
50
+ };
51
+ }
52
+ function renderTitleFrame(ctx, ui, text, scaleFactor) {
53
+ if (!ui) return;
54
+ drawTitle(ctx, text, ui.cx, ui.titleY, ui.scale, scaleFactor);
55
+ drawButton(ctx, ui.start.btn, ui.start.state);
56
+ drawButton(ctx, ui.settings.btn, ui.settings.state);
57
+ }
58
+ //#endregion
59
+ export { handleTitleFrame, renderTitleFrame };
60
+
61
+ //# sourceMappingURL=title.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title.mjs","names":[],"sources":["../src/title.ts"],"sourcesContent":["import type { Button } from \"@starweb-libs/ui/types.js\";\nimport type { Audio } from \"@starweb-libs/audio/audio.js\";\nimport { getPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { getLayout, drawTitle } from \"@starweb-libs/ui/layout.js\";\nimport { getButtonState, drawButton } from \"@starweb-libs/ui/button.js\";\nimport type { TitleFrame, TitleMenuState } from \"./types.ts\";\nimport { transition } from \"./transition.ts\";\n\nexport function handleTitleFrame(\n w: number, h: number, a: Audio,\n levelCount: number,\n onStart?: () => void,\n): TitleFrame {\n const { scale, gap, cx, cy, btnW, btnH } = getLayout(w, h);\n const titleY = cy - btnH * 1.5 - gap * 2;\n\n const startBtn: Button = { x: cx - btnW/2, y: cy - btnH/2, w: btnW, h: btnH, label: \"START\" };\n const settingsBtn: Button = { x: cx - btnW/2, y: cy + btnH/2 + gap, w: btnW, h: btnH, label: \"SETTINGS\" };\n\n const ui = {\n cx, scale, titleY,\n start: { btn: startBtn, state: getButtonState(startBtn, getPointer()) },\n settings: { btn: settingsBtn, state: getButtonState(settingsBtn, getPointer()) },\n };\n\n if (ui.start.state.clicked) return transition(\n levelCount > 1\n ? { game: \"menu-levels\", ui: null }\n : { game: \"level-playing\", ui: null },\n a,\n onStart,\n );\n if (ui.settings.state.clicked) return transition({ game: \"menu-settings\", ui: null }, a);\n return { game: \"menu-title\", ui };\n}\n\nexport function renderTitleFrame(\n ctx: CanvasRenderingContext2D,\n ui: TitleMenuState | null,\n text: string,\n scaleFactor: number,\n): void {\n if (!ui) return;\n drawTitle(ctx, text, ui.cx, ui.titleY, ui.scale, scaleFactor);\n drawButton(ctx, ui.start.btn, ui.start.state);\n drawButton(ctx, ui.settings.btn, ui.settings.state);\n}\n"],"mappings":";;;;;AAQA,SAAgB,iBACd,GAAW,GAAW,GACtB,YACA,SACY;CACZ,MAAM,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,SAAS,UAAU,GAAG,CAAC;CACzD,MAAM,SAAS,KAAK,OAAO,MAAM,MAAM;CAEvC,MAAM,WAAsB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,KAAK,OAAK;EAAS,GAAG;EAAM,GAAG;EAAM,OAAO;CAAW;CACxG,MAAM,cAAsB;EAAE,GAAG,KAAK,OAAK;EAAG,GAAG,KAAK,OAAK,IAAI;EAAK,GAAG;EAAM,GAAG;EAAM,OAAO;CAAW;CAExG,MAAM,KAAK;EACT;EAAI;EAAO;EACX,OAAU;GAAE,KAAK;GAAa,OAAO,eAAe,UAAU,WAAW,CAAC;EAAK;EAC/E,UAAU;GAAE,KAAK;GAAa,OAAO,eAAe,aAAa,WAAW,CAAC;EAAE;CACjF;CAEA,IAAI,GAAG,MAAM,MAAM,SAAS,OAAO,WACjC,aAAa,IACT;EAAE,MAAM;EAAe,IAAI;CAAK,IAChC;EAAE,MAAM;EAAiB,IAAI;CAAK,GACtC,GACA,OACF;CACA,IAAI,GAAG,SAAS,MAAM,SAAS,OAAO,WAAW;EAAE,MAAM;EAAiB,IAAI;CAAK,GAAG,CAAC;CACvF,OAAO;EAAE,MAAM;EAAc;CAAG;AAClC;AAEA,SAAgB,iBACd,KACA,IACA,MACA,aACM;CACN,IAAI,CAAC,IAAI;CACT,UAAU,KAAK,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,OAAO,WAAW;CAC5D,WAAW,KAAK,GAAG,MAAM,KAAQ,GAAG,MAAM,KAAK;CAC/C,WAAW,KAAK,GAAG,SAAS,KAAK,GAAG,SAAS,KAAK;AACpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"transition.mjs","names":[],"sources":["../src/transition.ts"],"sourcesContent":["import { flushPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { flushKeyboard } from \"@starweb-libs/engine/input/keyboard.js\";\nimport type { Audio } from \"@starweb-libs/audio/audio.js\";\n\nexport function transition<T>(frame: T, audio: Audio, fn?: () => void): T {\n audio.playSound(\"button\");\n fn?.();\n flushPointer();\n flushKeyboard();\n return frame;\n}\n"],"mappings":";;;AAIA,SAAgB,WAAc,OAAU,OAAc,IAAoB;CACxE,MAAM,UAAU,QAAQ;CACxB,KAAK;CACL,aAAa;CACb,cAAc;CACd,OAAO;AACT"}
1
+ {"version":3,"file":"transition.mjs","names":[],"sources":["../src/transition.ts"],"sourcesContent":["import type { Audio } from \"@starweb-libs/audio/audio.js\";\nimport { flushPointer } from \"@starweb-libs/engine/input/pointer.js\";\nimport { flushKeyboard } from \"@starweb-libs/engine/input/keyboard.js\";\n\nexport function transition<T>(frame: T, audio: Audio, fn?: () => void): T {\n audio.playSound(\"button\");\n fn?.();\n flushPointer();\n flushKeyboard();\n return frame;\n}\n"],"mappings":";;;AAIA,SAAgB,WAAc,OAAU,OAAc,IAAoB;CACxE,MAAM,UAAU,QAAQ;CACxB,KAAK;CACL,aAAa;CACb,cAAc;CACd,OAAO;AACT"}
@@ -0,0 +1,100 @@
1
+ import { ButtonEntry, MenuLayout, SliderEntry } from "@starweb-libs/ui/types.js";
2
+ //#region src/types.d.ts
3
+ interface TitleMenuState extends MenuLayout {
4
+ start: ButtonEntry;
5
+ settings: ButtonEntry;
6
+ }
7
+ type TitleFrame = {
8
+ game: "menu-levels";
9
+ ui: null;
10
+ } | {
11
+ game: "level-playing";
12
+ ui: null;
13
+ } | {
14
+ game: "menu-settings";
15
+ ui: null;
16
+ } | {
17
+ game: "menu-title";
18
+ ui: TitleMenuState;
19
+ };
20
+ type SettingsFrame = {
21
+ game: "menu-title";
22
+ ui: null;
23
+ } | {
24
+ game: "menu-settings";
25
+ ui: SettingsMenuState;
26
+ };
27
+ interface SettingsMenuState extends MenuLayout {
28
+ mute: ButtonEntry;
29
+ back: ButtonEntry;
30
+ vol: SliderEntry;
31
+ muted: boolean;
32
+ }
33
+ type LevelsFrame = {
34
+ game: "menu-title";
35
+ ui: null;
36
+ } | {
37
+ game: "level-playing";
38
+ ui: null;
39
+ } | {
40
+ game: "menu-levels";
41
+ ui: LevelSelectState;
42
+ };
43
+ interface LevelSelectState extends MenuLayout {
44
+ levels: ButtonEntry[];
45
+ back: ButtonEntry;
46
+ clickedIndex: number | null;
47
+ }
48
+ type PauseFrame = {
49
+ game: "level-playing";
50
+ ui: null;
51
+ } | {
52
+ game: "menu-title";
53
+ ui: null;
54
+ } | {
55
+ game: "level-paused";
56
+ ui: PauseMenuState;
57
+ };
58
+ type PauseAction = "resume" | "restart" | "quit" | null;
59
+ interface PauseMenuState extends MenuLayout {
60
+ resume: ButtonEntry;
61
+ restart: ButtonEntry;
62
+ quit: ButtonEntry;
63
+ action: PauseAction;
64
+ }
65
+ type FailedFrame = {
66
+ game: "menu-title";
67
+ ui: null;
68
+ } | {
69
+ game: "level-playing";
70
+ ui: null;
71
+ } | {
72
+ game: "level-failed";
73
+ ui: FailedMenuState;
74
+ };
75
+ type FailedAction = "restart" | "quit" | null;
76
+ interface FailedMenuState extends MenuLayout {
77
+ restart: ButtonEntry;
78
+ quit: ButtonEntry;
79
+ action: FailedAction;
80
+ }
81
+ type CompleteFrame = {
82
+ game: "menu-title";
83
+ ui: null;
84
+ } | {
85
+ game: "level-playing";
86
+ ui: null;
87
+ } | {
88
+ game: "level-complete";
89
+ ui: CompleteMenuState;
90
+ };
91
+ type CompleteAction = "next" | "restart" | "quit" | null;
92
+ interface CompleteMenuState extends MenuLayout {
93
+ restart: ButtonEntry;
94
+ next: ButtonEntry | null;
95
+ quit: ButtonEntry;
96
+ action: CompleteAction;
97
+ }
98
+ //#endregion
99
+ export { CompleteAction, CompleteFrame, CompleteMenuState, FailedAction, FailedFrame, FailedMenuState, LevelSelectState, LevelsFrame, PauseAction, PauseFrame, PauseMenuState, SettingsFrame, SettingsMenuState, TitleFrame, TitleMenuState };
100
+ //# sourceMappingURL=types.d.mts.map
package/dist/types.mjs ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@starweb-libs/menus",
3
3
  "description": "Composable canvas menu screens for browser games",
4
4
  "author": "Mason L'Etoile",
5
- "version": "0.0.2",
5
+ "version": "0.0.3",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -39,10 +39,13 @@
39
39
  },
40
40
  "files": ["dist"],
41
41
  "exports": {
42
- "./transition.js": {
43
- "types": "./dist/transition.d.mts",
44
- "@starweb-libs/source": "./src/transition.ts",
45
- "import": "./dist/transition.mjs"
46
- }
42
+ "./types.js": { "types": "./dist/types.d.mts", "@starweb-libs/source": "./src/types.ts", "import": "./dist/types.mjs" },
43
+ "./transition.js": { "types": "./dist/transition.d.mts", "@starweb-libs/source": "./src/transition.ts", "import": "./dist/transition.mjs" },
44
+ "./title.js": { "types": "./dist/title.d.mts", "@starweb-libs/source": "./src/title.ts", "import": "./dist/title.mjs" },
45
+ "./settings.js": { "types": "./dist/settings.d.mts", "@starweb-libs/source": "./src/settings.ts", "import": "./dist/settings.mjs" },
46
+ "./levels.js": { "types": "./dist/levels.d.mts", "@starweb-libs/source": "./src/levels.ts", "import": "./dist/levels.mjs" },
47
+ "./pause.js": { "types": "./dist/pause.d.mts", "@starweb-libs/source": "./src/pause.ts", "import": "./dist/pause.mjs" },
48
+ "./complete.js": { "types": "./dist/complete.d.mts", "@starweb-libs/source": "./src/complete.ts", "import": "./dist/complete.mjs" },
49
+ "./failed.js": { "types": "./dist/failed.d.mts", "@starweb-libs/source": "./src/failed.ts", "import": "./dist/failed.mjs" }
47
50
  }
48
51
  }