/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Obstacle: acid pool (hazard) var Acid = Container.expand(function () { var self = Container.call(this); var acid = self.attachAsset('acid', { anchorX: 0.5, anchorY: 0.5 }); // Acid can move left/right for challenge self.speed = 0; self.direction = 1; self.update = function () { if (self.speed !== 0) { self.x += self.speed * self.direction; if (self.x < 200 || self.x > 2048 - 200) { self.direction *= -1; } } }; return self; }); // Animal base class var Animal = Container.expand(function () { var self = Container.call(this); // These will be set by subclasses self.assetId = ''; self.moveType = 'normal'; // 'normal', 'hop', 'slide', 'float', etc. self.speed = 12; // Default speed self.jumpPower = 0; // For animals that can jump self.gravity = 0; // For animals affected by gravity self.vy = 0; // Vertical velocity // Attach animal asset var animalSprite = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5 }); // Used for per-animal movement logic self.update = function () { // Movement logic handled in main game loop }; // Called when switching to this animal self.onSwitchIn = function () { // Flash or animate to show selection tween(animalSprite, { scaleX: 1.2, scaleY: 1.2 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(animalSprite, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeIn }); } }); }; // Called when switching away from this animal self.onSwitchOut = function () { // No-op for now }; return self; }); // Snail: very slow, can stick to walls var Snail = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'snail'; self.speed = 4; return self; }); // Polar Bear: slow, immune to acid var PolarBear = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'polarBear'; self.speed = 7; return self; }); // Pig: slow, can push food chunks var Pig = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'pig'; self.speed = 8; return self; }); // Frog: can hop var Frog = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'frog'; self.speed = 10; self.jumpPower = 48; self.gravity = 3.5; self.isJumping = false; return self; }); // Elephant: can push obstacles, slowest var Elephant = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'elephant'; self.speed = 6; return self; }); // Cat: fast, can jump var Cat = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'cat'; self.speed = 16; self.jumpPower = 38; self.gravity = 3.2; self.isJumping = false; return self; }); // Bird: can fly (ignores gravity) var Bird = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'bird'; self.speed = 13; self.moveType = 'fly'; return self; }); // Bee: can float over acid, moves in short bursts var Bee = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'bee'; self.speed = 10; self.moveType = 'float'; return self; }); // Axolotl: can swim in acid, normal speed var Axolotl = Animal.expand(function () { var self = Animal.call(this); self.assetId = 'axolotl'; self.speed = 11; self.moveType = 'swim'; return self; }); // Obstacle: food chunk (movable) var FoodChunk = Container.expand(function () { var self = Container.call(this); var chunk = self.attachAsset('foodChunk', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () {}; return self; }); // Obstacle: stomach wall (static) var StomachWall = Container.expand(function () { var self = Container.call(this); var wall = self.attachAsset('stomachWall', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Touch button for animal switching var SwitchButton = Container.expand(function () { var self = Container.call(this); var btn = self.attachAsset('switchBtn', { anchorX: 0.5, anchorY: 0.5 }); self.icon = null; // Will be set to animal icon return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d1b0c // Deep brown, snake belly }); /**** * Game Code ****/ // Touch button shapes // Obstacle shapes // Animal shapes (unique color for each animal) // --- Game constants --- var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var PLAY_AREA_TOP = 200; var PLAY_AREA_BOTTOM = GAME_HEIGHT - 200; var PLAY_AREA_LEFT = 100; var PLAY_AREA_RIGHT = GAME_WIDTH - 100; // --- Animal roster and order --- var animalClasses = [Pig, Cat, Bee, Frog, PolarBear, Elephant, Bird, Snail, Axolotl]; var animalNames = ['Pig', 'Cat', 'Bee', 'Frog', 'Polar Bear', 'Elephant', 'Bird', 'Snail', 'Axolotl']; var animalIcons = ['pig', 'cat', 'bee', 'frog', 'polarBear', 'elephant', 'bird', 'snail', 'axolotl']; var animals = []; var currentAnimalIdx = 0; var currentAnimal = null; // --- Obstacles and hazards --- var obstacles = []; var acids = []; var foodChunks = []; // --- UI --- var switchButtons = []; var switchBtnY = GAME_HEIGHT - 180; var switchBtnSpacing = 200; var infoText = null; // --- Dragging --- var dragNode = null; var dragOffsetX = 0; var dragOffsetY = 0; // --- Game state --- var isMovingLeft = false; var isMovingRight = false; var isMovingUp = false; var isMovingDown = false; var isJumping = false; var isSwitching = false; var gameStarted = false; var gameOver = false; var youWin = false; // --- Level layout (simple for MVP) --- function setupLevel() { // Clear previous for (var i = 0; i < obstacles.length; ++i) obstacles[i].destroy(); for (var i = 0; i < acids.length; ++i) acids[i].destroy(); for (var i = 0; i < foodChunks.length; ++i) foodChunks[i].destroy(); obstacles = []; acids = []; foodChunks = []; // Top and bottom stomach walls var wallTop = new StomachWall(); wallTop.x = GAME_WIDTH / 2; wallTop.y = PLAY_AREA_TOP - 40; obstacles.push(wallTop); game.addChild(wallTop); var wallBottom = new StomachWall(); wallBottom.x = GAME_WIDTH / 2; wallBottom.y = PLAY_AREA_BOTTOM + 40; obstacles.push(wallBottom); game.addChild(wallBottom); // Acid pools (hazards) var acid1 = new Acid(); acid1.x = GAME_WIDTH / 2; acid1.y = PLAY_AREA_TOP + 400; acid1.speed = 6; acids.push(acid1); game.addChild(acid1); var acid2 = new Acid(); acid2.x = GAME_WIDTH / 2 + 400; acid2.y = PLAY_AREA_TOP + 900; acid2.speed = 0; acids.push(acid2); game.addChild(acid2); // Food chunks (movable obstacles) var chunk1 = new FoodChunk(); chunk1.x = GAME_WIDTH / 2 - 300; chunk1.y = PLAY_AREA_TOP + 700; foodChunks.push(chunk1); game.addChild(chunk1); var chunk2 = new FoodChunk(); chunk2.x = GAME_WIDTH / 2 + 300; chunk2.y = PLAY_AREA_TOP + 1200; foodChunks.push(chunk2); game.addChild(chunk2); // More obstacles can be added for future levels } // --- Animal setup --- function setupAnimals() { // Remove previous for (var i = 0; i < animals.length; ++i) animals[i].destroy(); animals = []; // Place all animals at start, stacked var startX = GAME_WIDTH / 2; var startY = PLAY_AREA_BOTTOM - 200; for (var i = 0; i < animalClasses.length; ++i) { var animal = new animalClasses[i](); animal.x = startX + (i - 4) * 40; animal.y = startY - i * 40; animal.zIndex = i; animal.visible = false; animals.push(animal); game.addChild(animal); } // Set first animal active currentAnimalIdx = 0; currentAnimal = animals[currentAnimalIdx]; currentAnimal.visible = true; currentAnimal.onSwitchIn(); } // --- UI setup --- function setupUI() { // Remove previous for (var i = 0; i < switchButtons.length; ++i) switchButtons[i].destroy(); switchButtons = []; // Create switch buttons for each animal var btnStartX = GAME_WIDTH / 2 - (animalClasses.length - 1) / 2 * switchBtnSpacing; for (var i = 0; i < animalClasses.length; ++i) { var btn = new SwitchButton(); btn.x = btnStartX + i * switchBtnSpacing; btn.y = switchBtnY; btn.animalIdx = i; // Add animal icon var icon = btn.attachAsset(animalIcons[i], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); btn.icon = icon; // Highlight current if (i === currentAnimalIdx) { icon.scaleX = icon.scaleY = 0.7; } // Touch event btn.down = function (x, y, obj) { if (isSwitching) return; var idx = this.animalIdx; if (idx !== currentAnimalIdx) { switchAnimal(idx); } }; LK.gui.bottom.addChild(btn); switchButtons.push(btn); } // Info text if (infoText) infoText.destroy(); infoText = new Text2('Help the animals escape!\nTap an animal to switch.\nDrag to move. Avoid acid!', { size: 70, fill: "#fff", align: "center" }); infoText.anchor.set(0.5, 0); LK.gui.top.addChild(infoText); } // --- Animal switching --- function switchAnimal(idx) { if (idx < 0 || idx >= animals.length) return; if (idx === currentAnimalIdx) return; isSwitching = true; // Animate out old currentAnimal.onSwitchOut(); currentAnimal.visible = false; // Animate in new currentAnimalIdx = idx; currentAnimal = animals[currentAnimalIdx]; currentAnimal.visible = true; currentAnimal.onSwitchIn(); // Highlight button for (var i = 0; i < switchButtons.length; ++i) { var icon = switchButtons[i].icon; if (i === currentAnimalIdx) { tween(icon, { scaleX: 0.7, scaleY: 0.7 }, { duration: 120 }); } else { tween(icon, { scaleX: 0.5, scaleY: 0.5 }, { duration: 120 }); } } // Small delay to prevent rapid switching LK.setTimeout(function () { isSwitching = false; }, 200); } // --- Movement controls (touch drag) --- function handleMove(x, y, obj) { if (gameOver || youWin) return; // Only move current animal if (!currentAnimal) return; // Clamp to play area var nx = Math.max(PLAY_AREA_LEFT, Math.min(PLAY_AREA_RIGHT, x)); var ny = Math.max(PLAY_AREA_TOP, Math.min(PLAY_AREA_BOTTOM, y)); // For animals with gravity, only allow horizontal drag if (currentAnimal.gravity > 0) { nx = Math.max(PLAY_AREA_LEFT, Math.min(PLAY_AREA_RIGHT, x)); // y is controlled by gravity/jump } else { currentAnimal.y = ny; } currentAnimal.x = nx; } // --- Touch events --- game.down = function (x, y, obj) { if (gameOver || youWin) return; // Start drag dragNode = currentAnimal; dragOffsetX = x - currentAnimal.x; dragOffsetY = y - currentAnimal.y; handleMove(x, y, obj); }; game.move = function (x, y, obj) { if (gameOver || youWin) return; if (dragNode) { handleMove(x - dragOffsetX, y - dragOffsetY, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // --- Collision helpers --- function intersectsAny(obj, arr) { for (var i = 0; i < arr.length; ++i) { if (obj.intersects(arr[i])) return true; } return false; } function getIntersecting(obj, arr) { for (var i = 0; i < arr.length; ++i) { if (obj.intersects(arr[i])) return arr[i]; } return null; } // --- Game update loop --- game.update = function () { if (gameOver || youWin) return; // Update obstacles for (var i = 0; i < acids.length; ++i) { if (acids[i].update) acids[i].update(); } for (var i = 0; i < foodChunks.length; ++i) { if (foodChunks[i].update) foodChunks[i].update(); } // Update current animal movement (gravity/jump) if (currentAnimal) { // Gravity/jump for Cat, Frog if (currentAnimal.gravity > 0) { // If not being dragged, apply gravity if (!dragNode) { currentAnimal.vy = (currentAnimal.vy || 0) + currentAnimal.gravity; currentAnimal.y += currentAnimal.vy; // Floor collision if (currentAnimal.y > PLAY_AREA_BOTTOM - 60) { currentAnimal.y = PLAY_AREA_BOTTOM - 60; currentAnimal.vy = 0; currentAnimal.isJumping = false; } // Ceiling collision if (currentAnimal.y < PLAY_AREA_TOP + 60) { currentAnimal.y = PLAY_AREA_TOP + 60; currentAnimal.vy = 0; } } } // TODO: Add jump on double tap or button (future) } // Check for acid collision (hazard) if (currentAnimal) { var acid = getIntersecting(currentAnimal, acids); if (acid) { // Some animals are immune if (currentAnimal.assetId === 'polarBear' || currentAnimal.assetId === 'axolotl' || currentAnimal.assetId === 'bee' || currentAnimal.assetId === 'bird') { // No effect } else { // Game over LK.effects.flashScreen(0x00ffff, 800); LK.showGameOver(); gameOver = true; return; } } } // Check for wall collision (keep inside) if (currentAnimal) { if (intersectsAny(currentAnimal, obstacles)) { // Push back inside if (currentAnimal.y < GAME_HEIGHT / 2) { currentAnimal.y += 20; } else { currentAnimal.y -= 20; } } } // Check for food chunk collision (Pig/Elephant can push) if (currentAnimal) { var chunk = getIntersecting(currentAnimal, foodChunks); if (chunk) { if (currentAnimal.assetId === 'pig' || currentAnimal.assetId === 'elephant') { // Push chunk in direction of movement var dx = chunk.x - currentAnimal.x; var dy = chunk.y - currentAnimal.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { chunk.x += dx / dist * 8; chunk.y += dy / dist * 8; // Clamp chunk inside play area chunk.x = Math.max(PLAY_AREA_LEFT + 60, Math.min(PLAY_AREA_RIGHT - 60, chunk.x)); chunk.y = Math.max(PLAY_AREA_TOP + 60, Math.min(PLAY_AREA_BOTTOM - 60, chunk.y)); } } else { // Block movement if (dragNode) { // Undo last move currentAnimal.x -= (currentAnimal.x - chunk.x) * 0.2; currentAnimal.y -= (currentAnimal.y - chunk.y) * 0.2; } } } } // Win condition: reach top of play area if (currentAnimal && currentAnimal.y < PLAY_AREA_TOP + 80) { // You win! LK.effects.flashScreen(0x00ff00, 1000); LK.showYouWin(); youWin = true; return; } }; // --- Game start --- function startGame() { setupLevel(); setupAnimals(); setupUI(); gameOver = false; youWin = false; } startGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Obstacle: acid pool (hazard)
var Acid = Container.expand(function () {
var self = Container.call(this);
var acid = self.attachAsset('acid', {
anchorX: 0.5,
anchorY: 0.5
});
// Acid can move left/right for challenge
self.speed = 0;
self.direction = 1;
self.update = function () {
if (self.speed !== 0) {
self.x += self.speed * self.direction;
if (self.x < 200 || self.x > 2048 - 200) {
self.direction *= -1;
}
}
};
return self;
});
// Animal base class
var Animal = Container.expand(function () {
var self = Container.call(this);
// These will be set by subclasses
self.assetId = '';
self.moveType = 'normal'; // 'normal', 'hop', 'slide', 'float', etc.
self.speed = 12; // Default speed
self.jumpPower = 0; // For animals that can jump
self.gravity = 0; // For animals affected by gravity
self.vy = 0; // Vertical velocity
// Attach animal asset
var animalSprite = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Used for per-animal movement logic
self.update = function () {
// Movement logic handled in main game loop
};
// Called when switching to this animal
self.onSwitchIn = function () {
// Flash or animate to show selection
tween(animalSprite, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(animalSprite, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeIn
});
}
});
};
// Called when switching away from this animal
self.onSwitchOut = function () {
// No-op for now
};
return self;
});
// Snail: very slow, can stick to walls
var Snail = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'snail';
self.speed = 4;
return self;
});
// Polar Bear: slow, immune to acid
var PolarBear = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'polarBear';
self.speed = 7;
return self;
});
// Pig: slow, can push food chunks
var Pig = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'pig';
self.speed = 8;
return self;
});
// Frog: can hop
var Frog = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'frog';
self.speed = 10;
self.jumpPower = 48;
self.gravity = 3.5;
self.isJumping = false;
return self;
});
// Elephant: can push obstacles, slowest
var Elephant = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'elephant';
self.speed = 6;
return self;
});
// Cat: fast, can jump
var Cat = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'cat';
self.speed = 16;
self.jumpPower = 38;
self.gravity = 3.2;
self.isJumping = false;
return self;
});
// Bird: can fly (ignores gravity)
var Bird = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'bird';
self.speed = 13;
self.moveType = 'fly';
return self;
});
// Bee: can float over acid, moves in short bursts
var Bee = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'bee';
self.speed = 10;
self.moveType = 'float';
return self;
});
// Axolotl: can swim in acid, normal speed
var Axolotl = Animal.expand(function () {
var self = Animal.call(this);
self.assetId = 'axolotl';
self.speed = 11;
self.moveType = 'swim';
return self;
});
// Obstacle: food chunk (movable)
var FoodChunk = Container.expand(function () {
var self = Container.call(this);
var chunk = self.attachAsset('foodChunk', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {};
return self;
});
// Obstacle: stomach wall (static)
var StomachWall = Container.expand(function () {
var self = Container.call(this);
var wall = self.attachAsset('stomachWall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Touch button for animal switching
var SwitchButton = Container.expand(function () {
var self = Container.call(this);
var btn = self.attachAsset('switchBtn', {
anchorX: 0.5,
anchorY: 0.5
});
self.icon = null; // Will be set to animal icon
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d1b0c // Deep brown, snake belly
});
/****
* Game Code
****/
// Touch button shapes
// Obstacle shapes
// Animal shapes (unique color for each animal)
// --- Game constants ---
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAY_AREA_TOP = 200;
var PLAY_AREA_BOTTOM = GAME_HEIGHT - 200;
var PLAY_AREA_LEFT = 100;
var PLAY_AREA_RIGHT = GAME_WIDTH - 100;
// --- Animal roster and order ---
var animalClasses = [Pig, Cat, Bee, Frog, PolarBear, Elephant, Bird, Snail, Axolotl];
var animalNames = ['Pig', 'Cat', 'Bee', 'Frog', 'Polar Bear', 'Elephant', 'Bird', 'Snail', 'Axolotl'];
var animalIcons = ['pig', 'cat', 'bee', 'frog', 'polarBear', 'elephant', 'bird', 'snail', 'axolotl'];
var animals = [];
var currentAnimalIdx = 0;
var currentAnimal = null;
// --- Obstacles and hazards ---
var obstacles = [];
var acids = [];
var foodChunks = [];
// --- UI ---
var switchButtons = [];
var switchBtnY = GAME_HEIGHT - 180;
var switchBtnSpacing = 200;
var infoText = null;
// --- Dragging ---
var dragNode = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
// --- Game state ---
var isMovingLeft = false;
var isMovingRight = false;
var isMovingUp = false;
var isMovingDown = false;
var isJumping = false;
var isSwitching = false;
var gameStarted = false;
var gameOver = false;
var youWin = false;
// --- Level layout (simple for MVP) ---
function setupLevel() {
// Clear previous
for (var i = 0; i < obstacles.length; ++i) obstacles[i].destroy();
for (var i = 0; i < acids.length; ++i) acids[i].destroy();
for (var i = 0; i < foodChunks.length; ++i) foodChunks[i].destroy();
obstacles = [];
acids = [];
foodChunks = [];
// Top and bottom stomach walls
var wallTop = new StomachWall();
wallTop.x = GAME_WIDTH / 2;
wallTop.y = PLAY_AREA_TOP - 40;
obstacles.push(wallTop);
game.addChild(wallTop);
var wallBottom = new StomachWall();
wallBottom.x = GAME_WIDTH / 2;
wallBottom.y = PLAY_AREA_BOTTOM + 40;
obstacles.push(wallBottom);
game.addChild(wallBottom);
// Acid pools (hazards)
var acid1 = new Acid();
acid1.x = GAME_WIDTH / 2;
acid1.y = PLAY_AREA_TOP + 400;
acid1.speed = 6;
acids.push(acid1);
game.addChild(acid1);
var acid2 = new Acid();
acid2.x = GAME_WIDTH / 2 + 400;
acid2.y = PLAY_AREA_TOP + 900;
acid2.speed = 0;
acids.push(acid2);
game.addChild(acid2);
// Food chunks (movable obstacles)
var chunk1 = new FoodChunk();
chunk1.x = GAME_WIDTH / 2 - 300;
chunk1.y = PLAY_AREA_TOP + 700;
foodChunks.push(chunk1);
game.addChild(chunk1);
var chunk2 = new FoodChunk();
chunk2.x = GAME_WIDTH / 2 + 300;
chunk2.y = PLAY_AREA_TOP + 1200;
foodChunks.push(chunk2);
game.addChild(chunk2);
// More obstacles can be added for future levels
}
// --- Animal setup ---
function setupAnimals() {
// Remove previous
for (var i = 0; i < animals.length; ++i) animals[i].destroy();
animals = [];
// Place all animals at start, stacked
var startX = GAME_WIDTH / 2;
var startY = PLAY_AREA_BOTTOM - 200;
for (var i = 0; i < animalClasses.length; ++i) {
var animal = new animalClasses[i]();
animal.x = startX + (i - 4) * 40;
animal.y = startY - i * 40;
animal.zIndex = i;
animal.visible = false;
animals.push(animal);
game.addChild(animal);
}
// Set first animal active
currentAnimalIdx = 0;
currentAnimal = animals[currentAnimalIdx];
currentAnimal.visible = true;
currentAnimal.onSwitchIn();
}
// --- UI setup ---
function setupUI() {
// Remove previous
for (var i = 0; i < switchButtons.length; ++i) switchButtons[i].destroy();
switchButtons = [];
// Create switch buttons for each animal
var btnStartX = GAME_WIDTH / 2 - (animalClasses.length - 1) / 2 * switchBtnSpacing;
for (var i = 0; i < animalClasses.length; ++i) {
var btn = new SwitchButton();
btn.x = btnStartX + i * switchBtnSpacing;
btn.y = switchBtnY;
btn.animalIdx = i;
// Add animal icon
var icon = btn.attachAsset(animalIcons[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
btn.icon = icon;
// Highlight current
if (i === currentAnimalIdx) {
icon.scaleX = icon.scaleY = 0.7;
}
// Touch event
btn.down = function (x, y, obj) {
if (isSwitching) return;
var idx = this.animalIdx;
if (idx !== currentAnimalIdx) {
switchAnimal(idx);
}
};
LK.gui.bottom.addChild(btn);
switchButtons.push(btn);
}
// Info text
if (infoText) infoText.destroy();
infoText = new Text2('Help the animals escape!\nTap an animal to switch.\nDrag to move. Avoid acid!', {
size: 70,
fill: "#fff",
align: "center"
});
infoText.anchor.set(0.5, 0);
LK.gui.top.addChild(infoText);
}
// --- Animal switching ---
function switchAnimal(idx) {
if (idx < 0 || idx >= animals.length) return;
if (idx === currentAnimalIdx) return;
isSwitching = true;
// Animate out old
currentAnimal.onSwitchOut();
currentAnimal.visible = false;
// Animate in new
currentAnimalIdx = idx;
currentAnimal = animals[currentAnimalIdx];
currentAnimal.visible = true;
currentAnimal.onSwitchIn();
// Highlight button
for (var i = 0; i < switchButtons.length; ++i) {
var icon = switchButtons[i].icon;
if (i === currentAnimalIdx) {
tween(icon, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 120
});
} else {
tween(icon, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 120
});
}
}
// Small delay to prevent rapid switching
LK.setTimeout(function () {
isSwitching = false;
}, 200);
}
// --- Movement controls (touch drag) ---
function handleMove(x, y, obj) {
if (gameOver || youWin) return;
// Only move current animal
if (!currentAnimal) return;
// Clamp to play area
var nx = Math.max(PLAY_AREA_LEFT, Math.min(PLAY_AREA_RIGHT, x));
var ny = Math.max(PLAY_AREA_TOP, Math.min(PLAY_AREA_BOTTOM, y));
// For animals with gravity, only allow horizontal drag
if (currentAnimal.gravity > 0) {
nx = Math.max(PLAY_AREA_LEFT, Math.min(PLAY_AREA_RIGHT, x));
// y is controlled by gravity/jump
} else {
currentAnimal.y = ny;
}
currentAnimal.x = nx;
}
// --- Touch events ---
game.down = function (x, y, obj) {
if (gameOver || youWin) return;
// Start drag
dragNode = currentAnimal;
dragOffsetX = x - currentAnimal.x;
dragOffsetY = y - currentAnimal.y;
handleMove(x, y, obj);
};
game.move = function (x, y, obj) {
if (gameOver || youWin) return;
if (dragNode) {
handleMove(x - dragOffsetX, y - dragOffsetY, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Collision helpers ---
function intersectsAny(obj, arr) {
for (var i = 0; i < arr.length; ++i) {
if (obj.intersects(arr[i])) return true;
}
return false;
}
function getIntersecting(obj, arr) {
for (var i = 0; i < arr.length; ++i) {
if (obj.intersects(arr[i])) return arr[i];
}
return null;
}
// --- Game update loop ---
game.update = function () {
if (gameOver || youWin) return;
// Update obstacles
for (var i = 0; i < acids.length; ++i) {
if (acids[i].update) acids[i].update();
}
for (var i = 0; i < foodChunks.length; ++i) {
if (foodChunks[i].update) foodChunks[i].update();
}
// Update current animal movement (gravity/jump)
if (currentAnimal) {
// Gravity/jump for Cat, Frog
if (currentAnimal.gravity > 0) {
// If not being dragged, apply gravity
if (!dragNode) {
currentAnimal.vy = (currentAnimal.vy || 0) + currentAnimal.gravity;
currentAnimal.y += currentAnimal.vy;
// Floor collision
if (currentAnimal.y > PLAY_AREA_BOTTOM - 60) {
currentAnimal.y = PLAY_AREA_BOTTOM - 60;
currentAnimal.vy = 0;
currentAnimal.isJumping = false;
}
// Ceiling collision
if (currentAnimal.y < PLAY_AREA_TOP + 60) {
currentAnimal.y = PLAY_AREA_TOP + 60;
currentAnimal.vy = 0;
}
}
}
// TODO: Add jump on double tap or button (future)
}
// Check for acid collision (hazard)
if (currentAnimal) {
var acid = getIntersecting(currentAnimal, acids);
if (acid) {
// Some animals are immune
if (currentAnimal.assetId === 'polarBear' || currentAnimal.assetId === 'axolotl' || currentAnimal.assetId === 'bee' || currentAnimal.assetId === 'bird') {
// No effect
} else {
// Game over
LK.effects.flashScreen(0x00ffff, 800);
LK.showGameOver();
gameOver = true;
return;
}
}
}
// Check for wall collision (keep inside)
if (currentAnimal) {
if (intersectsAny(currentAnimal, obstacles)) {
// Push back inside
if (currentAnimal.y < GAME_HEIGHT / 2) {
currentAnimal.y += 20;
} else {
currentAnimal.y -= 20;
}
}
}
// Check for food chunk collision (Pig/Elephant can push)
if (currentAnimal) {
var chunk = getIntersecting(currentAnimal, foodChunks);
if (chunk) {
if (currentAnimal.assetId === 'pig' || currentAnimal.assetId === 'elephant') {
// Push chunk in direction of movement
var dx = chunk.x - currentAnimal.x;
var dy = chunk.y - currentAnimal.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
chunk.x += dx / dist * 8;
chunk.y += dy / dist * 8;
// Clamp chunk inside play area
chunk.x = Math.max(PLAY_AREA_LEFT + 60, Math.min(PLAY_AREA_RIGHT - 60, chunk.x));
chunk.y = Math.max(PLAY_AREA_TOP + 60, Math.min(PLAY_AREA_BOTTOM - 60, chunk.y));
}
} else {
// Block movement
if (dragNode) {
// Undo last move
currentAnimal.x -= (currentAnimal.x - chunk.x) * 0.2;
currentAnimal.y -= (currentAnimal.y - chunk.y) * 0.2;
}
}
}
}
// Win condition: reach top of play area
if (currentAnimal && currentAnimal.y < PLAY_AREA_TOP + 80) {
// You win!
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
youWin = true;
return;
}
};
// --- Game start ---
function startGame() {
setupLevel();
setupAnimals();
setupUI();
gameOver = false;
youWin = false;
}
startGame();