/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Acid Bubble (obstacle)
var AcidBubble = Container.expand(function () {
var self = Container.call(this);
var bubble = self.attachAsset('acidBubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bubble.width / 2;
self.vx = (Math.random() - 0.5) * 6;
self.vy = (Math.random() - 0.5) * 6;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Bounce off belly walls
if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
self.vx *= -1;
}
if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
self.vx *= -1;
}
if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
self.vy *= -1;
}
if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
self.vy *= -1;
}
};
return self;
});
// Animal class
var Animal = Container.expand(function () {
var self = Container.call(this);
// Defensive: set a default assetId if not set
if (!self.assetId) self.assetId = 'pig';
// Asset id is passed in as self.assetId
var animalSprite = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = animalSprite.width / 2;
// Used for movement
self.vx = 0;
self.vy = 0;
self.isSelected = false;
// Called every tick
self.update = function () {
// Gravity inside snake
self.vy += 0.5;
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= 0.95;
self.vy *= 0.95;
// Keep inside belly bounds
if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
self.vx *= -0.5;
}
if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
self.vx *= -0.5;
}
if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
self.vy *= -0.5;
}
if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
self.vy *= -0.5;
}
};
// Touch down on animal
self.down = function (x, y, obj) {
self.isSelected = true;
selectedAnimal = self;
dragOffsetX = self.x - x;
dragOffsetY = self.y - y;
};
// Touch up
self.up = function (x, y, obj) {
self.isSelected = false;
selectedAnimal = null;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Sound for animal jump
// Exit (esophagus opening)
// Obstacle (stomach acid bubble)
// Snake exterior (for outside interaction)
// Snake belly (background)
// Animal shapes (simple colored ellipses for each animal)
// --- Global variables ---
var animalIds = ['pig', 'cat', 'bee', 'frog', 'polarBear', 'elephant', 'bird', 'snail', 'axolotl', 'pinkFox', 'boyBat', 'wolf', 'chinchilla', 'moth', 'goat', 'bunny', 'bear', 'deer'];
var animals = [];
var acidBubbles = [];
var selectedAnimal = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var insideMode = true; // true: inside snake, false: outside
var snakeBelly, snakeSkin, exitNode;
var scoreTxt;
var rescuedCount = 0;
var totalToRescue = 5; // MVP: rescue 5 animals to win
// --- Setup snake belly (inside) ---
snakeBelly = LK.getAsset('snakeBelly', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1200
});
game.addChild(snakeBelly);
// --- Setup exit (esophagus opening) ---
exitNode = LK.getAsset('exit', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 800
});
game.addChild(exitNode);
// --- Setup animals ---
function spawnAnimals() {
animals = [];
var startX = 2048 / 2 - 400;
var startY = 1200;
for (var i = 0; i < totalToRescue; i++) {
var id = animalIds[i];
var animal = new Animal();
animal.assetId = id;
animal.x = startX + i * 200;
animal.y = startY + Math.random() * 100 - 50;
animal.vx = (Math.random() - 0.5) * 6;
animal.vy = (Math.random() - 0.5) * 6;
animals.push(animal);
game.addChild(animal);
}
}
spawnAnimals();
// --- Setup acid bubbles (obstacles) ---
function spawnAcidBubbles() {
acidBubbles = [];
for (var i = 0; i < 3; i++) {
var bubble = new AcidBubble();
bubble.x = 2048 / 2 + (Math.random() - 0.5) * 600;
bubble.y = 1200 + (Math.random() - 0.5) * 300;
acidBubbles.push(bubble);
game.addChild(bubble);
}
}
spawnAcidBubbles();
// --- Setup score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Setup snake skin (outside) ---
snakeSkin = LK.getAsset('snakeSkin', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 0
});
// Not added yet; only shown in outside mode
// --- Helper: reset game state ---
function resetGame() {
// Remove all animals and bubbles
for (var i = 0; i < animals.length; i++) animals[i].destroy();
for (var i = 0; i < acidBubbles.length; i++) acidBubbles[i].destroy();
animals = [];
acidBubbles = [];
rescuedCount = 0;
scoreTxt.setText('0');
insideMode = true;
if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
if (!snakeBelly.parent) game.addChild(snakeBelly);
if (!exitNode.parent) game.addChild(exitNode);
spawnAnimals();
spawnAcidBubbles();
}
// --- Touch/drag logic ---
game.move = function (x, y, obj) {
if (!insideMode) return;
if (selectedAnimal) {
// Move animal with finger
selectedAnimal.x = x + dragOffsetX;
selectedAnimal.y = y + dragOffsetY;
}
};
game.down = function (x, y, obj) {
if (!insideMode) return;
// Check if any animal is touched
for (var i = 0; i < animals.length; i++) {
var a = animals[i];
var dx = x - a.x;
var dy = y - a.y;
if (dx * dx + dy * dy < a.radius * a.radius) {
a.down(x, y, obj);
return;
}
}
};
game.up = function (x, y, obj) {
if (!insideMode) return;
if (selectedAnimal) {
// Give a jump impulse upwards
selectedAnimal.vy = -18;
selectedAnimal.vx = (Math.random() - 0.5) * 8;
LK.getSound('jump').play();
selectedAnimal.up(x, y, obj);
}
};
// --- Outside mode: tap snake skin to help animals escape ---
snakeSkin.down = function (x, y, obj) {
if (!insideMode) {
// "Massage" the snake: all animals get a little upward boost
for (var i = 0; i < animals.length; i++) {
animals[i].vy -= 10 + Math.random() * 6;
}
// Animate snake skin
tween(snakeSkin, {
y: 20
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(snakeSkin, {
y: 0
}, {
duration: 180,
easing: tween.easeIn
});
}
});
}
};
// --- Game update loop ---
game.update = function () {
// Switch between inside/outside every 10 seconds
if (LK.ticks % 600 === 0) {
insideMode = !insideMode;
if (insideMode) {
// Show inside
if (!snakeBelly.parent) game.addChild(snakeBelly);
if (!exitNode.parent) game.addChild(exitNode);
if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
} else {
// Show outside
if (!snakeSkin.parent) game.addChild(snakeSkin);
if (snakeBelly.parent) snakeBelly.parent.removeChild(snakeBelly);
if (exitNode.parent) exitNode.parent.removeChild(exitNode);
}
}
// Update animals
for (var i = animals.length - 1; i >= 0; i--) {
var a = animals[i];
a.update();
// If animal touches exit, rescue it!
if (insideMode && a.intersects(exitNode)) {
// Animate out
tween(a, {
y: exitNode.y - 200
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
a.destroy();
}
});
animals.splice(i, 1);
rescuedCount++;
scoreTxt.setText(rescuedCount);
// Win condition
if (rescuedCount >= totalToRescue) {
LK.showYouWin();
return;
}
}
}
// Update acid bubbles
for (var i = acidBubbles.length - 1; i >= 0; i--) {
var b = acidBubbles[i];
b.update();
// If any animal hits a bubble, game over
for (var j = 0; j < animals.length; j++) {
var a = animals[j];
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = a.radius + b.radius - 10;
if (dx * dx + dy * dy < dist * dist) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
};
// --- Reset game on game over or win ---
LK.on('gameover', function () {
resetGame();
});
LK.on('youwin', function () {
resetGame();
});
// --- Instructions text ---
var instructions = new Text2("Drag animals to help them jump to the exit!\nAvoid acid bubbles.\nSometimes tap the snake's back to help!", {
size: 60,
fill: "#fff"
});
instructions.anchor.set(0.5, 0);
LK.gui.top.addChild(instructions);
instructions.y = 180;
instructions.x = 2048 / 2; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Acid Bubble (obstacle)
var AcidBubble = Container.expand(function () {
var self = Container.call(this);
var bubble = self.attachAsset('acidBubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bubble.width / 2;
self.vx = (Math.random() - 0.5) * 6;
self.vy = (Math.random() - 0.5) * 6;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Bounce off belly walls
if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
self.vx *= -1;
}
if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
self.vx *= -1;
}
if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
self.vy *= -1;
}
if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
self.vy *= -1;
}
};
return self;
});
// Animal class
var Animal = Container.expand(function () {
var self = Container.call(this);
// Defensive: set a default assetId if not set
if (!self.assetId) self.assetId = 'pig';
// Asset id is passed in as self.assetId
var animalSprite = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = animalSprite.width / 2;
// Used for movement
self.vx = 0;
self.vy = 0;
self.isSelected = false;
// Called every tick
self.update = function () {
// Gravity inside snake
self.vy += 0.5;
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= 0.95;
self.vy *= 0.95;
// Keep inside belly bounds
if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
self.vx *= -0.5;
}
if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
self.vx *= -0.5;
}
if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
self.vy *= -0.5;
}
if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
self.vy *= -0.5;
}
};
// Touch down on animal
self.down = function (x, y, obj) {
self.isSelected = true;
selectedAnimal = self;
dragOffsetX = self.x - x;
dragOffsetY = self.y - y;
};
// Touch up
self.up = function (x, y, obj) {
self.isSelected = false;
selectedAnimal = null;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Sound for animal jump
// Exit (esophagus opening)
// Obstacle (stomach acid bubble)
// Snake exterior (for outside interaction)
// Snake belly (background)
// Animal shapes (simple colored ellipses for each animal)
// --- Global variables ---
var animalIds = ['pig', 'cat', 'bee', 'frog', 'polarBear', 'elephant', 'bird', 'snail', 'axolotl', 'pinkFox', 'boyBat', 'wolf', 'chinchilla', 'moth', 'goat', 'bunny', 'bear', 'deer'];
var animals = [];
var acidBubbles = [];
var selectedAnimal = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var insideMode = true; // true: inside snake, false: outside
var snakeBelly, snakeSkin, exitNode;
var scoreTxt;
var rescuedCount = 0;
var totalToRescue = 5; // MVP: rescue 5 animals to win
// --- Setup snake belly (inside) ---
snakeBelly = LK.getAsset('snakeBelly', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1200
});
game.addChild(snakeBelly);
// --- Setup exit (esophagus opening) ---
exitNode = LK.getAsset('exit', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 800
});
game.addChild(exitNode);
// --- Setup animals ---
function spawnAnimals() {
animals = [];
var startX = 2048 / 2 - 400;
var startY = 1200;
for (var i = 0; i < totalToRescue; i++) {
var id = animalIds[i];
var animal = new Animal();
animal.assetId = id;
animal.x = startX + i * 200;
animal.y = startY + Math.random() * 100 - 50;
animal.vx = (Math.random() - 0.5) * 6;
animal.vy = (Math.random() - 0.5) * 6;
animals.push(animal);
game.addChild(animal);
}
}
spawnAnimals();
// --- Setup acid bubbles (obstacles) ---
function spawnAcidBubbles() {
acidBubbles = [];
for (var i = 0; i < 3; i++) {
var bubble = new AcidBubble();
bubble.x = 2048 / 2 + (Math.random() - 0.5) * 600;
bubble.y = 1200 + (Math.random() - 0.5) * 300;
acidBubbles.push(bubble);
game.addChild(bubble);
}
}
spawnAcidBubbles();
// --- Setup score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Setup snake skin (outside) ---
snakeSkin = LK.getAsset('snakeSkin', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 0
});
// Not added yet; only shown in outside mode
// --- Helper: reset game state ---
function resetGame() {
// Remove all animals and bubbles
for (var i = 0; i < animals.length; i++) animals[i].destroy();
for (var i = 0; i < acidBubbles.length; i++) acidBubbles[i].destroy();
animals = [];
acidBubbles = [];
rescuedCount = 0;
scoreTxt.setText('0');
insideMode = true;
if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
if (!snakeBelly.parent) game.addChild(snakeBelly);
if (!exitNode.parent) game.addChild(exitNode);
spawnAnimals();
spawnAcidBubbles();
}
// --- Touch/drag logic ---
game.move = function (x, y, obj) {
if (!insideMode) return;
if (selectedAnimal) {
// Move animal with finger
selectedAnimal.x = x + dragOffsetX;
selectedAnimal.y = y + dragOffsetY;
}
};
game.down = function (x, y, obj) {
if (!insideMode) return;
// Check if any animal is touched
for (var i = 0; i < animals.length; i++) {
var a = animals[i];
var dx = x - a.x;
var dy = y - a.y;
if (dx * dx + dy * dy < a.radius * a.radius) {
a.down(x, y, obj);
return;
}
}
};
game.up = function (x, y, obj) {
if (!insideMode) return;
if (selectedAnimal) {
// Give a jump impulse upwards
selectedAnimal.vy = -18;
selectedAnimal.vx = (Math.random() - 0.5) * 8;
LK.getSound('jump').play();
selectedAnimal.up(x, y, obj);
}
};
// --- Outside mode: tap snake skin to help animals escape ---
snakeSkin.down = function (x, y, obj) {
if (!insideMode) {
// "Massage" the snake: all animals get a little upward boost
for (var i = 0; i < animals.length; i++) {
animals[i].vy -= 10 + Math.random() * 6;
}
// Animate snake skin
tween(snakeSkin, {
y: 20
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(snakeSkin, {
y: 0
}, {
duration: 180,
easing: tween.easeIn
});
}
});
}
};
// --- Game update loop ---
game.update = function () {
// Switch between inside/outside every 10 seconds
if (LK.ticks % 600 === 0) {
insideMode = !insideMode;
if (insideMode) {
// Show inside
if (!snakeBelly.parent) game.addChild(snakeBelly);
if (!exitNode.parent) game.addChild(exitNode);
if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
} else {
// Show outside
if (!snakeSkin.parent) game.addChild(snakeSkin);
if (snakeBelly.parent) snakeBelly.parent.removeChild(snakeBelly);
if (exitNode.parent) exitNode.parent.removeChild(exitNode);
}
}
// Update animals
for (var i = animals.length - 1; i >= 0; i--) {
var a = animals[i];
a.update();
// If animal touches exit, rescue it!
if (insideMode && a.intersects(exitNode)) {
// Animate out
tween(a, {
y: exitNode.y - 200
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
a.destroy();
}
});
animals.splice(i, 1);
rescuedCount++;
scoreTxt.setText(rescuedCount);
// Win condition
if (rescuedCount >= totalToRescue) {
LK.showYouWin();
return;
}
}
}
// Update acid bubbles
for (var i = acidBubbles.length - 1; i >= 0; i--) {
var b = acidBubbles[i];
b.update();
// If any animal hits a bubble, game over
for (var j = 0; j < animals.length; j++) {
var a = animals[j];
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = a.radius + b.radius - 10;
if (dx * dx + dy * dy < dist * dist) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
};
// --- Reset game on game over or win ---
LK.on('gameover', function () {
resetGame();
});
LK.on('youwin', function () {
resetGame();
});
// --- Instructions text ---
var instructions = new Text2("Drag animals to help them jump to the exit!\nAvoid acid bubbles.\nSometimes tap the snake's back to help!", {
size: 60,
fill: "#fff"
});
instructions.anchor.set(0.5, 0);
LK.gui.top.addChild(instructions);
instructions.y = 180;
instructions.x = 2048 / 2;