From 60aaa69315210442f9dd024c1a6f60ca6b7caf67 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 15:52:31 -0700 Subject: [PATCH 01/66] use hotkey --- www/games/orbit-idle/src/button.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/button.ts b/www/games/orbit-idle/src/button.ts index 8b8ea27..8ce4fb8 100644 --- a/www/games/orbit-idle/src/button.ts +++ b/www/games/orbit-idle/src/button.ts @@ -21,7 +21,7 @@ export class Button implements Drawable { /** * Initialize a new button. */ - constructor(protected text: string, protected color: Color, protected enabled: boolean, protected x: number, protected y: number, protected w: number, protected h: number, protected callback: () => void) { + constructor(protected text: string, protected color: Color, protected enabled: boolean, protected x: number, protected y: number, protected w: number, protected h: number, protected hotkey: string | null, protected callback: () => void) { this.isHover = false; this.label = new Label(text, color.getContrastingColor(), 1, false, 'center', 'top', this.x + this.w / 2, this.y + Label.fontSize / 2); this.disabledColor = new Color(color.red, color.green, color.blue, color.alpha * 0.5); @@ -61,6 +61,14 @@ export class Button implements Drawable { this.callback(); } } + /** + * Attempt to use the hotkey for this button. + */ + public hkey(key: string): void { + if (key !== null && key === this.hotkey && this.enabled) { + this.callback(); + } + } public draw(graphics: CanvasRenderingContext2D): void { // Render the button and outline, if needed graphics.fillStyle = this.enabled ? this.color.toString() : this.disabledColor.toString(); From fe19f91f28d616195e776202323894772e479a03 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 15:56:13 -0700 Subject: [PATCH 02/66] pass on hotkey --- www/games/orbit-idle/src/button-toggle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/button-toggle.ts b/www/games/orbit-idle/src/button-toggle.ts index 7841a85..ce7ac89 100644 --- a/www/games/orbit-idle/src/button-toggle.ts +++ b/www/games/orbit-idle/src/button-toggle.ts @@ -12,8 +12,8 @@ export class ToggleButton extends Button { /** * Initialize a new toggle button, where the first state is the default state. */ - constructor(private readonly states: [string, Color, () => void][], x: number, y: number, width: number, height: number) { - super(states[0][0], states[0][1], true, x, y, width, height, () => { + constructor(private readonly states: [string, Color, () => void][], x: number, y: number, width: number, height: number, hotkey: string | null) { + super(states[0][0], states[0][1], true, x, y, width, height, hotkey, () => { // Execute the current state's callback function states[0][2](); // Change the current state of the toggle button From e486cf2b42d4fd81592125781e3206a637acfeab Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 15:56:27 -0700 Subject: [PATCH 03/66] no hotkeys --- www/games/orbit-idle/src/buy-type.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/games/orbit-idle/src/buy-type.ts b/www/games/orbit-idle/src/buy-type.ts index e16ca5b..70eafe2 100644 --- a/www/games/orbit-idle/src/buy-type.ts +++ b/www/games/orbit-idle/src/buy-type.ts @@ -29,9 +29,9 @@ export class BuyType implements Drawable { */ constructor(x: number, y: number) { this.quantity = 1; - this.buy1 = new Button('1', BuyType.btnColor, true, x - BuyType.btnSize * 3 / 2 - BuyType.btnPad, y, BuyType.btnSize, BuyType.btnSize, () => this.quantity = 1); - this.buy10 = new Button('10', BuyType.btnColor, true, x - BuyType.btnSize / 2, y, BuyType.btnSize, BuyType.btnSize, () => this.quantity = 10); - this.buy100 = new Button('100', BuyType.btnColor, true, x + BuyType.btnSize / 2 + BuyType.btnPad, y, BuyType.btnSize, BuyType.btnSize, () => this.quantity = 100); + this.buy1 = new Button('1', BuyType.btnColor, true, x - BuyType.btnSize * 3 / 2 - BuyType.btnPad, y, BuyType.btnSize, BuyType.btnSize, null, () => this.quantity = 1); + this.buy10 = new Button('10', BuyType.btnColor, true, x - BuyType.btnSize / 2, y, BuyType.btnSize, BuyType.btnSize, null, () => this.quantity = 10); + this.buy100 = new Button('100', BuyType.btnColor, true, x + BuyType.btnSize / 2 + BuyType.btnPad, y, BuyType.btnSize, BuyType.btnSize, null, () => this.quantity = 100); this.label = new Label('', new Color(255, 255, 255), 1, false, 'center', 'bottom', x, y - Label.fontSize); } /** From 8cc49fb9edb52a8066036f5ca5364878a56fb837 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 15:56:43 -0700 Subject: [PATCH 04/66] space hotkey to advance thru tutorial --- www/games/orbit-idle/src/help.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/help.ts b/www/games/orbit-idle/src/help.ts index 1159048..1cf44b3 100644 --- a/www/games/orbit-idle/src/help.ts +++ b/www/games/orbit-idle/src/help.ts @@ -38,7 +38,7 @@ export class Help extends Button { * Create a new help message with optional highlighted region. */ constructor(text: string, textOffsetX: number, textOffsetY: number, protected readonly x: number, protected readonly y: number, protected readonly w: number, protected readonly h: number, callback: () => void) { - super('', Help.transparent, true, x, y, w, h, callback); + super('', Help.transparent, true, x, y, w, h, ' ', callback); this.tip = new Label(text, Help.highlightStroke, 1, false, textOffsetX < 0 ? 'right' : 'left', 'top', x + textOffsetX, y + textOffsetY); } /** From 45fea89ba88d0263eda23f7f2c1d605acb44f0ec Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 15:57:03 -0700 Subject: [PATCH 05/66] no hotkeys --- www/games/orbit-idle/src/button-asc.ts | 2 +- www/games/orbit-idle/src/button-buy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/button-asc.ts b/www/games/orbit-idle/src/button-asc.ts index a436b63..2996658 100644 --- a/www/games/orbit-idle/src/button-asc.ts +++ b/www/games/orbit-idle/src/button-asc.ts @@ -20,7 +20,7 @@ export class AscendButton extends Button { * Initialize a new ascension button */ constructor(x: number, width: number, private readonly orbit: Orbit) { - super('', orbit.color, false, x, 0, width, Label.fontSize * 3, () => { + super('', orbit.color, false, x, 0, width, Label.fontSize * 3, null, () => { this.orbit.ascend(this.ascensions); player.spend(this.ascAmount); player.earn(this.orbit.getNextNCost(1)); diff --git a/www/games/orbit-idle/src/button-buy.ts b/www/games/orbit-idle/src/button-buy.ts index c6224f3..665147c 100644 --- a/www/games/orbit-idle/src/button-buy.ts +++ b/www/games/orbit-idle/src/button-buy.ts @@ -16,7 +16,7 @@ export class BuyButton extends Button { * Initialize a new buy button. */ constructor(x: number, width: number, private readonly orbit: Orbit) { - super('', orbit.color, true, x, 0, width, Label.fontSize * 3, () => { + super('', orbit.color, true, x, 0, width, Label.fontSize * 3, null, () => { player.spend(this.cost); orbit.increaseSpeed(buyType.getQuantity()); }); From 4f52d9512338ceb5a81691b9090e5c15131295e1 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 15:57:17 -0700 Subject: [PATCH 06/66] no hotkeys --- www/games/orbit-idle/src/button-full.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/button-full.ts b/www/games/orbit-idle/src/button-full.ts index 82c3b66..86fc51a 100644 --- a/www/games/orbit-idle/src/button-full.ts +++ b/www/games/orbit-idle/src/button-full.ts @@ -11,7 +11,7 @@ export class FullAscendButton extends Button { * Create a new full ascend button */ constructor(x: number, y: number, w: number, h: number) { - super('', new Color(200, 200, 200), false, x, y, w, h, () => this.ascend()); + super('', new Color(200, 200, 200), false, x, y, w, h, null, () => this.ascend()); } /** * Ascend and regenerate the game elements From 68f45a77ea01cb47ffc6a9f4eda6bd490639d535 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 15:58:17 -0700 Subject: [PATCH 07/66] actually, no hotkey for tutorial --- www/games/orbit-idle/src/help.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/help.ts b/www/games/orbit-idle/src/help.ts index 1cf44b3..1d8a429 100644 --- a/www/games/orbit-idle/src/help.ts +++ b/www/games/orbit-idle/src/help.ts @@ -38,7 +38,7 @@ export class Help extends Button { * Create a new help message with optional highlighted region. */ constructor(text: string, textOffsetX: number, textOffsetY: number, protected readonly x: number, protected readonly y: number, protected readonly w: number, protected readonly h: number, callback: () => void) { - super('', Help.transparent, true, x, y, w, h, ' ', callback); + super('', Help.transparent, true, x, y, w, h, null, callback); this.tip = new Label(text, Help.highlightStroke, 1, false, textOffsetX < 0 ? 'right' : 'left', 'top', x + textOffsetX, y + textOffsetY); } /** From a1e439b8ad63059da9e4a0ccbceae0b782415933 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:03:16 -0700 Subject: [PATCH 08/66] actually make hotkeys plural --- www/games/orbit-idle/src/button.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/games/orbit-idle/src/button.ts b/www/games/orbit-idle/src/button.ts index 8ce4fb8..2150e78 100644 --- a/www/games/orbit-idle/src/button.ts +++ b/www/games/orbit-idle/src/button.ts @@ -21,7 +21,7 @@ export class Button implements Drawable { /** * Initialize a new button. */ - constructor(protected text: string, protected color: Color, protected enabled: boolean, protected x: number, protected y: number, protected w: number, protected h: number, protected hotkey: string | null, protected callback: () => void) { + constructor(protected text: string, protected color: Color, protected enabled: boolean, protected x: number, protected y: number, protected w: number, protected h: number, protected hotkeys: string[] | null, protected callback: () => void) { this.isHover = false; this.label = new Label(text, color.getContrastingColor(), 1, false, 'center', 'top', this.x + this.w / 2, this.y + Label.fontSize / 2); this.disabledColor = new Color(color.red, color.green, color.blue, color.alpha * 0.5); @@ -64,8 +64,8 @@ export class Button implements Drawable { /** * Attempt to use the hotkey for this button. */ - public hkey(key: string): void { - if (key !== null && key === this.hotkey && this.enabled) { + public hotkey(key: string): void { + if (this.hotkeys?.includes(key) && this.enabled) { this.callback(); } } From daed1af23e47084851454fefb71cf6566727cf6b Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:06:44 -0700 Subject: [PATCH 09/66] hotkeys should be plural here too --- www/games/orbit-idle/src/button-toggle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/button-toggle.ts b/www/games/orbit-idle/src/button-toggle.ts index ce7ac89..afabfe7 100644 --- a/www/games/orbit-idle/src/button-toggle.ts +++ b/www/games/orbit-idle/src/button-toggle.ts @@ -12,8 +12,8 @@ export class ToggleButton extends Button { /** * Initialize a new toggle button, where the first state is the default state. */ - constructor(private readonly states: [string, Color, () => void][], x: number, y: number, width: number, height: number, hotkey: string | null) { - super(states[0][0], states[0][1], true, x, y, width, height, hotkey, () => { + constructor(private readonly states: [string, Color, () => void][], x: number, y: number, width: number, height: number, hotkeys: string[] | null) { + super(states[0][0], states[0][1], true, x, y, width, height, hotkeys, () => { // Execute the current state's callback function states[0][2](); // Change the current state of the toggle button From 36599898daafe628e5badf51c62d97df2e05033e Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:08:11 -0700 Subject: [PATCH 10/66] add +/- hotkeys --- www/games/orbit-idle/src/zoom.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/zoom.ts b/www/games/orbit-idle/src/zoom.ts index 3746493..9dfaf70 100644 --- a/www/games/orbit-idle/src/zoom.ts +++ b/www/games/orbit-idle/src/zoom.ts @@ -36,8 +36,8 @@ export class Zoom implements Drawable { * Create a new zoom control. */ constructor(x: number, y: number, private zoom: number = player.getNumOrbits()) { - this.zoomOutBtn = new Button('-', Zoom.btnColor, true, x - Zoom.btnSize - Zoom.btnPad / 2, y, Zoom.btnSize, Zoom.btnSize, () => this.zoomOut()); - this.zoomInBtn = new Button('+', Zoom.btnColor, true, x + Zoom.btnPad / 2, y, Zoom.btnSize, Zoom.btnSize, () => this.zoomIn()); + this.zoomOutBtn = new Button('-', Zoom.btnColor, true, x - Zoom.btnSize - Zoom.btnPad / 2, y, Zoom.btnSize, Zoom.btnSize, ['-', '_'], () => this.zoomOut()); + this.zoomInBtn = new Button('+', Zoom.btnColor, true, x + Zoom.btnPad / 2, y, Zoom.btnSize, Zoom.btnSize, ['=', '+'], () => this.zoomIn()); this.label = new Label('', new Color(255, 255, 255), 1, false, 'center', 'bottom', x, y - Zoom.btnPad); this.setButtonAbility(); } @@ -101,6 +101,13 @@ export class Zoom implements Drawable { this.zoomInBtn.click(button); this.zoomOutBtn.click(button); } + /** + * Check to see if the user inputted any hotkeys. + */ + public checkHotkeys(key: string): void { + this.zoomInBtn.hotkey(key); + this.zoomOutBtn.hotkey(key); + } public draw(graphics: CanvasRenderingContext2D): void { this.zoomInBtn.draw(graphics); this.zoomOutBtn.draw(graphics); From 58fc1d6e9dc9dfaf1307765bdfff69e50603a5cf Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:08:32 -0700 Subject: [PATCH 11/66] add various hotkeys --- www/games/orbit-idle/src/menu.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/www/games/orbit-idle/src/menu.ts b/www/games/orbit-idle/src/menu.ts index d02ccf4..5aadead 100644 --- a/www/games/orbit-idle/src/menu.ts +++ b/www/games/orbit-idle/src/menu.ts @@ -105,24 +105,24 @@ export class Menu implements Drawable { this.save = new ToggleButton([ ['Save', Menu.btnColor, () => { this.saveReset = Menu.buttonResetDuration; this.saveCallback(); }], ['Saved!', new Color(150, 250, 150), () => { return }], - ], x - (width / 2), y, width, Menu.btnHeight); + ], x - (width / 2), y, width, Menu.btnHeight, ['s']); this.help = new ToggleButton([ ['Tutorial', Menu.btnColor, () => tutorial.show()], ['End Tutorial', Menu.btnColor, () => tutorial.hide()], - ], x - (width / 2), y + Menu.btnHeight + Menu.btnPadding, width, Menu.btnHeight); + ], x - (width / 2), y + Menu.btnHeight + Menu.btnPadding, width, Menu.btnHeight, ['h']); this.mute = new ToggleButton([ ['Mute', Menu.btnColor, () => this.muteCallback()], ['Unmute', Menu.btnColor, () => this.unmuteCallback()], - ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 2, width, Menu.btnHeight); + ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 2, width, Menu.btnHeight, ['m']); this.clear = new ToggleButton([ ['Wipe Data', Menu.btnColor, () => this.clearReset = Menu.buttonResetDuration], ['Confirm', new Color(255, 0, 0), () => this.clearCallback()], ['Reload Page', Menu.btnColor, () => window.location.reload()], - ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 3, width, Menu.btnHeight); + ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 3, width, Menu.btnHeight, null); this.back = new ToggleButton([ ['Menu', Menu.btnColor, () => this.toggle()], ['Back', Menu.btnColor, () => this.toggle()], - ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 4, width, Menu.btnHeight); + ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 4, width, Menu.btnHeight, ['escape']); } /** * Set all callbacks, needed from the main canvas @@ -141,6 +141,15 @@ export class Menu implements Drawable { this.save.reset(); this.clear.reset(); } + /** + * Check to see if the user inputted any hotkeys. + */ + public checkHotkeys(key: string): void { + this.save.hotkey(key); + this.help.hotkey(key); + this.mute.hotkey(key); + this.back.hotkey(key); + } /** * Menu tick for resetting buttons and autosaving */ From 32373e4b40525039686b0c7582f66bb1fc397935 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:08:37 -0700 Subject: [PATCH 12/66] check hotkeys --- www/games/orbit-idle/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/www/games/orbit-idle/src/index.ts b/www/games/orbit-idle/src/index.ts index 58e4e1b..b3567c6 100644 --- a/www/games/orbit-idle/src/index.ts +++ b/www/games/orbit-idle/src/index.ts @@ -58,6 +58,11 @@ const canv: Canvas = new Canvas({ canv.playAudio('./Between_The_Sleepless_Stars.mp3', true, 0.25); } }, + keydown(key) { + console.log(key); + menu.checkHotkeys(key); + zoom.checkHotkeys(key); + }, focus(dt) { system.step(dt); }, From 1a12653c50709c8c93c4a82e32eb39022f3f76e2 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:24:01 -0700 Subject: [PATCH 13/66] mention hotkeys --- www/games/orbit-idle/src/tutorial.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/tutorial.ts b/www/games/orbit-idle/src/tutorial.ts index dcab659..d4dcc7a 100644 --- a/www/games/orbit-idle/src/tutorial.ts +++ b/www/games/orbit-idle/src/tutorial.ts @@ -42,10 +42,10 @@ export class Tutorial implements Drawable { new Help('When an orbit reaches its max speed, you\ncan Ascend it. Ascensions are expensive\nand reset speed for that orbit, but\nincrease your long-term income.', 140, 0, 135, 100, 130, 400, () => this.next()), new Help('The breakdown of your income is shown here.\nEach planet gains +0.01 bonus each planetary year\nby completing a full revolution around the sun.\nBonuses from individual orbits are multiplied\ntogether and raised to their ascension level.', 0, 50, 5, 5, 790, 45, () => this.next()), new Help('This is your total income and\ntotal revolutions per second.\nEach time any planet completes\none full revolution, you earn\nthe income under $/rev.', -10, 0, 600, 50, 200, 80, () => this.next()), - new Help('Use the Zoom controls on the top right to focus\non different planets. Zooming changes the store\nbuttons and income labels to match the focused orbit.', -10, 0, 675, 150, 100, 60, () => this.next()), + new Help('Use the Zoom controls on the top right to focus\non different planets. Zooming changes the store\nbuttons and income labels to match the focused\norbit. Hotkeys: + and -', -10, 0, 675, 150, 100, 60, () => this.next()), new Help('For convenience, you can buy\nspeed upgrades in bulk. It\ndoes not affect ascending.', -10, 0, 660, 220, 130, 60, () => this.next()), new Help('When you have make the required income\nper second, you will be given the option\nto perform a full ascension. This resets\nthe entire solar system, but adds\nan additional planet.', -10, 0, 660, 290, 130, 70, () => this.next()), - new Help('Open Menu > Tutorial anytime to review these slides.\nYour progress saves automatically, and the game\ncontinues earning even when the tab is not active.\nClick "End Tutorial" to close this message.\nGood luck!', -10, 75, 660, 375, 130, 175, () => this.next()), + new Help('Open Menu > Tutorial anytime to review these slides.\nYour progress saves automatically, and the game\ncontinues earning even when the tab is not active.\nClick "End Tutorial" to close this message.\nGood luck!\n\nHotkeys: (S)ave, (H)elp, (M)ute, (ESC) open/close menu', -10, 75, 660, 375, 130, 175, () => this.next()), ]; } /** From ca44c9d04745f57684a151b6dc6cab8a729e76df Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:26:07 -0700 Subject: [PATCH 14/66] create stats class --- www/games/orbit-idle/src/stats.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 www/games/orbit-idle/src/stats.ts diff --git a/www/games/orbit-idle/src/stats.ts b/www/games/orbit-idle/src/stats.ts new file mode 100644 index 0000000..9da92a7 --- /dev/null +++ b/www/games/orbit-idle/src/stats.ts @@ -0,0 +1,7 @@ +import { Drawable } from 'graphico'; + +export class Statistics implements Drawable { + public draw(graphics: CanvasRenderingContext2D): void { + throw new Error("Method not implemented."); + } +} From 4cf16b9f51399f8cf62ad85189fc001f08cf73ae Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:36:22 -0700 Subject: [PATCH 15/66] render shade and labels --- www/games/orbit-idle/src/stats.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/stats.ts b/www/games/orbit-idle/src/stats.ts index 9da92a7..2b1fe87 100644 --- a/www/games/orbit-idle/src/stats.ts +++ b/www/games/orbit-idle/src/stats.ts @@ -1,7 +1,31 @@ import { Drawable } from 'graphico'; +import { Label } from './label'; +import { Color } from 'viridis'; +/** + * Represents an in-game statistics panel + */ export class Statistics implements Drawable { + private static readonly shade: Color = new Color(0, 0, 0, 50); + private readonly title: Label; + private readonly headers: Label; + private readonly content: Label; + /** + * Create a new statistics window + */ + constructor() { + this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 14); + this.headers = new Label('Test', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); + this.content = new Label('Test', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 15); + } public draw(graphics: CanvasRenderingContext2D): void { - throw new Error("Method not implemented."); + graphics.fillStyle = Statistics.shade.toString(); + graphics.fillRect(0, 0, graphics.canvas.width, graphics.canvas.height); + this.title.x = graphics.canvas.width / 2; + this.headers.x = (graphics.canvas.width - Label.fontSize) / 2; + this.content.x = (graphics.canvas.width + Label.fontSize) / 2; + this.title.draw(graphics); + this.headers.draw(graphics); + this.content.draw(graphics); } } From fc1bdd89713ae5b1cce4c0b276b4703a10524c68 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:36:29 -0700 Subject: [PATCH 16/66] create stats in state --- www/games/orbit-idle/src/state.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/games/orbit-idle/src/state.ts b/www/games/orbit-idle/src/state.ts index 41ea334..5dacd83 100644 --- a/www/games/orbit-idle/src/state.ts +++ b/www/games/orbit-idle/src/state.ts @@ -7,6 +7,7 @@ import { Pause } from './pause'; import { Player } from './player'; import { Shop } from './shop'; import { SolarSystem } from './solar-system'; +import { Statistics } from './stats'; import { Tutorial } from './tutorial'; import { Zoom } from './zoom'; @@ -19,6 +20,7 @@ export const player: Player = new Player(); export const zoom: Zoom = new Zoom(725, 175); export const buyType: BuyType = new BuyType(725, 250); export const ascBtn: FullAscendButton = new FullAscendButton(675, 300, 100, 50); +export const stats: Statistics = new Statistics(); // Game elements export const system: SolarSystem = new SolarSystem(); From e8ff6805f1c725be9ffc83f488f9a301d339b64c Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:44:10 -0700 Subject: [PATCH 17/66] add visibility and close button --- www/games/orbit-idle/src/stats.ts | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/www/games/orbit-idle/src/stats.ts b/www/games/orbit-idle/src/stats.ts index 2b1fe87..2c9f938 100644 --- a/www/games/orbit-idle/src/stats.ts +++ b/www/games/orbit-idle/src/stats.ts @@ -1,24 +1,60 @@ import { Drawable } from 'graphico'; import { Label } from './label'; import { Color } from 'viridis'; +import { Button } from './button'; /** * Represents an in-game statistics panel */ export class Statistics implements Drawable { private static readonly shade: Color = new Color(0, 0, 0, 50); + private visible: boolean; private readonly title: Label; private readonly headers: Label; private readonly content: Label; + private readonly close: Button; /** * Create a new statistics window */ constructor() { + this.visible = false; this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 14); this.headers = new Label('Test', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); this.content = new Label('Test', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 15); + this.close = new Button('x', new Color(255, 100, 100), true, Label.fontSize / 2, Label.fontSize / 2, Label.fontSize * 2, Label.fontSize * 2, null, () => this.hide()); + } + /** + * Show the statistics panel + */ + public show(): void { + this.visible = true; + } + /** + * Hide the statistics panel + */ + private hide(): void { + this.visible = false; + } + /** + * Check to see if the mouse is hovering over the close button + */ + public checkHover(x: number, y: number): void { + if (this.visible) { + this.close.checkHover(x, y); + } + } + /** + * Check to see if the user clicked the close button + */ + public click(button: number): void { + if (this.visible) { + this.close.click(button); + } } public draw(graphics: CanvasRenderingContext2D): void { + if (!this.visible) { + return; + } graphics.fillStyle = Statistics.shade.toString(); graphics.fillRect(0, 0, graphics.canvas.width, graphics.canvas.height); this.title.x = graphics.canvas.width / 2; @@ -27,5 +63,6 @@ export class Statistics implements Drawable { this.title.draw(graphics); this.headers.draw(graphics); this.content.draw(graphics); + this.close.draw(graphics); } } From ae34107a477d899ddc17364d2de5ba72c5159ba5 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:44:18 -0700 Subject: [PATCH 18/66] implement it in index --- www/games/orbit-idle/src/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/index.ts b/www/games/orbit-idle/src/index.ts index b3567c6..4e12b5a 100644 --- a/www/games/orbit-idle/src/index.ts +++ b/www/games/orbit-idle/src/index.ts @@ -1,8 +1,10 @@ import './close-info-handler'; import { Canvas } from 'graphico'; -import { ascBtn, buyType, cursor, income, menu, paused, player, shop, system, tutorial, zoom } from './state'; +import { ascBtn, buyType, cursor, income, menu, paused, player, shop, stats, system, tutorial, zoom } from './state'; import { GameData } from './gamedata'; +stats.show(); + /** * Whether the canvas started playing audio yet */ @@ -32,6 +34,7 @@ const canv: Canvas = new Canvas({ canv.draw(buyType, 1); canv.draw(ascBtn, 1); canv.draw(menu, 1); + canv.draw(stats, 1); tutorial.tick(dt); canv.draw(tutorial, 2); cursor.tick(dt); @@ -45,6 +48,7 @@ const canv: Canvas = new Canvas({ shop.checkHover(x, y); ascBtn.checkHover(x, y); menu.checkHover(x, y); + stats.checkHover(x, y); }, mousedown(button) { tutorial.click(button); @@ -53,6 +57,7 @@ const canv: Canvas = new Canvas({ shop.click(button); ascBtn.click(button); menu.click(button); + stats.click(button); if (!audio) { audio = true; canv.playAudio('./Between_The_Sleepless_Stars.mp3', true, 0.25); From 7565b3e3c7cf79d7d8f7cf5392c4a7b5089f5571 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:47:38 -0700 Subject: [PATCH 19/66] oops --- www/games/orbit-idle/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/www/games/orbit-idle/src/index.ts b/www/games/orbit-idle/src/index.ts index 4e12b5a..1153493 100644 --- a/www/games/orbit-idle/src/index.ts +++ b/www/games/orbit-idle/src/index.ts @@ -3,8 +3,6 @@ import { Canvas } from 'graphico'; import { ascBtn, buyType, cursor, income, menu, paused, player, shop, stats, system, tutorial, zoom } from './state'; import { GameData } from './gamedata'; -stats.show(); - /** * Whether the canvas started playing audio yet */ From 388b8a500c4594486b0c60c38016a6cabb621c27 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:47:48 -0700 Subject: [PATCH 20/66] start working on interface --- www/games/orbit-idle/src/stats.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/www/games/orbit-idle/src/stats.ts b/www/games/orbit-idle/src/stats.ts index 2c9f938..0c18aa9 100644 --- a/www/games/orbit-idle/src/stats.ts +++ b/www/games/orbit-idle/src/stats.ts @@ -66,3 +66,13 @@ export class Statistics implements Drawable { this.close.draw(graphics); } } + +/** + * Interface containing various in-game statistics + */ +export interface Stats { + moneyEarned: number; + orbitsCompleted: number; + speedLevelsPurchased: number; + ascensionsPurchased: number; +} From b9f12a9a3aac19ed88d951bea1e6ee4aa3cf266e Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:49:05 -0700 Subject: [PATCH 21/66] move to form controls subfolder --- www/games/orbit-idle/src/{ => form-controls}/button-asc.ts | 6 +++--- www/games/orbit-idle/src/{ => form-controls}/button-buy.ts | 6 +++--- www/games/orbit-idle/src/{ => form-controls}/button-full.ts | 4 ++-- .../orbit-idle/src/{ => form-controls}/button-toggle.ts | 0 www/games/orbit-idle/src/{ => form-controls}/button.ts | 0 www/games/orbit-idle/src/{ => form-controls}/label.ts | 0 6 files changed, 8 insertions(+), 8 deletions(-) rename www/games/orbit-idle/src/{ => form-controls}/button-asc.ts (94%) rename www/games/orbit-idle/src/{ => form-controls}/button-buy.ts (93%) rename www/games/orbit-idle/src/{ => form-controls}/button-full.ts (92%) rename www/games/orbit-idle/src/{ => form-controls}/button-toggle.ts (100%) rename www/games/orbit-idle/src/{ => form-controls}/button.ts (100%) rename www/games/orbit-idle/src/{ => form-controls}/label.ts (100%) diff --git a/www/games/orbit-idle/src/button-asc.ts b/www/games/orbit-idle/src/form-controls/button-asc.ts similarity index 94% rename from www/games/orbit-idle/src/button-asc.ts rename to www/games/orbit-idle/src/form-controls/button-asc.ts index 2996658..9f98aa5 100644 --- a/www/games/orbit-idle/src/button-asc.ts +++ b/www/games/orbit-idle/src/form-controls/button-asc.ts @@ -1,8 +1,8 @@ import { Button } from './button'; import { Label } from './label'; -import { N } from './lib'; -import { player, zoom } from './state'; -import { Orbit } from './orbit'; +import { N } from '../lib'; +import { player, zoom } from '../state'; +import { Orbit } from '../orbit'; /** * Represents a button to ascend a specific orbit diff --git a/www/games/orbit-idle/src/button-buy.ts b/www/games/orbit-idle/src/form-controls/button-buy.ts similarity index 93% rename from www/games/orbit-idle/src/button-buy.ts rename to www/games/orbit-idle/src/form-controls/button-buy.ts index 665147c..a29064d 100644 --- a/www/games/orbit-idle/src/button-buy.ts +++ b/www/games/orbit-idle/src/form-controls/button-buy.ts @@ -1,8 +1,8 @@ import { Button } from './button'; import { Label } from './label'; -import { N } from './lib'; -import { buyType, player, zoom } from './state'; -import { Orbit } from './orbit'; +import { N } from '../lib'; +import { buyType, player, zoom } from '../state'; +import { Orbit } from '../orbit'; /** * Represents a button to increase a orbit's speed. diff --git a/www/games/orbit-idle/src/button-full.ts b/www/games/orbit-idle/src/form-controls/button-full.ts similarity index 92% rename from www/games/orbit-idle/src/button-full.ts rename to www/games/orbit-idle/src/form-controls/button-full.ts index 86fc51a..0c434fa 100644 --- a/www/games/orbit-idle/src/button-full.ts +++ b/www/games/orbit-idle/src/form-controls/button-full.ts @@ -1,7 +1,7 @@ import { Color } from 'viridis'; import { Button } from './button'; -import { income, player, shop, system, zoom } from './state'; -import { N } from './lib'; +import { income, player, shop, system, zoom } from '../state'; +import { N } from '../lib'; /** * Represents a button to perform a full ascension diff --git a/www/games/orbit-idle/src/button-toggle.ts b/www/games/orbit-idle/src/form-controls/button-toggle.ts similarity index 100% rename from www/games/orbit-idle/src/button-toggle.ts rename to www/games/orbit-idle/src/form-controls/button-toggle.ts diff --git a/www/games/orbit-idle/src/button.ts b/www/games/orbit-idle/src/form-controls/button.ts similarity index 100% rename from www/games/orbit-idle/src/button.ts rename to www/games/orbit-idle/src/form-controls/button.ts diff --git a/www/games/orbit-idle/src/label.ts b/www/games/orbit-idle/src/form-controls/label.ts similarity index 100% rename from www/games/orbit-idle/src/label.ts rename to www/games/orbit-idle/src/form-controls/label.ts From 8898ce3fb0a5999f44462c46f94a94caaf52146e Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:51:33 -0700 Subject: [PATCH 22/66] move menu options to ui --- www/games/orbit-idle/src/{ => ui}/buy-type.ts | 4 ++-- www/games/orbit-idle/src/{ => ui}/menu.ts | 6 +++--- www/games/orbit-idle/src/{ => ui}/pause.ts | 2 +- www/games/orbit-idle/src/{ => ui}/zoom.ts | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) rename www/games/orbit-idle/src/{ => ui}/buy-type.ts (96%) rename www/games/orbit-idle/src/{ => ui}/menu.ts (97%) rename www/games/orbit-idle/src/{ => ui}/pause.ts (95%) rename www/games/orbit-idle/src/{ => ui}/zoom.ts (96%) diff --git a/www/games/orbit-idle/src/buy-type.ts b/www/games/orbit-idle/src/ui/buy-type.ts similarity index 96% rename from www/games/orbit-idle/src/buy-type.ts rename to www/games/orbit-idle/src/ui/buy-type.ts index 70eafe2..6ed8675 100644 --- a/www/games/orbit-idle/src/buy-type.ts +++ b/www/games/orbit-idle/src/ui/buy-type.ts @@ -1,7 +1,7 @@ import { Drawable } from 'graphico'; -import { Button } from './button'; +import { Button } from '../form-controls/button'; import { Color } from 'viridis'; -import { Label } from './label'; +import { Label } from '../form-controls/label'; /** * Represents a control to change how many items are being purchased at once. diff --git a/www/games/orbit-idle/src/menu.ts b/www/games/orbit-idle/src/ui/menu.ts similarity index 97% rename from www/games/orbit-idle/src/menu.ts rename to www/games/orbit-idle/src/ui/menu.ts index 5aadead..29f266c 100644 --- a/www/games/orbit-idle/src/menu.ts +++ b/www/games/orbit-idle/src/ui/menu.ts @@ -1,8 +1,8 @@ import { Drawable } from 'graphico'; import { Color } from 'viridis'; -import { Label } from './label'; -import { ToggleButton } from './button-toggle'; -import { tutorial, version } from './state'; +import { Label } from '../form-controls/label'; +import { ToggleButton } from '../form-controls/button-toggle'; +import { tutorial, version } from '../state'; /** * Represents the in-game menu diff --git a/www/games/orbit-idle/src/pause.ts b/www/games/orbit-idle/src/ui/pause.ts similarity index 95% rename from www/games/orbit-idle/src/pause.ts rename to www/games/orbit-idle/src/ui/pause.ts index 4482f43..31d8ed1 100644 --- a/www/games/orbit-idle/src/pause.ts +++ b/www/games/orbit-idle/src/ui/pause.ts @@ -1,5 +1,5 @@ import { Drawable } from 'graphico'; -import { Label } from './label'; +import { Label } from '../form-controls/label'; import { Color } from 'viridis'; /** diff --git a/www/games/orbit-idle/src/zoom.ts b/www/games/orbit-idle/src/ui/zoom.ts similarity index 96% rename from www/games/orbit-idle/src/zoom.ts rename to www/games/orbit-idle/src/ui/zoom.ts index 9dfaf70..431d194 100644 --- a/www/games/orbit-idle/src/zoom.ts +++ b/www/games/orbit-idle/src/ui/zoom.ts @@ -1,8 +1,8 @@ import { Drawable } from 'graphico'; -import { Button } from './button'; +import { Button } from '../form-controls/button'; import { Color } from 'viridis'; -import { Label } from './label'; -import { player } from './state'; +import { Label } from '../form-controls/label'; +import { player } from '../state'; /** * Represents a zoom control. From e78a1eb63e3481e9e60f509074f89e871e8bacbe Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 16:59:21 -0700 Subject: [PATCH 23/66] move almost everything into subdirs --- .../orbit-idle/src/{ => data}/gamedata.ts | 4 ++-- www/games/orbit-idle/src/{ => data}/lib.ts | 0 www/games/orbit-idle/src/{ => data}/state.ts | 22 +++++++++---------- www/games/orbit-idle/src/{ => data}/stats.ts | 4 ++-- .../src/form-controls/button-asc.ts | 6 ++--- .../src/form-controls/button-buy.ts | 6 ++--- .../src/form-controls/button-full.ts | 4 ++-- .../src/{ => form-controls}/help.ts | 0 .../src/{ => form-controls}/tutorial.ts | 0 www/games/orbit-idle/src/{ => game}/income.ts | 6 ++--- .../orbit-idle/src/{ => game}/orbit-score.ts | 4 ++-- www/games/orbit-idle/src/{ => game}/orbit.ts | 2 +- www/games/orbit-idle/src/{ => game}/player.ts | 4 ++-- www/games/orbit-idle/src/{ => game}/shop.ts | 8 +++---- .../orbit-idle/src/{ => game}/solar-system.ts | 2 +- www/games/orbit-idle/src/index.ts | 4 ++-- www/games/orbit-idle/src/{ => ui}/cursor.ts | 0 www/games/orbit-idle/src/ui/menu.ts | 2 +- www/games/orbit-idle/src/ui/zoom.ts | 2 +- 19 files changed, 40 insertions(+), 40 deletions(-) rename www/games/orbit-idle/src/{ => data}/gamedata.ts (68%) rename www/games/orbit-idle/src/{ => data}/lib.ts (100%) rename www/games/orbit-idle/src/{ => data}/state.ts (65%) rename www/games/orbit-idle/src/{ => data}/stats.ts (96%) rename www/games/orbit-idle/src/{ => form-controls}/help.ts (100%) rename www/games/orbit-idle/src/{ => form-controls}/tutorial.ts (100%) rename www/games/orbit-idle/src/{ => game}/income.ts (96%) rename www/games/orbit-idle/src/{ => game}/orbit-score.ts (96%) rename www/games/orbit-idle/src/{ => game}/orbit.ts (99%) rename www/games/orbit-idle/src/{ => game}/player.ts (97%) rename www/games/orbit-idle/src/{ => game}/shop.ts (90%) rename www/games/orbit-idle/src/{ => game}/solar-system.ts (97%) rename www/games/orbit-idle/src/{ => ui}/cursor.ts (100%) diff --git a/www/games/orbit-idle/src/gamedata.ts b/www/games/orbit-idle/src/data/gamedata.ts similarity index 68% rename from www/games/orbit-idle/src/gamedata.ts rename to www/games/orbit-idle/src/data/gamedata.ts index ff5e5a9..1248035 100644 --- a/www/games/orbit-idle/src/gamedata.ts +++ b/www/games/orbit-idle/src/data/gamedata.ts @@ -1,5 +1,5 @@ -import { PlayerData } from "./player"; -import { SolarData } from "./solar-system"; +import { PlayerData } from '../game/player'; +import { SolarData } from '../game/solar-system'; /** * Represents and interface for saving and loading game data diff --git a/www/games/orbit-idle/src/lib.ts b/www/games/orbit-idle/src/data/lib.ts similarity index 100% rename from www/games/orbit-idle/src/lib.ts rename to www/games/orbit-idle/src/data/lib.ts diff --git a/www/games/orbit-idle/src/state.ts b/www/games/orbit-idle/src/data/state.ts similarity index 65% rename from www/games/orbit-idle/src/state.ts rename to www/games/orbit-idle/src/data/state.ts index 5dacd83..b332e15 100644 --- a/www/games/orbit-idle/src/state.ts +++ b/www/games/orbit-idle/src/data/state.ts @@ -1,15 +1,15 @@ -import { FullAscendButton } from './button-full'; -import { BuyType } from './buy-type'; -import { Cursor } from './cursor'; -import { Income } from './income'; -import { Menu } from './menu'; -import { Pause } from './pause'; -import { Player } from './player'; -import { Shop } from './shop'; -import { SolarSystem } from './solar-system'; +import { FullAscendButton } from '../form-controls/button-full'; +import { BuyType } from '../ui/buy-type'; +import { Cursor } from '../ui/cursor'; +import { Income } from '../game/income'; +import { Menu } from '../ui/menu'; +import { Pause } from '../ui/pause'; +import { Player } from '../game/player'; +import { Shop } from '../game/shop'; +import { SolarSystem } from '../game/solar-system'; import { Statistics } from './stats'; -import { Tutorial } from './tutorial'; -import { Zoom } from './zoom'; +import { Tutorial } from '../form-controls/tutorial'; +import { Zoom } from '../ui/zoom'; // Global constants export const version = 'v1.2.1-alpha'; diff --git a/www/games/orbit-idle/src/stats.ts b/www/games/orbit-idle/src/data/stats.ts similarity index 96% rename from www/games/orbit-idle/src/stats.ts rename to www/games/orbit-idle/src/data/stats.ts index 0c18aa9..55dabdf 100644 --- a/www/games/orbit-idle/src/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -1,7 +1,7 @@ import { Drawable } from 'graphico'; -import { Label } from './label'; +import { Label } from '../form-controls/label'; import { Color } from 'viridis'; -import { Button } from './button'; +import { Button } from '../form-controls/button'; /** * Represents an in-game statistics panel diff --git a/www/games/orbit-idle/src/form-controls/button-asc.ts b/www/games/orbit-idle/src/form-controls/button-asc.ts index 9f98aa5..deee129 100644 --- a/www/games/orbit-idle/src/form-controls/button-asc.ts +++ b/www/games/orbit-idle/src/form-controls/button-asc.ts @@ -1,8 +1,8 @@ import { Button } from './button'; import { Label } from './label'; -import { N } from '../lib'; -import { player, zoom } from '../state'; -import { Orbit } from '../orbit'; +import { N } from '../data/lib'; +import { player, zoom } from '../data/state'; +import { Orbit } from '../game/orbit'; /** * Represents a button to ascend a specific orbit diff --git a/www/games/orbit-idle/src/form-controls/button-buy.ts b/www/games/orbit-idle/src/form-controls/button-buy.ts index a29064d..1d8a88a 100644 --- a/www/games/orbit-idle/src/form-controls/button-buy.ts +++ b/www/games/orbit-idle/src/form-controls/button-buy.ts @@ -1,8 +1,8 @@ import { Button } from './button'; import { Label } from './label'; -import { N } from '../lib'; -import { buyType, player, zoom } from '../state'; -import { Orbit } from '../orbit'; +import { N } from '../data/lib'; +import { buyType, player, zoom } from '../data/state'; +import { Orbit } from '../game/orbit'; /** * Represents a button to increase a orbit's speed. diff --git a/www/games/orbit-idle/src/form-controls/button-full.ts b/www/games/orbit-idle/src/form-controls/button-full.ts index 0c434fa..26addd0 100644 --- a/www/games/orbit-idle/src/form-controls/button-full.ts +++ b/www/games/orbit-idle/src/form-controls/button-full.ts @@ -1,7 +1,7 @@ import { Color } from 'viridis'; import { Button } from './button'; -import { income, player, shop, system, zoom } from '../state'; -import { N } from '../lib'; +import { income, player, shop, system, zoom } from '../data/state'; +import { N } from '../data/lib'; /** * Represents a button to perform a full ascension diff --git a/www/games/orbit-idle/src/help.ts b/www/games/orbit-idle/src/form-controls/help.ts similarity index 100% rename from www/games/orbit-idle/src/help.ts rename to www/games/orbit-idle/src/form-controls/help.ts diff --git a/www/games/orbit-idle/src/tutorial.ts b/www/games/orbit-idle/src/form-controls/tutorial.ts similarity index 100% rename from www/games/orbit-idle/src/tutorial.ts rename to www/games/orbit-idle/src/form-controls/tutorial.ts diff --git a/www/games/orbit-idle/src/income.ts b/www/games/orbit-idle/src/game/income.ts similarity index 96% rename from www/games/orbit-idle/src/income.ts rename to www/games/orbit-idle/src/game/income.ts index 8ebe1ab..1fc26fb 100644 --- a/www/games/orbit-idle/src/income.ts +++ b/www/games/orbit-idle/src/game/income.ts @@ -1,10 +1,10 @@ import { SMath } from 'smath'; import { Drawable } from 'graphico'; import { OrbitScore } from './orbit-score'; -import { N } from './lib'; -import { Label } from './label'; +import { N } from '../data/lib'; +import { Label } from '../form-controls/label'; import { Color } from 'viridis'; -import { player, system, zoom } from './state'; +import { player, system, zoom } from '../data/state'; /** * Handles and renders game income. diff --git a/www/games/orbit-idle/src/orbit-score.ts b/www/games/orbit-idle/src/game/orbit-score.ts similarity index 96% rename from www/games/orbit-idle/src/orbit-score.ts rename to www/games/orbit-idle/src/game/orbit-score.ts index 74436d7..f60bce6 100644 --- a/www/games/orbit-idle/src/orbit-score.ts +++ b/www/games/orbit-idle/src/game/orbit-score.ts @@ -1,8 +1,8 @@ import { Drawable } from 'graphico'; import { Color } from 'viridis'; import { Orbit } from './orbit'; -import { N } from './lib'; -import { Label } from './label'; +import { N } from '../data/lib'; +import { Label } from '../form-controls/label'; /** * Renders the orbit score to the user interface. diff --git a/www/games/orbit-idle/src/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts similarity index 99% rename from www/games/orbit-idle/src/orbit.ts rename to www/games/orbit-idle/src/game/orbit.ts index 05d6879..a849e6f 100644 --- a/www/games/orbit-idle/src/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -1,7 +1,7 @@ import { Drawable } from 'graphico'; import { SMath, Vec3 } from 'smath'; import { Color } from 'viridis'; -import { player, zoom } from './state'; +import { player, zoom } from '../data/state'; /** * Represents a single planetary orbit diff --git a/www/games/orbit-idle/src/player.ts b/www/games/orbit-idle/src/game/player.ts similarity index 97% rename from www/games/orbit-idle/src/player.ts rename to www/games/orbit-idle/src/game/player.ts index 2a9b20a..d302bad 100644 --- a/www/games/orbit-idle/src/player.ts +++ b/www/games/orbit-idle/src/game/player.ts @@ -1,6 +1,6 @@ import { Drawable } from 'graphico'; -import { N } from './lib'; -import { Label } from './label'; +import { N } from '../data/lib'; +import { Label } from '../form-controls/label'; import { Color } from 'viridis'; import { SMath } from 'smath'; diff --git a/www/games/orbit-idle/src/shop.ts b/www/games/orbit-idle/src/game/shop.ts similarity index 90% rename from www/games/orbit-idle/src/shop.ts rename to www/games/orbit-idle/src/game/shop.ts index 829fb60..8f997e2 100644 --- a/www/games/orbit-idle/src/shop.ts +++ b/www/games/orbit-idle/src/game/shop.ts @@ -1,8 +1,8 @@ import { Drawable } from 'graphico'; -import { Label } from './label'; -import { BuyButton } from './button-buy'; -import { AscendButton } from './button-asc'; -import { system } from './state'; +import { Label } from '../form-controls/label'; +import { BuyButton } from '../form-controls/button-buy'; +import { AscendButton } from '../form-controls/button-asc'; +import { system } from '../data/state'; /** * Represents a collection of buy buttons and ascension buttons (if applicable.) diff --git a/www/games/orbit-idle/src/solar-system.ts b/www/games/orbit-idle/src/game/solar-system.ts similarity index 97% rename from www/games/orbit-idle/src/solar-system.ts rename to www/games/orbit-idle/src/game/solar-system.ts index 336263e..c77ac7f 100644 --- a/www/games/orbit-idle/src/solar-system.ts +++ b/www/games/orbit-idle/src/game/solar-system.ts @@ -1,6 +1,6 @@ import { Drawable } from 'graphico'; import { Orbit, OrbitData } from './orbit'; -import { income, player, zoom } from './state'; +import { income, player, zoom } from '../data/state'; import { Color, Gradient } from 'viridis'; import { Vec3 } from 'smath'; diff --git a/www/games/orbit-idle/src/index.ts b/www/games/orbit-idle/src/index.ts index 1153493..8104ae9 100644 --- a/www/games/orbit-idle/src/index.ts +++ b/www/games/orbit-idle/src/index.ts @@ -1,7 +1,7 @@ import './close-info-handler'; import { Canvas } from 'graphico'; -import { ascBtn, buyType, cursor, income, menu, paused, player, shop, stats, system, tutorial, zoom } from './state'; -import { GameData } from './gamedata'; +import { ascBtn, buyType, cursor, income, menu, paused, player, shop, stats, system, tutorial, zoom } from './data/state'; +import { GameData } from './data/gamedata'; /** * Whether the canvas started playing audio yet diff --git a/www/games/orbit-idle/src/cursor.ts b/www/games/orbit-idle/src/ui/cursor.ts similarity index 100% rename from www/games/orbit-idle/src/cursor.ts rename to www/games/orbit-idle/src/ui/cursor.ts diff --git a/www/games/orbit-idle/src/ui/menu.ts b/www/games/orbit-idle/src/ui/menu.ts index 29f266c..3e6022a 100644 --- a/www/games/orbit-idle/src/ui/menu.ts +++ b/www/games/orbit-idle/src/ui/menu.ts @@ -2,7 +2,7 @@ import { Drawable } from 'graphico'; import { Color } from 'viridis'; import { Label } from '../form-controls/label'; import { ToggleButton } from '../form-controls/button-toggle'; -import { tutorial, version } from '../state'; +import { tutorial, version } from '../data/state'; /** * Represents the in-game menu diff --git a/www/games/orbit-idle/src/ui/zoom.ts b/www/games/orbit-idle/src/ui/zoom.ts index 431d194..0bdf7e0 100644 --- a/www/games/orbit-idle/src/ui/zoom.ts +++ b/www/games/orbit-idle/src/ui/zoom.ts @@ -2,7 +2,7 @@ import { Drawable } from 'graphico'; import { Button } from '../form-controls/button'; import { Color } from 'viridis'; import { Label } from '../form-controls/label'; -import { player } from '../state'; +import { player } from '../data/state'; /** * Represents a zoom control. From c8ffb2f0ba5ba9188d20cccc9782f616cf15581b Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:04:22 -0700 Subject: [PATCH 24/66] create and assign default stats --- www/games/orbit-idle/src/data/stats.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index 55dabdf..36b4273 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -16,7 +16,7 @@ export class Statistics implements Drawable { /** * Create a new statistics window */ - constructor() { + constructor(public stats: Stats = defaultStats) { this.visible = false; this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 14); this.headers = new Label('Test', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); @@ -76,3 +76,10 @@ export interface Stats { speedLevelsPurchased: number; ascensionsPurchased: number; } + +const defaultStats: Stats = { + moneyEarned: 0, + orbitsCompleted: 0, + speedLevelsPurchased: 0, + ascensionsPurchased: 0, +}; From c971eb2af2b39f298660e7335707abc74489d6f1 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:12:51 -0700 Subject: [PATCH 25/66] add money spent --- www/games/orbit-idle/src/data/stats.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index 36b4273..9b4e365 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -72,6 +72,7 @@ export class Statistics implements Drawable { */ export interface Stats { moneyEarned: number; + moneySpent: number; orbitsCompleted: number; speedLevelsPurchased: number; ascensionsPurchased: number; @@ -79,6 +80,7 @@ export interface Stats { const defaultStats: Stats = { moneyEarned: 0, + moneySpent: 0, orbitsCompleted: 0, speedLevelsPurchased: 0, ascensionsPurchased: 0, From 3b4e62bac4a892b415fe23b6e882e5d8294429c0 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:13:03 -0700 Subject: [PATCH 26/66] track money totals --- www/games/orbit-idle/src/game/player.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/www/games/orbit-idle/src/game/player.ts b/www/games/orbit-idle/src/game/player.ts index d302bad..49282f9 100644 --- a/www/games/orbit-idle/src/game/player.ts +++ b/www/games/orbit-idle/src/game/player.ts @@ -3,6 +3,7 @@ import { N } from '../data/lib'; import { Label } from '../form-controls/label'; import { Color } from 'viridis'; import { SMath } from 'smath'; +import { stats } from '../data/state'; /** * Represents the main player of the game @@ -23,12 +24,14 @@ export class Player implements Drawable { */ public earn(amount: number): void { this.data.money += amount; + stats.stats.moneyEarned += amount; } /** * Spend a certain amount of money from the player's bank */ public spend(amount: number): void { this.data.money -= amount; + stats.stats.moneySpent += amount; } /** * Determine if the player has sufficient funds to purchase something of a certain amount From 162d7074b2d7b187e3f231ea890c20a6d4e0fb0b Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:20:23 -0700 Subject: [PATCH 27/66] get next ascensions exp --- www/games/orbit-idle/src/game/orbit.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/game/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts index a849e6f..9c4f7ea 100644 --- a/www/games/orbit-idle/src/game/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -1,7 +1,7 @@ import { Drawable } from 'graphico'; import { SMath, Vec3 } from 'smath'; import { Color } from 'viridis'; -import { player, zoom } from '../data/state'; +import { player, stats, zoom } from '../data/state'; /** * Represents a single planetary orbit @@ -117,6 +117,12 @@ export class Orbit implements Drawable { public getNextNSpeed(n: number): number { return SMath.translate(SMath.clamp(this.data.speedLevel + n, 0, this.maxSpeedLevel), 0, this.maxSpeedLevel, 0, this.maxSpeed); } + /** + * Get the next ascension exponent after `N` ascensions. + */ + public getNextNAscensionExp(n: number): number { + return (this.data.ascensions + n) / 100 + 1; + } /** * Increase the speed by `N` levels. */ From b64acf2cff569db5f61b4029b14ca5cadd649cf9 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:20:37 -0700 Subject: [PATCH 28/66] new ascension button to show progress --- www/games/orbit-idle/src/form-controls/button-asc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/form-controls/button-asc.ts b/www/games/orbit-idle/src/form-controls/button-asc.ts index deee129..6d218cc 100644 --- a/www/games/orbit-idle/src/form-controls/button-asc.ts +++ b/www/games/orbit-idle/src/form-controls/button-asc.ts @@ -39,7 +39,7 @@ export class AscendButton extends Button { } // Calculate and display cost this.ascAmount = this.orbit.getNextNAscensionCost(this.ascensions); - super.text = `Ascend +${this.ascensions}\n$${N(this.ascAmount)}`; + super.text = `^${N(this.orbit.getExp())} > ^${N(this.orbit.getNextNAscensionExp(this.ascensions))}\n$${N(this.ascAmount)}`; // Check if player has sufficient funds to ascend if (player.hasFunds(this.ascAmount)) { super.enable(); From c1af0a3f8d9a97d652e866a09b83d85a821d28bd Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:21:12 -0700 Subject: [PATCH 29/66] track orbits completed and ascensions purchased --- www/games/orbit-idle/src/game/orbit.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/games/orbit-idle/src/game/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts index 9c4f7ea..1a5ba47 100644 --- a/www/games/orbit-idle/src/game/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -143,6 +143,7 @@ export class Orbit implements Drawable { this.data.ascensions += n | 0; this.data.angle = 0; this.data.speedLevel = 0; + stats.stats.ascensionsPurchased += n | 0; } } /** @@ -165,6 +166,7 @@ export class Orbit implements Drawable { const rotations: number = (this.data.angle / Orbit.TAU) | 0; this.data.rotations += rotations; this.data.angle %= Orbit.TAU; + stats.stats.orbitsCompleted += rotations; return rotations; } public draw(graphics: CanvasRenderingContext2D): void { From b9918b21736a907e9992c41b1b47768f01bd0122 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:22:53 -0700 Subject: [PATCH 30/66] track speed levels purchased --- www/games/orbit-idle/src/game/orbit.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/game/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts index 1a5ba47..b909699 100644 --- a/www/games/orbit-idle/src/game/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -127,7 +127,9 @@ export class Orbit implements Drawable { * Increase the speed by `N` levels. */ public increaseSpeed(n: number): void { - this.data.speedLevel = SMath.clamp(this.data.speedLevel + n, 0, this.maxSpeedLevel) | 0; + const upgradeAmount: number = SMath.clamp(n, 0, this.maxSpeedLevel - this.data.speedLevel) | 0; + this.data.speedLevel += upgradeAmount; + stats.stats.speedLevelsPurchased += upgradeAmount; } /** * Calculates the current speed in rotations per second. (Hz) From 9aa10781f9eb482506ed58d051b80f1a51d622d9 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:34:48 -0700 Subject: [PATCH 31/66] show stats --- www/games/orbit-idle/src/data/stats.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index 9b4e365..e6e2ebb 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -2,6 +2,8 @@ import { Drawable } from 'graphico'; import { Label } from '../form-controls/label'; import { Color } from 'viridis'; import { Button } from '../form-controls/button'; +import { player } from './state'; +import { N } from './lib'; /** * Represents an in-game statistics panel @@ -19,8 +21,8 @@ export class Statistics implements Drawable { constructor(public stats: Stats = defaultStats) { this.visible = false; this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 14); - this.headers = new Label('Test', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); - this.content = new Label('Test', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 15); + this.headers = new Label('Started on\nSolar system size\nTotal earned\nTotal spent\nTotal planetary years\nSpeed levels purchased\nAscensions purchased\n\nCredits\n\n\nDisclaimer', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); + this.content = new Label('', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 15); this.close = new Button('x', new Color(255, 100, 100), true, Label.fontSize / 2, Label.fontSize / 2, Label.fontSize * 2, Label.fontSize * 2, null, () => this.hide()); } /** @@ -52,14 +54,20 @@ export class Statistics implements Drawable { } } public draw(graphics: CanvasRenderingContext2D): void { + // Skip if not visible if (!this.visible) { return; } + // Draw background shading graphics.fillStyle = Statistics.shade.toString(); graphics.fillRect(0, 0, graphics.canvas.width, graphics.canvas.height); + // Set content + this.content.value = `${new Date(this.stats.startTime).toLocaleString()}\n${N(player.getNumOrbits())} planets\n$${N(this.stats.moneyEarned)}\n$${N(this.stats.moneySpent)}\n${N(this.stats.orbitsCompleted)} orbits\n${N(this.stats.speedLevelsPurchased)}\n${N(this.stats.ascensionsPurchased)}\n\nNicolas Ventura\nnicfv.com\n\nGenerative AI was used for\n30-second looping audio but\nnot for game design or code`; + // X-center all labels this.title.x = graphics.canvas.width / 2; this.headers.x = (graphics.canvas.width - Label.fontSize) / 2; this.content.x = (graphics.canvas.width + Label.fontSize) / 2; + // Draw all labels and close button this.title.draw(graphics); this.headers.draw(graphics); this.content.draw(graphics); @@ -71,6 +79,7 @@ export class Statistics implements Drawable { * Interface containing various in-game statistics */ export interface Stats { + startTime: number; moneyEarned: number; moneySpent: number; orbitsCompleted: number; @@ -79,6 +88,7 @@ export interface Stats { } const defaultStats: Stats = { + startTime: 0, moneyEarned: 0, moneySpent: 0, orbitsCompleted: 0, From e5822a31ad43bdb5881ee6c689c680f2e90fcf42 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:46:24 -0700 Subject: [PATCH 32/66] hide is public --- www/games/orbit-idle/src/data/stats.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index e6e2ebb..dd551d8 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -34,7 +34,7 @@ export class Statistics implements Drawable { /** * Hide the statistics panel */ - private hide(): void { + public hide(): void { this.visible = false; } /** From f26062538740ee5e43952d6ab450f178a5f155af Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:46:40 -0700 Subject: [PATCH 33/66] create stats button --- www/games/orbit-idle/src/ui/menu.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/www/games/orbit-idle/src/ui/menu.ts b/www/games/orbit-idle/src/ui/menu.ts index 3e6022a..a6e5249 100644 --- a/www/games/orbit-idle/src/ui/menu.ts +++ b/www/games/orbit-idle/src/ui/menu.ts @@ -2,7 +2,7 @@ import { Drawable } from 'graphico'; import { Color } from 'viridis'; import { Label } from '../form-controls/label'; import { ToggleButton } from '../form-controls/button-toggle'; -import { tutorial, version } from '../data/state'; +import { stats, tutorial, version } from '../data/state'; /** * Represents the in-game menu @@ -48,6 +48,10 @@ export class Menu implements Drawable { * Mute button */ private readonly mute: ToggleButton; + /** + * Stats button + */ + private readonly stats: ToggleButton; /** * Clear data button */ @@ -114,15 +118,19 @@ export class Menu implements Drawable { ['Mute', Menu.btnColor, () => this.muteCallback()], ['Unmute', Menu.btnColor, () => this.unmuteCallback()], ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 2, width, Menu.btnHeight, ['m']); + this.stats = new ToggleButton([ + ['Show Stats', Menu.btnColor, () => stats.show()], + ['Close Stats', Menu.btnColor, () => stats.hide()], + ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 3, width, Menu.btnHeight, null); this.clear = new ToggleButton([ ['Wipe Data', Menu.btnColor, () => this.clearReset = Menu.buttonResetDuration], ['Confirm', new Color(255, 0, 0), () => this.clearCallback()], ['Reload Page', Menu.btnColor, () => window.location.reload()], - ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 3, width, Menu.btnHeight, null); + ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 4, width, Menu.btnHeight, null); this.back = new ToggleButton([ ['Menu', Menu.btnColor, () => this.toggle()], ['Back', Menu.btnColor, () => this.toggle()], - ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 4, width, Menu.btnHeight, ['escape']); + ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 5, width, Menu.btnHeight, ['escape']); } /** * Set all callbacks, needed from the main canvas @@ -187,6 +195,7 @@ export class Menu implements Drawable { this.save.checkHover(x, y); this.help.checkHover(x, y); this.mute.checkHover(x, y); + this.stats.checkHover(x, y); this.clear.checkHover(x, y); } this.back.checkHover(x, y); @@ -199,6 +208,7 @@ export class Menu implements Drawable { this.save.click(button); this.help.click(button); this.mute.click(button); + this.stats.click(button); this.clear.click(button); } this.back.click(button); @@ -209,6 +219,7 @@ export class Menu implements Drawable { this.save.draw(graphics); this.help.draw(graphics); this.mute.draw(graphics); + this.stats.draw(graphics); this.clear.draw(graphics); } this.back.draw(graphics); From dd10d75c23ed82b92818b7e599fedf4414383be5 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 17:46:51 -0700 Subject: [PATCH 34/66] move menu up a bit --- www/games/orbit-idle/src/data/state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/state.ts b/www/games/orbit-idle/src/data/state.ts index b332e15..3698a34 100644 --- a/www/games/orbit-idle/src/data/state.ts +++ b/www/games/orbit-idle/src/data/state.ts @@ -29,5 +29,5 @@ export const shop: Shop = new Shop(10, 120); // Additional popups export const tutorial: Tutorial = new Tutorial(); -export const menu: Menu = new Menu(725, 400, 100); +export const menu: Menu = new Menu(725, 365, 100); export const paused: Pause = new Pause('Orbit Idle has lost focus!\nThe game is NOT paused, but has\nstopped rendering to save resources.\nThe game is still running in the background.'); From 797d3c8dea92b7ec9a619c036f283936e7013edf Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:01:08 -0700 Subject: [PATCH 35/66] create saveload abstract --- www/games/orbit-idle/src/data/lib.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/www/games/orbit-idle/src/data/lib.ts b/www/games/orbit-idle/src/data/lib.ts index d50b0cb..0071d40 100644 --- a/www/games/orbit-idle/src/data/lib.ts +++ b/www/games/orbit-idle/src/data/lib.ts @@ -31,3 +31,23 @@ export function N(num: number): string { return num.toFixed(2); } }; + +/** + * Represents a data type which can be saved and loaded from memory + */ +export abstract class SaveLoad { + /** + * Create a new instance of this class + */ + constructor(protected data: T) { } + /** + * Make a copy of this data object for saving + */ + public save(): T { + return JSON.parse(JSON.stringify(this.data)); + } + /** + * Load the saved data into this object + */ + public abstract load(data: T): void; +} From 9decb8a5311349fb34e927bfcc3c348f3b9e2303 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:01:22 -0700 Subject: [PATCH 36/66] extend that --- www/games/orbit-idle/src/data/stats.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index dd551d8..23e3473 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -3,12 +3,13 @@ import { Label } from '../form-controls/label'; import { Color } from 'viridis'; import { Button } from '../form-controls/button'; import { player } from './state'; -import { N } from './lib'; +import { N, SaveLoad } from './lib'; +import { SMath } from 'smath'; /** * Represents an in-game statistics panel */ -export class Statistics implements Drawable { +export class Statistics extends SaveLoad implements Drawable { private static readonly shade: Color = new Color(0, 0, 0, 50); private visible: boolean; private readonly title: Label; @@ -18,13 +19,22 @@ export class Statistics implements Drawable { /** * Create a new statistics window */ - constructor(public stats: Stats = defaultStats) { + constructor(stats: StatsData = defaultStats) { + super(stats); this.visible = false; this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 14); this.headers = new Label('Started on\nSolar system size\nTotal earned\nTotal spent\nTotal planetary years\nSpeed levels purchased\nAscensions purchased\n\nCredits\n\n\nDisclaimer', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); this.content = new Label('', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 15); this.close = new Button('x', new Color(255, 100, 100), true, Label.fontSize / 2, Label.fontSize / 2, Label.fontSize * 2, Label.fontSize * 2, null, () => this.hide()); } + public load(data: StatsData): void { + super.data.startTime = SMath.clamp(data.startTime, 0, Infinity) | 0; + super.data.moneyEarned = SMath.clamp(data.moneyEarned, 0, Infinity); + super.data.moneySpent = SMath.clamp(data.moneySpent, 0, Infinity); + super.data.orbitsCompleted = SMath.clamp(data.orbitsCompleted, 0, Infinity) | 0; + super.data.speedLevelsPurchased = SMath.clamp(data.speedLevelsPurchased, 0, Infinity) | 0; + super.data.ascensionsPurchased = SMath.clamp(data.ascensionsPurchased, 0, Infinity) | 0; + } /** * Show the statistics panel */ @@ -62,7 +72,7 @@ export class Statistics implements Drawable { graphics.fillStyle = Statistics.shade.toString(); graphics.fillRect(0, 0, graphics.canvas.width, graphics.canvas.height); // Set content - this.content.value = `${new Date(this.stats.startTime).toLocaleString()}\n${N(player.getNumOrbits())} planets\n$${N(this.stats.moneyEarned)}\n$${N(this.stats.moneySpent)}\n${N(this.stats.orbitsCompleted)} orbits\n${N(this.stats.speedLevelsPurchased)}\n${N(this.stats.ascensionsPurchased)}\n\nNicolas Ventura\nnicfv.com\n\nGenerative AI was used for\n30-second looping audio but\nnot for game design or code`; + this.content.value = `${new Date(super.data.startTime).toLocaleString()}\n${N(player.getNumOrbits())} planets\n$${N(super.data.moneyEarned)}\n$${N(super.data.moneySpent)}\n${N(super.data.orbitsCompleted)} orbits\n${N(super.data.speedLevelsPurchased)}\n${N(super.data.ascensionsPurchased)}\n\nNicolas Ventura\nnicfv.com\n\nGenerative AI was used for\n30-second looping audio but\nnot for game design or code`; // X-center all labels this.title.x = graphics.canvas.width / 2; this.headers.x = (graphics.canvas.width - Label.fontSize) / 2; @@ -78,7 +88,7 @@ export class Statistics implements Drawable { /** * Interface containing various in-game statistics */ -export interface Stats { +export interface StatsData { startTime: number; moneyEarned: number; moneySpent: number; @@ -87,7 +97,7 @@ export interface Stats { ascensionsPurchased: number; } -const defaultStats: Stats = { +const defaultStats: StatsData = { startTime: 0, moneyEarned: 0, moneySpent: 0, From b863613f29e61378aa138239c3393c06b1a6a53d Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:01:40 -0700 Subject: [PATCH 37/66] statsdata should be saved/loaded --- www/games/orbit-idle/src/data/gamedata.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/games/orbit-idle/src/data/gamedata.ts b/www/games/orbit-idle/src/data/gamedata.ts index 1248035..c2a1606 100644 --- a/www/games/orbit-idle/src/data/gamedata.ts +++ b/www/games/orbit-idle/src/data/gamedata.ts @@ -1,5 +1,6 @@ import { PlayerData } from '../game/player'; import { SolarData } from '../game/solar-system'; +import { StatsData } from './stats'; /** * Represents and interface for saving and loading game data @@ -8,4 +9,5 @@ export interface GameData { readonly timestamp: number; readonly playerData: PlayerData; readonly solarData: SolarData; + readonly statsData: StatsData; }; From 40fe95068f0ed143e233b260ba94ad0e22a5f1eb Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:02:46 -0700 Subject: [PATCH 38/66] readonly --- www/games/orbit-idle/src/data/lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/lib.ts b/www/games/orbit-idle/src/data/lib.ts index 0071d40..8114a8a 100644 --- a/www/games/orbit-idle/src/data/lib.ts +++ b/www/games/orbit-idle/src/data/lib.ts @@ -39,7 +39,7 @@ export abstract class SaveLoad { /** * Create a new instance of this class */ - constructor(protected data: T) { } + constructor(protected readonly data: T) { } /** * Make a copy of this data object for saving */ From 985cfe952c374fa960ae58bcf053e22a1593f3be Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:46:03 -0700 Subject: [PATCH 39/66] make data public --- www/games/orbit-idle/src/data/stats.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index 23e3473..0012b15 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -19,8 +19,8 @@ export class Statistics extends SaveLoad implements Drawable { /** * Create a new statistics window */ - constructor(stats: StatsData = defaultStats) { - super(stats); + constructor(public readonly data: StatsData = defaultStats) { + super(data); this.visible = false; this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 14); this.headers = new Label('Started on\nSolar system size\nTotal earned\nTotal spent\nTotal planetary years\nSpeed levels purchased\nAscensions purchased\n\nCredits\n\n\nDisclaimer', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); From c465a24b57752e0ad8b80f4cda693c206b554908 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:46:23 -0700 Subject: [PATCH 40/66] extend saveload --- www/games/orbit-idle/src/game/player.ts | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/www/games/orbit-idle/src/game/player.ts b/www/games/orbit-idle/src/game/player.ts index 49282f9..cabbb2e 100644 --- a/www/games/orbit-idle/src/game/player.ts +++ b/www/games/orbit-idle/src/game/player.ts @@ -1,5 +1,5 @@ import { Drawable } from 'graphico'; -import { N } from '../data/lib'; +import { N, SaveLoad } from '../data/lib'; import { Label } from '../form-controls/label'; import { Color } from 'viridis'; import { SMath } from 'smath'; @@ -8,7 +8,7 @@ import { stats } from '../data/state'; /** * Represents the main player of the game */ -export class Player implements Drawable { +export class Player extends SaveLoad implements Drawable { /** * The label showing the amount of money in the player's bank */ @@ -16,7 +16,8 @@ export class Player implements Drawable { /** * Create a new instance of the game player */ - constructor(private readonly data: PlayerData = defaultPlayerData) { + constructor(data: PlayerData = defaultPlayerData) { + super(data); this.moneyLabel = new Label('', new Color(255, 255, 255), 3, true, 'center', 'bottom', 0, 0); } /** @@ -24,14 +25,14 @@ export class Player implements Drawable { */ public earn(amount: number): void { this.data.money += amount; - stats.stats.moneyEarned += amount; + stats.data.moneyEarned += amount; } /** * Spend a certain amount of money from the player's bank */ public spend(amount: number): void { this.data.money -= amount; - stats.stats.moneySpent += amount; + stats.data.moneySpent += amount; } /** * Determine if the player has sufficient funds to purchase something of a certain amount @@ -58,15 +59,6 @@ export class Player implements Drawable { this.data.money = 0; this.data.orbits++; } - /** - * Copy player data for saving - */ - public save(): PlayerData { - return JSON.parse(JSON.stringify(this.data)); - } - /** - * Load data for the player - */ public load(data: PlayerData = defaultPlayerData): void { this.data.money = SMath.clamp(data.money, 0, Infinity); this.data.orbits = SMath.clamp(data.orbits, 2, Infinity) | 0; From e3cd4f33ce750608e17d7f418257267d0066b43d Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:48:29 -0700 Subject: [PATCH 41/66] simplify constructor, remove defaults --- www/games/orbit-idle/src/game/player.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/www/games/orbit-idle/src/game/player.ts b/www/games/orbit-idle/src/game/player.ts index cabbb2e..c9d286a 100644 --- a/www/games/orbit-idle/src/game/player.ts +++ b/www/games/orbit-idle/src/game/player.ts @@ -16,8 +16,11 @@ export class Player extends SaveLoad implements Drawable { /** * Create a new instance of the game player */ - constructor(data: PlayerData = defaultPlayerData) { - super(data); + constructor() { + super({ + money: 0, + orbits: 2, + }); this.moneyLabel = new Label('', new Color(255, 255, 255), 3, true, 'center', 'bottom', 0, 0); } /** @@ -59,7 +62,7 @@ export class Player extends SaveLoad implements Drawable { this.data.money = 0; this.data.orbits++; } - public load(data: PlayerData = defaultPlayerData): void { + public load(data: PlayerData): void { this.data.money = SMath.clamp(data.money, 0, Infinity); this.data.orbits = SMath.clamp(data.orbits, 2, Infinity) | 0; } @@ -82,8 +85,3 @@ export interface PlayerData { money: number; orbits: number; } - -const defaultPlayerData: PlayerData = { - money: 0, - orbits: 2, -}; From 418f440adb37e897ca159403b7b7f087f512f3de Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:49:36 -0700 Subject: [PATCH 42/66] extend saveload, simplify constructor, remove defaults --- www/games/orbit-idle/src/game/orbit.ts | 33 ++++++++++---------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/www/games/orbit-idle/src/game/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts index b909699..707565a 100644 --- a/www/games/orbit-idle/src/game/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -2,11 +2,12 @@ import { Drawable } from 'graphico'; import { SMath, Vec3 } from 'smath'; import { Color } from 'viridis'; import { player, stats, zoom } from '../data/state'; +import { SaveLoad } from '../data/lib'; /** * Represents a single planetary orbit */ -export class Orbit implements Drawable { +export class Orbit extends SaveLoad implements Drawable { /** * 2pi */ @@ -46,7 +47,13 @@ export class Orbit implements Drawable { /** * Create a new planet. */ - constructor(public readonly index: number, private readonly data: OrbitData = JSON.parse(JSON.stringify(defaultData))) { + constructor(public readonly index: number) { + super({ + angle: 0, + ascensions: 0, + rotations: 0, + speedLevel: 0, + }); this.color = Color.hsl(SMath.translate(index, 0, player.getNumOrbits(), 0, 360), 100, 55); this.radius = 100 * (1.3 ** index); this.thickness = this.radius * 0.2; @@ -56,15 +63,6 @@ export class Orbit implements Drawable { this.costIncrease = 1.02 + 0.02 * index; this.ascCostIncrease = 2.25 + 0.05 * index; } - /** - * Copy orbit data for saving this orbit. - */ - public save(): OrbitData { - return JSON.parse(JSON.stringify(this.data)); - } - /** - * Load orbit data into this orbit. - */ public load(data: OrbitData): void { this.data.angle = SMath.clamp(data.angle, 0, Orbit.TAU); this.data.ascensions = SMath.clamp(data.ascensions, 0, Infinity) | 0; @@ -129,7 +127,7 @@ export class Orbit implements Drawable { public increaseSpeed(n: number): void { const upgradeAmount: number = SMath.clamp(n, 0, this.maxSpeedLevel - this.data.speedLevel) | 0; this.data.speedLevel += upgradeAmount; - stats.stats.speedLevelsPurchased += upgradeAmount; + stats.data.speedLevelsPurchased += upgradeAmount; } /** * Calculates the current speed in rotations per second. (Hz) @@ -145,7 +143,7 @@ export class Orbit implements Drawable { this.data.ascensions += n | 0; this.data.angle = 0; this.data.speedLevel = 0; - stats.stats.ascensionsPurchased += n | 0; + stats.data.ascensionsPurchased += n | 0; } } /** @@ -168,7 +166,7 @@ export class Orbit implements Drawable { const rotations: number = (this.data.angle / Orbit.TAU) | 0; this.data.rotations += rotations; this.data.angle %= Orbit.TAU; - stats.stats.orbitsCompleted += rotations; + stats.data.orbitsCompleted += rotations; return rotations; } public draw(graphics: CanvasRenderingContext2D): void { @@ -210,10 +208,3 @@ export interface OrbitData { */ speedLevel: number; } - -const defaultData: OrbitData = { - angle: 0, - rotations: 0, - ascensions: 0, - speedLevel: 0, -}; From 81fe9e25f0aa24368cb7d6c2abb0186b64952d09 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:50:44 -0700 Subject: [PATCH 43/66] oops, i guess i deleted too much --- www/games/orbit-idle/src/game/orbit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/game/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts index 707565a..7afa91f 100644 --- a/www/games/orbit-idle/src/game/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -47,8 +47,8 @@ export class Orbit extends SaveLoad implements Drawable { /** * Create a new planet. */ - constructor(public readonly index: number) { - super({ + constructor(public readonly index: number, data?: OrbitData) { + super(data ?? { angle: 0, ascensions: 0, rotations: 0, From d193d88f87823ab5011831abc19712af50567328 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:51:06 -0700 Subject: [PATCH 44/66] extend saveload --- www/games/orbit-idle/src/game/solar-system.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/game/solar-system.ts b/www/games/orbit-idle/src/game/solar-system.ts index c77ac7f..48d3a04 100644 --- a/www/games/orbit-idle/src/game/solar-system.ts +++ b/www/games/orbit-idle/src/game/solar-system.ts @@ -3,11 +3,12 @@ import { Orbit, OrbitData } from './orbit'; import { income, player, zoom } from '../data/state'; import { Color, Gradient } from 'viridis'; import { Vec3 } from 'smath'; +import { SaveLoad } from '../data/lib'; /** * Represents the solar system in the game */ -export class SolarSystem implements Drawable { +export class SolarSystem extends SaveLoad implements Drawable { /** * The gradient for the main central star */ @@ -23,12 +24,13 @@ export class SolarSystem implements Drawable { * Create a new solar system, passing in optional orbit data */ constructor() { + super([]); this.orbits = []; } /** * Generate data for all orbits in the solar system for saving data */ - public save(): SolarData { + public override save(): SolarData { return this.orbits.map(o => o.save()); } /** From 4a785a970c7681684c6843fb94ed74cf2c667abd Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:53:27 -0700 Subject: [PATCH 45/66] load should be flexible --- www/games/orbit-idle/src/data/lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/lib.ts b/www/games/orbit-idle/src/data/lib.ts index 8114a8a..4ef7a47 100644 --- a/www/games/orbit-idle/src/data/lib.ts +++ b/www/games/orbit-idle/src/data/lib.ts @@ -49,5 +49,5 @@ export abstract class SaveLoad { /** * Load the saved data into this object */ - public abstract load(data: T): void; + public abstract load(data?: T): void; } From 30d5c70113bc172996b20f3aeb3b6a21f3d10077 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:58:37 -0700 Subject: [PATCH 46/66] should be partial --- www/games/orbit-idle/src/data/lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/lib.ts b/www/games/orbit-idle/src/data/lib.ts index 4ef7a47..c93e57d 100644 --- a/www/games/orbit-idle/src/data/lib.ts +++ b/www/games/orbit-idle/src/data/lib.ts @@ -49,5 +49,5 @@ export abstract class SaveLoad { /** * Load the saved data into this object */ - public abstract load(data?: T): void; + public abstract load(data?: Partial): void; } From d63d2047ed83c4efa759903c6c5ced9fc833762a Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:59:16 -0700 Subject: [PATCH 47/66] properly extend class --- www/games/orbit-idle/src/data/stats.ts | 14 +++++++------- www/games/orbit-idle/src/game/orbit.ts | 24 +++++++++++++----------- www/games/orbit-idle/src/game/player.ts | 16 +++++++++------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index 0012b15..7f44259 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -27,13 +27,13 @@ export class Statistics extends SaveLoad implements Drawable { this.content = new Label('', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 15); this.close = new Button('x', new Color(255, 100, 100), true, Label.fontSize / 2, Label.fontSize / 2, Label.fontSize * 2, Label.fontSize * 2, null, () => this.hide()); } - public load(data: StatsData): void { - super.data.startTime = SMath.clamp(data.startTime, 0, Infinity) | 0; - super.data.moneyEarned = SMath.clamp(data.moneyEarned, 0, Infinity); - super.data.moneySpent = SMath.clamp(data.moneySpent, 0, Infinity); - super.data.orbitsCompleted = SMath.clamp(data.orbitsCompleted, 0, Infinity) | 0; - super.data.speedLevelsPurchased = SMath.clamp(data.speedLevelsPurchased, 0, Infinity) | 0; - super.data.ascensionsPurchased = SMath.clamp(data.ascensionsPurchased, 0, Infinity) | 0; + public load(data: Partial = defaultStats): void { + super.data.startTime = SMath.clamp(data.startTime ?? defaultStats.startTime, 0, Infinity) | 0; + super.data.moneyEarned = SMath.clamp(data.moneyEarned ?? defaultStats.moneyEarned, 0, Infinity); + super.data.moneySpent = SMath.clamp(data.moneySpent ?? defaultStats.moneySpent, 0, Infinity); + super.data.orbitsCompleted = SMath.clamp(data.orbitsCompleted ?? defaultStats.orbitsCompleted, 0, Infinity) | 0; + super.data.speedLevelsPurchased = SMath.clamp(data.speedLevelsPurchased ?? defaultStats.speedLevelsPurchased, 0, Infinity) | 0; + super.data.ascensionsPurchased = SMath.clamp(data.ascensionsPurchased ?? defaultStats.ascensionsPurchased, 0, Infinity) | 0; } /** * Show the statistics panel diff --git a/www/games/orbit-idle/src/game/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts index 7afa91f..2f26d8c 100644 --- a/www/games/orbit-idle/src/game/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -48,12 +48,7 @@ export class Orbit extends SaveLoad implements Drawable { * Create a new planet. */ constructor(public readonly index: number, data?: OrbitData) { - super(data ?? { - angle: 0, - ascensions: 0, - rotations: 0, - speedLevel: 0, - }); + super(data ?? defaultOrbitData); this.color = Color.hsl(SMath.translate(index, 0, player.getNumOrbits(), 0, 360), 100, 55); this.radius = 100 * (1.3 ** index); this.thickness = this.radius * 0.2; @@ -63,11 +58,11 @@ export class Orbit extends SaveLoad implements Drawable { this.costIncrease = 1.02 + 0.02 * index; this.ascCostIncrease = 2.25 + 0.05 * index; } - public load(data: OrbitData): void { - this.data.angle = SMath.clamp(data.angle, 0, Orbit.TAU); - this.data.ascensions = SMath.clamp(data.ascensions, 0, Infinity) | 0; - this.data.rotations = SMath.clamp(data.rotations, 0, Infinity) | 0; - this.data.speedLevel = SMath.clamp(data.speedLevel, 0, Infinity) | 0; + public load(data: Partial = defaultOrbitData): void { + this.data.angle = SMath.clamp(data.angle ?? defaultOrbitData.angle, 0, Orbit.TAU); + this.data.ascensions = SMath.clamp(data.ascensions ?? defaultOrbitData.ascensions, 0, Infinity) | 0; + this.data.rotations = SMath.clamp(data.rotations ?? defaultOrbitData.rotations, 0, Infinity) | 0; + this.data.speedLevel = SMath.clamp(data.speedLevel ?? defaultOrbitData.speedLevel, 0, Infinity) | 0; } /** * Determine if the orbit has been activated. @@ -208,3 +203,10 @@ export interface OrbitData { */ speedLevel: number; } + +const defaultOrbitData: OrbitData = { + angle: 0, + ascensions: 0, + rotations: 0, + speedLevel: 0, +}; diff --git a/www/games/orbit-idle/src/game/player.ts b/www/games/orbit-idle/src/game/player.ts index c9d286a..7099b34 100644 --- a/www/games/orbit-idle/src/game/player.ts +++ b/www/games/orbit-idle/src/game/player.ts @@ -17,10 +17,7 @@ export class Player extends SaveLoad implements Drawable { * Create a new instance of the game player */ constructor() { - super({ - money: 0, - orbits: 2, - }); + super(defaultPlayerData); this.moneyLabel = new Label('', new Color(255, 255, 255), 3, true, 'center', 'bottom', 0, 0); } /** @@ -62,9 +59,9 @@ export class Player extends SaveLoad implements Drawable { this.data.money = 0; this.data.orbits++; } - public load(data: PlayerData): void { - this.data.money = SMath.clamp(data.money, 0, Infinity); - this.data.orbits = SMath.clamp(data.orbits, 2, Infinity) | 0; + public load(data: Partial = defaultPlayerData): void { + this.data.money = SMath.clamp(data.money ?? defaultPlayerData.money, 0, Infinity); + this.data.orbits = SMath.clamp(data.orbits ?? defaultPlayerData.orbits, 2, Infinity) | 0; } public draw(graphics: CanvasRenderingContext2D): void { // Render black background @@ -85,3 +82,8 @@ export interface PlayerData { money: number; orbits: number; } + +const defaultPlayerData: PlayerData = { + money: 0, + orbits: 2, +}; From 6e546b7089abd57ba55f0dba99503677851351fd Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 18:59:28 -0700 Subject: [PATCH 48/66] save and load stats --- www/games/orbit-idle/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/games/orbit-idle/src/index.ts b/www/games/orbit-idle/src/index.ts index 8104ae9..6b0b848 100644 --- a/www/games/orbit-idle/src/index.ts +++ b/www/games/orbit-idle/src/index.ts @@ -79,6 +79,7 @@ menu.setCallbacks(() => { timestamp: Date.now(), playerData: player.save(), solarData: system.save(), + statsData: stats.save(), }); }, () => canv.mute(), () => canv.unmute(), () => canv.clearData()); @@ -87,6 +88,7 @@ if (data) { // Load and parse saved data player.load(data.playerData); system.load(data.solarData); + stats.load(data.statsData); const dt: number = Date.now() - (data.timestamp ?? Date.now()); system.step(dt); // Reset zoom and regenerate UI From c25a0b8da43f212667cb81c2e15a031d49f795f1 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:07:46 -0700 Subject: [PATCH 49/66] super -> this --- www/games/orbit-idle/src/data/stats.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index 7f44259..ec62515 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -28,12 +28,12 @@ export class Statistics extends SaveLoad implements Drawable { this.close = new Button('x', new Color(255, 100, 100), true, Label.fontSize / 2, Label.fontSize / 2, Label.fontSize * 2, Label.fontSize * 2, null, () => this.hide()); } public load(data: Partial = defaultStats): void { - super.data.startTime = SMath.clamp(data.startTime ?? defaultStats.startTime, 0, Infinity) | 0; - super.data.moneyEarned = SMath.clamp(data.moneyEarned ?? defaultStats.moneyEarned, 0, Infinity); - super.data.moneySpent = SMath.clamp(data.moneySpent ?? defaultStats.moneySpent, 0, Infinity); - super.data.orbitsCompleted = SMath.clamp(data.orbitsCompleted ?? defaultStats.orbitsCompleted, 0, Infinity) | 0; - super.data.speedLevelsPurchased = SMath.clamp(data.speedLevelsPurchased ?? defaultStats.speedLevelsPurchased, 0, Infinity) | 0; - super.data.ascensionsPurchased = SMath.clamp(data.ascensionsPurchased ?? defaultStats.ascensionsPurchased, 0, Infinity) | 0; + this.data.startTime = SMath.clamp(data.startTime ?? defaultStats.startTime, 0, Infinity) | 0; + this.data.moneyEarned = SMath.clamp(data.moneyEarned ?? defaultStats.moneyEarned, 0, Infinity); + this.data.moneySpent = SMath.clamp(data.moneySpent ?? defaultStats.moneySpent, 0, Infinity); + this.data.orbitsCompleted = SMath.clamp(data.orbitsCompleted ?? defaultStats.orbitsCompleted, 0, Infinity) | 0; + this.data.speedLevelsPurchased = SMath.clamp(data.speedLevelsPurchased ?? defaultStats.speedLevelsPurchased, 0, Infinity) | 0; + this.data.ascensionsPurchased = SMath.clamp(data.ascensionsPurchased ?? defaultStats.ascensionsPurchased, 0, Infinity) | 0; } /** * Show the statistics panel @@ -72,7 +72,7 @@ export class Statistics extends SaveLoad implements Drawable { graphics.fillStyle = Statistics.shade.toString(); graphics.fillRect(0, 0, graphics.canvas.width, graphics.canvas.height); // Set content - this.content.value = `${new Date(super.data.startTime).toLocaleString()}\n${N(player.getNumOrbits())} planets\n$${N(super.data.moneyEarned)}\n$${N(super.data.moneySpent)}\n${N(super.data.orbitsCompleted)} orbits\n${N(super.data.speedLevelsPurchased)}\n${N(super.data.ascensionsPurchased)}\n\nNicolas Ventura\nnicfv.com\n\nGenerative AI was used for\n30-second looping audio but\nnot for game design or code`; + this.content.value = `${new Date(this.data.startTime).toLocaleString()}\n${N(player.getNumOrbits())} planets\n$${N(this.data.moneyEarned)}\n$${N(this.data.moneySpent)}\n${N(this.data.orbitsCompleted)} orbits\n${N(this.data.speedLevelsPurchased)}\n${N(this.data.ascensionsPurchased)}\n\nNicolas Ventura\nnicfv.com\n\nGenerative AI was used for\n30-second looping audio but\nnot for game design or code`; // X-center all labels this.title.x = graphics.canvas.width / 2; this.headers.x = (graphics.canvas.width - Label.fontSize) / 2; @@ -98,7 +98,7 @@ export interface StatsData { } const defaultStats: StatsData = { - startTime: 0, + startTime: Date.now(), moneyEarned: 0, moneySpent: 0, orbitsCompleted: 0, From c51b2f15fab2bf9ffabb9af2d9ac566418617760 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:14:25 -0700 Subject: [PATCH 50/66] constructor duplicates data --- www/games/orbit-idle/src/data/lib.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/lib.ts b/www/games/orbit-idle/src/data/lib.ts index c93e57d..964fb69 100644 --- a/www/games/orbit-idle/src/data/lib.ts +++ b/www/games/orbit-idle/src/data/lib.ts @@ -39,7 +39,9 @@ export abstract class SaveLoad { /** * Create a new instance of this class */ - constructor(protected readonly data: T) { } + constructor(protected readonly data: T) { + this.data = JSON.parse(JSON.stringify(data)); + } /** * Make a copy of this data object for saving */ From 1ee5487fb0d8be226af596c0c527d8efa5a4ed3c Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:17:49 -0700 Subject: [PATCH 51/66] typecheck --- cmd/start | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/start b/cmd/start index b307561..580a162 100755 --- a/cmd/start +++ b/cmd/start @@ -11,4 +11,4 @@ else fi # Lint and build project -bun x eslint src && bun build "${args[@]}" --outfile=dist/main.js src/index.ts +bun x tsc src/index.ts --noEmit --ignoreConfig && bun x eslint src && bun build "${args[@]}" --outfile=dist/main.js src/index.ts From 8966478ea3753ac05aac4b93ff28aeca2fe476d1 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:20:57 -0700 Subject: [PATCH 52/66] positioning, remove "x" button --- www/games/orbit-idle/src/data/stats.ts | 30 +++++--------------------- www/games/orbit-idle/src/index.ts | 2 -- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index ec62515..e175636 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -1,7 +1,6 @@ import { Drawable } from 'graphico'; import { Label } from '../form-controls/label'; import { Color } from 'viridis'; -import { Button } from '../form-controls/button'; import { player } from './state'; import { N, SaveLoad } from './lib'; import { SMath } from 'smath'; @@ -10,22 +9,20 @@ import { SMath } from 'smath'; * Represents an in-game statistics panel */ export class Statistics extends SaveLoad implements Drawable { - private static readonly shade: Color = new Color(0, 0, 0, 50); + private static readonly shade: Color = new Color(25, 25, 25, 75); private visible: boolean; private readonly title: Label; private readonly headers: Label; private readonly content: Label; - private readonly close: Button; /** * Create a new statistics window */ constructor(public readonly data: StatsData = defaultStats) { super(data); this.visible = false; - this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 14); - this.headers = new Label('Started on\nSolar system size\nTotal earned\nTotal spent\nTotal planetary years\nSpeed levels purchased\nAscensions purchased\n\nCredits\n\n\nDisclaimer', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 15); - this.content = new Label('', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 15); - this.close = new Button('x', new Color(255, 100, 100), true, Label.fontSize / 2, Label.fontSize / 2, Label.fontSize * 2, Label.fontSize * 2, null, () => this.hide()); + this.title = new Label('Orbit Idle: Statistics', new Color(255, 255, 255), 2, true, 'center', 'bottom', 0, Label.fontSize * 16); + this.headers = new Label('Started on\nSolar system size\nTotal earned\nTotal spent\nTotal planetary years\nSpeed levels purchased\nAscensions purchased\n\nCredits\n\n\nDisclaimer', new Color(255, 255, 255), 1, false, 'right', 'top', 0, Label.fontSize * 18); + this.content = new Label('', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 18); } public load(data: Partial = defaultStats): void { this.data.startTime = SMath.clamp(data.startTime ?? defaultStats.startTime, 0, Infinity) | 0; @@ -47,22 +44,6 @@ export class Statistics extends SaveLoad implements Drawable { public hide(): void { this.visible = false; } - /** - * Check to see if the mouse is hovering over the close button - */ - public checkHover(x: number, y: number): void { - if (this.visible) { - this.close.checkHover(x, y); - } - } - /** - * Check to see if the user clicked the close button - */ - public click(button: number): void { - if (this.visible) { - this.close.click(button); - } - } public draw(graphics: CanvasRenderingContext2D): void { // Skip if not visible if (!this.visible) { @@ -70,7 +51,7 @@ export class Statistics extends SaveLoad implements Drawable { } // Draw background shading graphics.fillStyle = Statistics.shade.toString(); - graphics.fillRect(0, 0, graphics.canvas.width, graphics.canvas.height); + graphics.fillRect(Label.fontSize * 15, Label.fontSize * 10, graphics.canvas.width - Label.fontSize * 30, graphics.canvas.height - Label.fontSize * 20); // Set content this.content.value = `${new Date(this.data.startTime).toLocaleString()}\n${N(player.getNumOrbits())} planets\n$${N(this.data.moneyEarned)}\n$${N(this.data.moneySpent)}\n${N(this.data.orbitsCompleted)} orbits\n${N(this.data.speedLevelsPurchased)}\n${N(this.data.ascensionsPurchased)}\n\nNicolas Ventura\nnicfv.com\n\nGenerative AI was used for\n30-second looping audio but\nnot for game design or code`; // X-center all labels @@ -81,7 +62,6 @@ export class Statistics extends SaveLoad implements Drawable { this.title.draw(graphics); this.headers.draw(graphics); this.content.draw(graphics); - this.close.draw(graphics); } } diff --git a/www/games/orbit-idle/src/index.ts b/www/games/orbit-idle/src/index.ts index 6b0b848..d7c6197 100644 --- a/www/games/orbit-idle/src/index.ts +++ b/www/games/orbit-idle/src/index.ts @@ -46,7 +46,6 @@ const canv: Canvas = new Canvas({ shop.checkHover(x, y); ascBtn.checkHover(x, y); menu.checkHover(x, y); - stats.checkHover(x, y); }, mousedown(button) { tutorial.click(button); @@ -55,7 +54,6 @@ const canv: Canvas = new Canvas({ shop.click(button); ascBtn.click(button); menu.click(button); - stats.click(button); if (!audio) { audio = true; canv.playAudio('./Between_The_Sleepless_Stars.mp3', true, 0.25); From b5bebf495a13bf7362675e21b1eeef1e606fa0a6 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:24:28 -0700 Subject: [PATCH 53/66] shift a few items up --- www/games/orbit-idle/src/data/state.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/games/orbit-idle/src/data/state.ts b/www/games/orbit-idle/src/data/state.ts index 3698a34..cbc2a64 100644 --- a/www/games/orbit-idle/src/data/state.ts +++ b/www/games/orbit-idle/src/data/state.ts @@ -17,9 +17,9 @@ export const version = 'v1.2.1-alpha'; // UI elements export const cursor: Cursor = new Cursor(); export const player: Player = new Player(); -export const zoom: Zoom = new Zoom(725, 175); -export const buyType: BuyType = new BuyType(725, 250); -export const ascBtn: FullAscendButton = new FullAscendButton(675, 300, 100, 50); +export const zoom: Zoom = new Zoom(725, 150); +export const buyType: BuyType = new BuyType(725, 225); +export const ascBtn: FullAscendButton = new FullAscendButton(675, 275, 100, 50); export const stats: Statistics = new Statistics(); // Game elements From f79cfe5ef0ba7bd3e13c75e45db82987706fd753 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:27:30 -0700 Subject: [PATCH 54/66] fix tutorial positioning --- www/games/orbit-idle/src/form-controls/tutorial.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/www/games/orbit-idle/src/form-controls/tutorial.ts b/www/games/orbit-idle/src/form-controls/tutorial.ts index d4dcc7a..2a9eb7a 100644 --- a/www/games/orbit-idle/src/form-controls/tutorial.ts +++ b/www/games/orbit-idle/src/form-controls/tutorial.ts @@ -42,10 +42,10 @@ export class Tutorial implements Drawable { new Help('When an orbit reaches its max speed, you\ncan Ascend it. Ascensions are expensive\nand reset speed for that orbit, but\nincrease your long-term income.', 140, 0, 135, 100, 130, 400, () => this.next()), new Help('The breakdown of your income is shown here.\nEach planet gains +0.01 bonus each planetary year\nby completing a full revolution around the sun.\nBonuses from individual orbits are multiplied\ntogether and raised to their ascension level.', 0, 50, 5, 5, 790, 45, () => this.next()), new Help('This is your total income and\ntotal revolutions per second.\nEach time any planet completes\none full revolution, you earn\nthe income under $/rev.', -10, 0, 600, 50, 200, 80, () => this.next()), - new Help('Use the Zoom controls on the top right to focus\non different planets. Zooming changes the store\nbuttons and income labels to match the focused\norbit. Hotkeys: + and -', -10, 0, 675, 150, 100, 60, () => this.next()), - new Help('For convenience, you can buy\nspeed upgrades in bulk. It\ndoes not affect ascending.', -10, 0, 660, 220, 130, 60, () => this.next()), - new Help('When you have make the required income\nper second, you will be given the option\nto perform a full ascension. This resets\nthe entire solar system, but adds\nan additional planet.', -10, 0, 660, 290, 130, 70, () => this.next()), - new Help('Open Menu > Tutorial anytime to review these slides.\nYour progress saves automatically, and the game\ncontinues earning even when the tab is not active.\nClick "End Tutorial" to close this message.\nGood luck!\n\nHotkeys: (S)ave, (H)elp, (M)ute, (ESC) open/close menu', -10, 75, 660, 375, 130, 175, () => this.next()), + new Help('Use the Zoom controls on the top right to focus\non different planets. Zooming changes the store\nbuttons and income labels to match the focused\norbit. Hotkeys: + and -', -10, 0, 675, 125, 100, 60, () => this.next()), + new Help('For convenience, you can buy\nspeed upgrades in bulk. It\ndoes not affect ascending.', -10, 0, 660, 195, 130, 60, () => this.next()), + new Help('When you have make the required income\nper second, you will be given the option\nto perform a full ascension. This resets\nthe entire solar system, but adds\nan additional planet.', -10, 0, 660, 265, 130, 70, () => this.next()), + new Help('Open Menu > Tutorial anytime to review these slides.\nYour progress saves automatically, and the game\ncontinues earning even when the tab is not active.\nClick "End Tutorial" to close this message.\nGood luck!\n\nHotkeys: (S)ave, (H)elp, (M)ute, (ESC) open/close menu', -10, 75, 660, 335, 130, 215, () => this.next()), ]; } /** From f3ce6066c4bdb349b0533b3a0771fd9313f8c22e Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:30:29 -0700 Subject: [PATCH 55/66] add hotkey "A" for stats --- www/games/orbit-idle/src/ui/menu.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/ui/menu.ts b/www/games/orbit-idle/src/ui/menu.ts index a6e5249..c9f548d 100644 --- a/www/games/orbit-idle/src/ui/menu.ts +++ b/www/games/orbit-idle/src/ui/menu.ts @@ -121,7 +121,7 @@ export class Menu implements Drawable { this.stats = new ToggleButton([ ['Show Stats', Menu.btnColor, () => stats.show()], ['Close Stats', Menu.btnColor, () => stats.hide()], - ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 3, width, Menu.btnHeight, null); + ], x - (width / 2), y + (Menu.btnHeight + Menu.btnPadding) * 3, width, Menu.btnHeight, ['a']); this.clear = new ToggleButton([ ['Wipe Data', Menu.btnColor, () => this.clearReset = Menu.buttonResetDuration], ['Confirm', new Color(255, 0, 0), () => this.clearCallback()], @@ -157,6 +157,7 @@ export class Menu implements Drawable { this.help.hotkey(key); this.mute.hotkey(key); this.back.hotkey(key); + this.stats.hotkey(key); } /** * Menu tick for resetting buttons and autosaving From d77b9959c9a5744cbc18db9e5c290e010c0400e4 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:30:59 -0700 Subject: [PATCH 56/66] write hotkey --- www/games/orbit-idle/src/form-controls/tutorial.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/form-controls/tutorial.ts b/www/games/orbit-idle/src/form-controls/tutorial.ts index 2a9eb7a..73177fc 100644 --- a/www/games/orbit-idle/src/form-controls/tutorial.ts +++ b/www/games/orbit-idle/src/form-controls/tutorial.ts @@ -45,7 +45,7 @@ export class Tutorial implements Drawable { new Help('Use the Zoom controls on the top right to focus\non different planets. Zooming changes the store\nbuttons and income labels to match the focused\norbit. Hotkeys: + and -', -10, 0, 675, 125, 100, 60, () => this.next()), new Help('For convenience, you can buy\nspeed upgrades in bulk. It\ndoes not affect ascending.', -10, 0, 660, 195, 130, 60, () => this.next()), new Help('When you have make the required income\nper second, you will be given the option\nto perform a full ascension. This resets\nthe entire solar system, but adds\nan additional planet.', -10, 0, 660, 265, 130, 70, () => this.next()), - new Help('Open Menu > Tutorial anytime to review these slides.\nYour progress saves automatically, and the game\ncontinues earning even when the tab is not active.\nClick "End Tutorial" to close this message.\nGood luck!\n\nHotkeys: (S)ave, (H)elp, (M)ute, (ESC) open/close menu', -10, 75, 660, 335, 130, 215, () => this.next()), + new Help('Open Menu > Tutorial anytime to review these slides.\nYour progress saves automatically, and the game\ncontinues earning even when the tab is not active.\nClick "End Tutorial" to close this message.\nGood luck!\n\nHotkeys: (S)ave, (H)elp, (M)ute, St(A)ts, (ESC) open/close menu', -10, 75, 660, 335, 130, 215, () => this.next()), ]; } /** From cf905c64deb46d8394c481676552ae6674bf31e7 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:31:07 -0700 Subject: [PATCH 57/66] remove console log --- www/games/orbit-idle/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/www/games/orbit-idle/src/index.ts b/www/games/orbit-idle/src/index.ts index d7c6197..f841c98 100644 --- a/www/games/orbit-idle/src/index.ts +++ b/www/games/orbit-idle/src/index.ts @@ -60,7 +60,6 @@ const canv: Canvas = new Canvas({ } }, keydown(key) { - console.log(key); menu.checkHotkeys(key); zoom.checkHotkeys(key); }, From 8bb621efe75665a992f947ad1e9b55ed6943d760 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:32:43 -0700 Subject: [PATCH 58/66] remove |0 in player --- www/games/orbit-idle/src/game/player.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/orbit-idle/src/game/player.ts b/www/games/orbit-idle/src/game/player.ts index 7099b34..3cd2b4e 100644 --- a/www/games/orbit-idle/src/game/player.ts +++ b/www/games/orbit-idle/src/game/player.ts @@ -44,7 +44,7 @@ export class Player extends SaveLoad implements Drawable { * Calculate the income required to ascend (gain another planet/orbit) */ public incomeRequired(): number { - return 10 ** ((1.25 * (this.data.orbits ** 1.75) + 1) | 0); + return 10 ** Math.floor(1.25 * (this.data.orbits ** 1.75) + 1); } /** * Get the total number of orbits @@ -61,7 +61,7 @@ export class Player extends SaveLoad implements Drawable { } public load(data: Partial = defaultPlayerData): void { this.data.money = SMath.clamp(data.money ?? defaultPlayerData.money, 0, Infinity); - this.data.orbits = SMath.clamp(data.orbits ?? defaultPlayerData.orbits, 2, Infinity) | 0; + this.data.orbits = Math.floor(SMath.clamp(data.orbits ?? defaultPlayerData.orbits, 2, Infinity)); } public draw(graphics: CanvasRenderingContext2D): void { // Render black background From f16c888a195054b735a3a70cf92cc24ad1497878 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:34:34 -0700 Subject: [PATCH 59/66] change risky |0 to math.floor --- www/games/orbit-idle/src/data/stats.ts | 8 ++++---- www/games/orbit-idle/src/game/orbit.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/www/games/orbit-idle/src/data/stats.ts b/www/games/orbit-idle/src/data/stats.ts index e175636..2d83f55 100644 --- a/www/games/orbit-idle/src/data/stats.ts +++ b/www/games/orbit-idle/src/data/stats.ts @@ -25,12 +25,12 @@ export class Statistics extends SaveLoad implements Drawable { this.content = new Label('', new Color(255, 255, 255), 1, true, 'left', 'top', 0, Label.fontSize * 18); } public load(data: Partial = defaultStats): void { - this.data.startTime = SMath.clamp(data.startTime ?? defaultStats.startTime, 0, Infinity) | 0; + this.data.startTime = Math.floor(SMath.clamp(data.startTime ?? defaultStats.startTime, 0, Infinity)); this.data.moneyEarned = SMath.clamp(data.moneyEarned ?? defaultStats.moneyEarned, 0, Infinity); this.data.moneySpent = SMath.clamp(data.moneySpent ?? defaultStats.moneySpent, 0, Infinity); - this.data.orbitsCompleted = SMath.clamp(data.orbitsCompleted ?? defaultStats.orbitsCompleted, 0, Infinity) | 0; - this.data.speedLevelsPurchased = SMath.clamp(data.speedLevelsPurchased ?? defaultStats.speedLevelsPurchased, 0, Infinity) | 0; - this.data.ascensionsPurchased = SMath.clamp(data.ascensionsPurchased ?? defaultStats.ascensionsPurchased, 0, Infinity) | 0; + this.data.orbitsCompleted = Math.floor(SMath.clamp(data.orbitsCompleted ?? defaultStats.orbitsCompleted, 0, Infinity)); + this.data.speedLevelsPurchased = Math.floor(SMath.clamp(data.speedLevelsPurchased ?? defaultStats.speedLevelsPurchased, 0, Infinity)); + this.data.ascensionsPurchased = Math.floor(SMath.clamp(data.ascensionsPurchased ?? defaultStats.ascensionsPurchased, 0, Infinity)); } /** * Show the statistics panel diff --git a/www/games/orbit-idle/src/game/orbit.ts b/www/games/orbit-idle/src/game/orbit.ts index 2f26d8c..d01788d 100644 --- a/www/games/orbit-idle/src/game/orbit.ts +++ b/www/games/orbit-idle/src/game/orbit.ts @@ -60,9 +60,9 @@ export class Orbit extends SaveLoad implements Drawable { } public load(data: Partial = defaultOrbitData): void { this.data.angle = SMath.clamp(data.angle ?? defaultOrbitData.angle, 0, Orbit.TAU); - this.data.ascensions = SMath.clamp(data.ascensions ?? defaultOrbitData.ascensions, 0, Infinity) | 0; - this.data.rotations = SMath.clamp(data.rotations ?? defaultOrbitData.rotations, 0, Infinity) | 0; - this.data.speedLevel = SMath.clamp(data.speedLevel ?? defaultOrbitData.speedLevel, 0, Infinity) | 0; + this.data.ascensions = Math.floor(SMath.clamp(data.ascensions ?? defaultOrbitData.ascensions, 0, Infinity)); + this.data.rotations = Math.floor(SMath.clamp(data.rotations ?? defaultOrbitData.rotations, 0, Infinity)); + this.data.speedLevel = Math.floor(SMath.clamp(data.speedLevel ?? defaultOrbitData.speedLevel, 0, Infinity)); } /** * Determine if the orbit has been activated. @@ -158,7 +158,7 @@ export class Orbit extends SaveLoad implements Drawable { */ public rotate(dt: number): number { this.data.angle += this.currentSpeedHz() * dt / 1e3 * Orbit.TAU; - const rotations: number = (this.data.angle / Orbit.TAU) | 0; + const rotations: number = Math.floor(this.data.angle / Orbit.TAU); this.data.rotations += rotations; this.data.angle %= Orbit.TAU; stats.data.orbitsCompleted += rotations; From cfdf49ab9512df58dc9722380153f328bf38a336 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 19:38:12 -0700 Subject: [PATCH 60/66] fallback on unitless --- www/tools/dimensional/src/program.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/tools/dimensional/src/program.ts b/www/tools/dimensional/src/program.ts index dbbc722..a0721e5 100644 --- a/www/tools/dimensional/src/program.ts +++ b/www/tools/dimensional/src/program.ts @@ -36,7 +36,7 @@ export function getUnitNames(): string[] { * Find the unit matching the selected name */ export function getUnitByName(name: string): Unit { - return allUnits.find(nu => nu[0] === name)?.[1]; + return allUnits.find(nu => nu[0] === name)?.[1] ?? units.Unitless; } /** * Swap the input and output of a pair From f3686d53913cd967b0d58a494030de849e794eaf Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 21:59:56 -0700 Subject: [PATCH 61/66] typecheck (better) --- cmd/start | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/start b/cmd/start index 580a162..1929da7 100755 --- a/cmd/start +++ b/cmd/start @@ -11,4 +11,4 @@ else fi # Lint and build project -bun x tsc src/index.ts --noEmit --ignoreConfig && bun x eslint src && bun build "${args[@]}" --outfile=dist/main.js src/index.ts +bun x tsc --noEmit && bun x eslint src && bun build "${args[@]}" --outfile=dist/main.js src/index.ts From 8318a38bc8089e6a6c914ca6fa77b4cc6b599c2c Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 22:00:15 -0700 Subject: [PATCH 62/66] migrate all custom tsconfig rules to root --- tsconfig.json | 1 + www/games/geomarin/tsconfig.json | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 www/games/geomarin/tsconfig.json diff --git a/tsconfig.json b/tsconfig.json index 876b35d..ab75db4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,5 +5,6 @@ "DOM", "ESNext", ], + "module": "esnext", }, } \ No newline at end of file diff --git a/www/games/geomarin/tsconfig.json b/www/games/geomarin/tsconfig.json deleted file mode 100644 index 9ec768c..0000000 --- a/www/games/geomarin/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "module": "esnext", - }, -} \ No newline at end of file From 2501088d7bf7098ce314dc1e14394f64d9a534c0 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 22:04:59 -0700 Subject: [PATCH 63/66] update deps --- www/games/geomarin/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/games/geomarin/package.json b/www/games/geomarin/package.json index 3250766..6176a95 100644 --- a/www/games/geomarin/package.json +++ b/www/games/geomarin/package.json @@ -4,7 +4,7 @@ "start": "../../../cmd/start" }, "dependencies": { - "graphico": "1.3.0", - "smath": "2.2.0" + "graphico": "1.6.0", + "smath": "3.0.0" } } \ No newline at end of file From f2e24349a4a6d6c33ee54e91edbdb18d2e8929a9 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 22:05:27 -0700 Subject: [PATCH 64/66] |0 to bar dimensions --- www/games/geomarin/src/lib/bar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/geomarin/src/lib/bar.ts b/www/games/geomarin/src/lib/bar.ts index 0e08efe..7bbbe75 100644 --- a/www/games/geomarin/src/lib/bar.ts +++ b/www/games/geomarin/src/lib/bar.ts @@ -15,7 +15,7 @@ export class Bar implements Drawable { const barWidth: number = this.value / this.maxValue * maxWidth; // Draw the bar background ctx.fillStyle = this.color; - ctx.fillRect(this.thickness * 1.5, yOffset - this.thickness / 2, barWidth + this.thickness * 0.5, this.thickness); + ctx.fillRect((this.thickness * 1.5) | 0, (yOffset - this.thickness / 2) | 0, (barWidth + this.thickness * 0.5) | 0, this.thickness | 0); // Draw the bar name and value ctx.font = `${this.thickness}px sans-serif`; ctx.fillStyle = 'white'; From cccd009fe974c7ecebc4bc663053edf0653ae9c1 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 22:05:31 -0700 Subject: [PATCH 65/66] v1.1 --- www/games/geomarin/src/globals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/geomarin/src/globals.ts b/www/games/geomarin/src/globals.ts index 3ac135e..0e66978 100644 --- a/www/games/geomarin/src/globals.ts +++ b/www/games/geomarin/src/globals.ts @@ -5,7 +5,7 @@ export const global = { /** * Application version number */ - version: '1.0', + version: '1.1', /** * Colors for SVG paths (regions) */ From da3f701539d84a6e07c6b2bfa75b3051d077ee29 Mon Sep 17 00:00:00 2001 From: nicfv Date: Fri, 7 Aug 2026 22:07:12 -0700 Subject: [PATCH 66/66] v1.3 --- www/games/orbit-idle/src/data/state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/games/orbit-idle/src/data/state.ts b/www/games/orbit-idle/src/data/state.ts index cbc2a64..be68c6b 100644 --- a/www/games/orbit-idle/src/data/state.ts +++ b/www/games/orbit-idle/src/data/state.ts @@ -12,7 +12,7 @@ import { Tutorial } from '../form-controls/tutorial'; import { Zoom } from '../ui/zoom'; // Global constants -export const version = 'v1.2.1-alpha'; +export const version = 'v1.3.0-alpha'; // UI elements export const cursor: Cursor = new Cursor();