Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/creature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,30 @@ export class Creature {
});
}
game.UI.btnDelay.changeState('disabled');

// Determine animation: use 'fly' for Gumble's leap (2+ hexes when ability upgraded)
let animation: string = args.creature.movementType() === 'flying' ? 'fly' : 'walk';
if (animation === 'walk') {
// Check if Gumble can leap over units for this movement
const isGumbleLeaping =
args.creature.type === 14 &&
args.creature.abilities[0] &&
args.creature.abilities[0].isUpgraded();
if (isGumbleLeaping) {
// Use hex distance to determine if this is a 2+ hex leap
// Simple offset coord distance (conservative estimate)
const d = Math.max(
Math.abs(hex.x - args.creature.x),
Math.abs(hex.y - args.creature.y),
);
if (d >= 2) {
animation = 'fly';
}
}
}

args.creature.moveTo(hex, {
animation: args.creature.movementType() === 'flying' ? 'fly' : 'walk',
animation: animation,
callback: function () {
game.activeCreature.queryMove();
},
Expand Down
2 changes: 1 addition & 1 deletion src/data/units.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1367,7 +1367,7 @@ export const unitData: UnitDataStructure = [
title: 'Gooey Body',
desc: 'When killed, it melts into a puddle that traps any unit that walks on top of it.',
info: "Can't move current round while on goo.",
upgrade: 'Does not affect allied units.',
upgrade: 'Can leap over units when moving 2+ hexes.',
},
{
title: 'Gummy Mallet',
Expand Down
3 changes: 3 additions & 0 deletions src/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
15 changes: 15 additions & 0 deletions src/ui/fullscreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

Expand All @@ -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');
Expand Down
8 changes: 4 additions & 4 deletions src/ui/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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')
Expand Down
38 changes: 37 additions & 1 deletion src/utility/hex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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
Expand Down
44 changes: 43 additions & 1 deletion src/utility/hexgrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1495,7 +1495,28 @@ export class HexGrid {
});
}

/**
* Compute hex distance between two hexes in the offset coordinate system.
* Converts to cube coordinates for the calculation.
*/
private hexDistance(hex1: { x: number; y: number }, hex2: { x: number; y: number }): number {
// Convert offset coordinates (odd-q) to cube coordinates
const offsetToCube = (x: number, y: number) => {
const cubeX = x - (y - (y & 1)) / 2;
const cubeZ = y;
const cubeY = -cubeX - cubeZ;
return { x: cubeX, y: cubeY, z: cubeZ };
};
const c1 = offsetToCube(hex1.x, hex1.y);
const c2 = offsetToCube(hex2.x, hex2.y);
return Math.max(Math.abs(c1.x - c2.x), Math.abs(c1.y - c2.y), Math.abs(c1.z - c2.z));
}

findCreatureMovementHexes(creature) {
// Gumble (type 14) with upgraded ability 0 can leap over units when moving 2+ hexes
const isGumbleWithLeapUpgrade =
creature.type === 14 && creature.abilities[0] && creature.abilities[0].isUpgraded();

if (creature.movementType() === 'flying') {
return this.getFlyingRange(
creature.x,
Expand All @@ -1505,13 +1526,34 @@ export class HexGrid {
creature.id,
);
} else {
return this.getMovementRange(
const hexes = this.getMovementRange(
creature.x,
creature.y,
creature.stats.movement,
creature.size,
creature.id,
);

// Add flying hexes (2+ hexes away) for Gumble's leap upgrade
if (isGumbleWithLeapUpgrade) {
const flyingHexes = this.getFlyingRange(
creature.x,
creature.y,
creature.stats.movement,
creature.size,
creature.id,
);
// Only include flying hexes that are at least 2 hexes away
const leapHexes = flyingHexes.filter((hex) => this.hexDistance(creature, hex) >= 2);
// Merge with normal hexes, avoiding duplicates
for (const hex of leapHexes) {
if (!hexes.some((h) => h.x === hex.x && h.y === hex.y)) {
hexes.push(hex);
}
}
}

return hexes;
}
}

Expand Down
Loading