diff --git a/src/script.ts b/src/script.ts index aae50221b..a56bd1463 100644 --- a/src/script.ts +++ b/src/script.ts @@ -96,6 +96,9 @@ $j(() => { const fullscreen = new Fullscreen(document.getElementById('fullscreen')); $j('#fullscreen').on('click', () => fullscreen.toggle()); + // Attempt to lock orientation to landscape on page load (works without fullscreen on some browsers) + Fullscreen.lockLandscapeOrientation(); + const startScreenHotkeys = { Space: { keyDownTest() { diff --git a/src/ui/fullscreen.ts b/src/ui/fullscreen.ts index aad819ce1..7c4c7d6b2 100644 --- a/src/ui/fullscreen.ts +++ b/src/ui/fullscreen.ts @@ -20,6 +20,8 @@ export class Fullscreen { const gameElement = document.getElementById('AncientBeast'); if (gameElement) { await gameElement.requestFullscreen(); + // Lock to landscape after entering fullscreen + Fullscreen.lockLandscapeOrientation(); } } @@ -29,6 +31,19 @@ export class Fullscreen { } } + /** + * Attempts to lock the screen orientation to landscape. + * Most browsers require fullscreen to be active before locking orientation. + */ + static lockLandscapeOrientation() { + const screenOrientation = screen as Screen & { orientation?: ScreenOrientation }; + if (screenOrientation.orientation && 'lock' in screenOrientation.orientation) { + (screenOrientation.orientation as ScreenOrientation).lock('landscape').catch((err) => { + console.warn('Could not lock screen orientation:', err); + }); + } + } + updateButtonState() { if (document.fullscreenElement) { this.button.classList.add('fullscreenMode'); diff --git a/src/ui/interface.ts b/src/ui/interface.ts index 14453859e..7783947bf 100644 --- a/src/ui/interface.ts +++ b/src/ui/interface.ts @@ -1071,12 +1071,12 @@ export class UI { .children('#upgrade') .text('Upgrade: ' + stats.ability_info[key].upgrade); - if (stats.ability_info[key].costs !== undefined && key !== 0) { + if (typeof stats.ability_info[key].costs?.energy === 'number' && key !== 0) { $ability .children('.wrapper') .children('.info') .children('#cost') - .text(' - costs ' + stats.ability_info[key].costs.energy + ' energy pts.'); + .text(' - costs ' + stats.ability_info[key].costs!.energy + ' energy pts.'); } else { $ability .children('.wrapper') @@ -1260,12 +1260,12 @@ export class UI { $ability.children('.wrapper').children('.info').children('#upgrade').text(' '); } - if (stats.ability_info[key].costs !== undefined && key !== 0) { + if (typeof stats.ability_info[key].costs?.energy === 'number' && key !== 0) { $ability .children('.wrapper') .children('.info') .children('#cost') - .text(' - costs ' + stats.ability_info[key].costs.energy + ' energy pts.'); + .text(' - costs ' + stats.ability_info[key].costs!.energy + ' energy pts.'); } else { $ability .children('.wrapper') diff --git a/src/utility/hex.ts b/src/utility/hex.ts index 1e3d3bd29..2cbabe838 100644 --- a/src/utility/hex.ts +++ b/src/utility/hex.ts @@ -180,10 +180,23 @@ export class Hex { this.overlay = grid.overlayHexesGroup.create(x, y, 'hex'); this.overlay.alpha = 0; + // Track if last interaction was from touch to avoid double-firing + let lastInteractionTouch = false; + // Binding Events - this.hitBox.events.onInputOver.add(() => { + this.hitBox.events.onInputOver.add((_, pointer) => { if (game.freezedInput || game.UI.dashopen) return; + // Skip hover effect for touch input - on Android, touch triggers + // onInputOver before onInputUp, making users tap twice to act. + // Touch input is handled directly in onInputDown instead. + if (pointer && pointer.pointerType === 'touch') { + lastInteractionTouch = true; + return; + } + + lastInteractionTouch = false; + // Show dashed overlay on current hexes of active creature if (this.reachable && game.activeCreature) { game.activeCreature.highlightCurrentHexesAsDashed(); @@ -194,9 +207,27 @@ export class Hex { this.onSelectFn(this); }, this); + // onInputDown: handle touch directly to avoid double-tap issue on Android + this.hitBox.events.onInputDown.add((_, pointer) => { + if (game.freezedInput || game.UI.dashopen) return; + + // For touch input, immediately trigger the confirm action. + // This bypasses the hover-first behavior that requires double-tapping on Android. + if (pointer && pointer.pointerType === 'touch') { + lastInteractionTouch = true; + this.onConfirmFn(this); + } + }, this); + this.hitBox.events.onInputOut.add((_, pointer) => { if (game.freezedInput || game.UI.dashopen || !pointer.withinGame) return; + // Skip hover-off for touch since we skip hover-on for touch + if (lastInteractionTouch) { + lastInteractionTouch = false; + return; + } + // Clear dashed overlay when leaving a reachable hex if (this.reachable && game.activeCreature) { game.activeCreature.clearDashedOverlayOnHexes(); @@ -212,6 +243,11 @@ export class Hex { return; } + // Skip onInputUp for touch since we already handled it in onInputDown + if (Pointer.pointerType === 'touch') { + return; + } + switch (Pointer.button) { case 0: // Left mouse button pressed