/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
creepy: false
});
/****
* Classes
****/
// Food class
var Food = Container.expand(function () {
var self = Container.call(this);
self.foodId = null;
self.isGood = true;
self.isCreepy = false;
self.asset = null;
self.eaten = false;
self.init = function (foodId, isGood, isCreepy) {
self.foodId = foodId;
self.isGood = isGood;
self.isCreepy = isCreepy;
self.asset = self.attachAsset(foodId, {
anchorX: 0.5,
anchorY: 0.5
});
};
// Animate food being eaten
self.eat = function (_onFinish) {
if (self.eaten) return;
self.eaten = true;
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 350,
easing: tween.easeIn,
onFinish: function onFinish() {
if (_onFinish) _onFinish();
self.destroy();
}
});
};
// Touch event
self.down = function (x, y, obj) {
if (game.state !== 'playing' && game.state !== 'creepy') return;
game.handleFoodSelect(self);
};
return self;
});
// Knife class
var Knife = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('fork', {
anchorX: 0.5,
anchorY: 0.5
});
self.stabbing = false;
self.target = null;
self.update = function () {
// Animate knife toward tomato if stabbing
if (self.stabbing && self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - 120 - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 30) {
self.x += dx * 0.25;
self.y += dy * 0.25;
self.rotation = Math.atan2(dy, dx) + Math.PI / 2;
} else {
// Hit!
self.stabbing = false;
self.visible = false;
if (typeof self.onStab === "function") self.onStab();
self.destroy();
}
}
};
return self;
});
// Tomato class
var Tomato = Container.expand(function () {
var self = Container.call(this);
self.face = null;
self.creepy = false;
self.jumpscare = false;
self.init = function () {
self.face = self.attachAsset('tomatoNormal', {
anchorX: 0.5,
anchorY: 1
});
};
self.setNormal = function () {
self.creepy = false;
self.jumpscare = false;
self.face.assetId = 'tomatoNormal';
self.face.setAsset('tomatoNormal');
};
self.setCreepy = function () {
self.creepy = true;
self.jumpscare = false;
self.face.assetId = 'tomatoCreepy';
self.face.setAsset('tomatoCreepy');
};
self.setJumpscare = function () {
self.jumpscare = true;
self.face.assetId = 'tomatoJumpscare';
self.face.setAsset('tomatoJumpscare');
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf7e7c1
});
/****
* Game Code
****/
// --- GLOBALS ---
// Tomato (normal)
// Tomato (creepy)
// Tomato (jumpscare)
// Table
// Plate
// Fork
// Spoon
// Good foods
// Bad foods
// Creepy foods
// Sounds
// Music
var tomato = null;
var table = null;
var plate = null;
var utensils = [];
var foodObjs = [];
var foodSlots = [];
var messageTxt = null;
var scoreTxt = null;
var stateTxt = null;
var jumpscareTimeout = null;
var mistakeCount = 0;
var correctCount = 0;
var maxMistakes = 10;
var maxCorrect = 5;
var isJumpscare = false;
var isHaunted = false;
var lastFoodIds = [];
var creepyFoodsFed = 0;
var creepyFoodsNeeded = 3;
var foodY = 2000;
var foodSlotY = 2000;
var foodSlotXStart = 524;
var foodSlotSpacing = 250;
var foodSlotCount = 5;
var foodTypes = [{
id: 'foodApple',
isGood: true,
isCreepy: false
}, {
id: 'foodCheese',
isGood: true,
isCreepy: false
}, {
id: 'foodBread',
isGood: true,
isCreepy: false
}, {
id: 'foodEgg',
isGood: true,
isCreepy: false
}, {
id: 'foodCarrot',
isGood: true,
isCreepy: false
}, {
id: 'foodSoap',
isGood: false,
isCreepy: false
}, {
id: 'foodRock',
isGood: false,
isCreepy: false
}, {
id: 'foodBattery',
isGood: false,
isCreepy: false
}, {
id: 'foodShoe',
isGood: false,
isCreepy: false
}, {
id: 'foodBug',
isGood: false,
isCreepy: false
}];
var creepyFoodTypes = [{
id: 'foodEyeball',
isGood: false,
isCreepy: true
}, {
id: 'foodWorm',
isGood: false,
isCreepy: true
}, {
id: 'foodFingertip',
isGood: false,
isCreepy: true
}, {
id: 'foodTooth',
isGood: false,
isCreepy: true
}, {
id: 'foodHeart',
isGood: false,
isCreepy: true
}];
// --- HAUNTED STATE ---
if (storage.creepy) {
isHaunted = true;
}
// --- SETUP SCENE ---
function setupScene() {
// Table
table = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200
});
game.addChild(table);
// Plate
plate = LK.getAsset('plate', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2100
});
game.addChild(plate);
// Utensils
var fork = LK.getAsset('fork', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 2180
});
var spoon = LK.getAsset('spoon', {
anchorX: 0.5,
anchorY: 0.5,
x: 1150,
y: 2180
});
utensils = [fork, spoon];
game.addChild(fork);
game.addChild(spoon);
// Tomato
tomato = new Tomato();
tomato.init();
tomato.x = 1024;
tomato.y = 900;
game.addChild(tomato);
// Message text
messageTxt = new Text2('', {
size: 90,
fill: 0x222222
});
messageTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(messageTxt);
// Score text (hidden, but used for state)
scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(scoreTxt);
// Console button
var consoleBtn = new Text2('console', {
size: 60,
fill: 0x333333
});
consoleBtn.anchor.set(1, 0);
consoleBtn.x = -20;
consoleBtn.y = 20;
LK.gui.topRight.addChild(consoleBtn);
// Shop button
var shopBtn = new Text2('shop', {
size: 60,
fill: 0x333333
});
shopBtn.anchor.set(1, 0);
shopBtn.x = -20;
shopBtn.y = 110;
LK.gui.topRight.addChild(shopBtn);
// --- SHOP STATE ---
var shopOverlay = null;
var shopKnives = typeof storage.knives === "number" ? storage.knives : 0;
// Shop button handler
shopBtn.down = function (x, y, obj) {
if (shopOverlay) return;
shopOverlay = new Container();
game.addChild(shopOverlay);
// Dimmed background
var shopBg = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 30,
x: 1024,
y: 1366
});
shopBg.tint = 0x222222;
shopBg.alpha = 0.85;
shopOverlay.addChild(shopBg);
// Shop title
var shopTitle = new Text2('Shop', {
size: 160,
fill: 0xffffff
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 1024;
shopTitle.y = 400;
shopOverlay.addChild(shopTitle);
// Knives icon
var knifeIcon = LK.getAsset('fork', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 700,
scaleX: 1.2,
scaleY: 1.2
});
shopOverlay.addChild(knifeIcon);
// Knives label
var knifeLabel = new Text2('Knives: ' + shopKnives, {
size: 90,
fill: 0xffffff
});
knifeLabel.anchor.set(0, 0.5);
knifeLabel.x = 1000;
knifeLabel.y = 700;
shopOverlay.addChild(knifeLabel);
// Buy knife button
var buyBtn = new Text2('Buy Knife (Free!)', {
size: 80,
fill: 0x00ff00
});
buyBtn.anchor.set(0.5, 0.5);
buyBtn.x = 1024;
buyBtn.y = 950;
shopOverlay.addChild(buyBtn);
buyBtn.down = function () {
shopKnives++;
storage.knives = shopKnives;
knifeLabel.setText('Knives: ' + shopKnives);
showMessage("You bought a knife!");
};
// --- ID SHOP ITEM ---
// Only allow one free ID ever
var hasId = !!storage.hasId;
var idIcon = LK.getAsset('googleBlock', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 1050,
scaleX: 0.18,
scaleY: 0.18
});
shopOverlay.addChild(idIcon);
var idLabel = new Text2('ID: ' + (hasId ? 'Owned' : 'Not owned'), {
size: 90,
fill: 0xffffff
});
idLabel.anchor.set(0, 0.5);
idLabel.x = 1000;
idLabel.y = 1050;
shopOverlay.addChild(idLabel);
var buyIdBtn = new Text2(hasId ? 'ID Claimed' : 'Claim ID (Free!)', {
size: 80,
fill: hasId ? 0x888888 : 0x00ffff
});
buyIdBtn.anchor.set(0.5, 0.5);
buyIdBtn.x = 1024;
buyIdBtn.y = 1200;
shopOverlay.addChild(buyIdBtn);
buyIdBtn.down = function () {
if (!hasId) {
storage.hasId = true;
hasId = true;
idLabel.setText('ID: Owned');
buyIdBtn.setText('ID Claimed');
buyIdBtn.fill = 0x888888;
showMessage("You got the ID! Try the console...");
} else {
showMessage("You already have the ID.");
}
};
// Use knife button
var useKnifeBtn = new Text2('Use Knife', {
size: 80,
fill: 0xff0000
});
useKnifeBtn.anchor.set(0.5, 0.5);
useKnifeBtn.x = 1024;
useKnifeBtn.y = 1350;
shopOverlay.addChild(useKnifeBtn);
useKnifeBtn.down = function () {
if (shopKnives > 0 && tomato && !isHaunted && game.state !== 'jumpscare' && game.state !== 'haunt' && game.state !== 'blocked') {
shopKnives--;
storage.knives = shopKnives;
knifeLabel.setText('Knives: ' + shopKnives);
shopOverlay.destroy();
shopOverlay = null;
stabTomato();
} else if (shopKnives <= 0) {
showMessage("You have no knives!");
}
};
// Close button
var closeBtn = new Text2('Close', {
size: 80,
fill: 0xff4444
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = 1024;
closeBtn.y = 1500;
shopOverlay.addChild(closeBtn);
closeBtn.down = function () {
if (shopOverlay) {
shopOverlay.destroy();
shopOverlay = null;
}
};
};
// Console variables
var consoleOverlay = null;
var consoleBg = null;
var consoleText = null;
var consoleVisible = false;
var keyboardVisible = false;
var currentInput = "";
var deletingFiles = false;
var deleteProgress = 0;
var deleteTimeout = null;
// Console button click handler
consoleBtn.down = function (x, y, obj) {
if (!consoleVisible) {
showConsole();
} else {
hideConsole();
}
};
// Show console function
function showConsole() {
if (consoleOverlay) return;
consoleVisible = true;
// Create console overlay container covering full screen
consoleOverlay = new Container();
game.addChild(consoleOverlay);
// Full screen black background
consoleBg = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 30,
x: 1024,
y: 1366
});
consoleBg.tint = 0x000000;
consoleOverlay.addChild(consoleBg);
// Console text centered
consoleText = new Text2('hello user', {
size: 180,
fill: 0xFFFFFF
});
consoleText.anchor.set(0.5, 0.5);
consoleText.x = 1024;
consoleText.y = 800;
consoleOverlay.addChild(consoleText);
// Create virtual keyboard buttons
var keyboardKeys = [['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], ['a', 's', 'd', 'e', 'l', 'e', 't', 'e'], ['z', 'x', 'c', 'v', 'b', 'n', 'm'], ['backspace', 'enter']];
var keyButtons = [];
var startY = 1200;
for (var row = 0; row < keyboardKeys.length; row++) {
for (var col = 0; col < keyboardKeys[row].length; col++) {
var key = keyboardKeys[row][col];
var keyBtn = new Text2(key, {
size: 80,
fill: 0xFFFFFF
});
keyBtn.anchor.set(0.5, 0.5);
// Special spacing for bottom row to prevent overlap
if (row === 3) {
keyBtn.x = 400 + col * 300;
} else {
keyBtn.x = 200 + col * 160;
}
keyBtn.y = startY + row * 120;
keyBtn.keyValue = key;
keyBtn.down = function (x, y, obj) {
if (keyboardVisible) {
handleKeyInput(this.keyValue);
}
};
consoleOverlay.addChild(keyBtn);
keyButtons.push(keyBtn);
}
}
// Make console clickable to show keyboard
consoleBg.down = function (x, y, obj) {
showKeyboard();
};
}
// Hide console function
function hideConsole() {
if (consoleOverlay) {
consoleOverlay.destroy();
consoleOverlay = null;
consoleBg = null;
consoleText = null;
consoleVisible = false;
keyboardVisible = false;
currentInput = "";
deletingFiles = false;
deleteProgress = 0;
if (deleteTimeout) {
LK.clearTimeout(deleteTimeout);
deleteTimeout = null;
}
}
}
// Show keyboard function
function showKeyboard() {
if (keyboardVisible) return;
keyboardVisible = true;
updateConsoleText();
}
// Update console text with current input
function updateConsoleText() {
if (!consoleText) return;
var displayText = "hello user\n> " + currentInput;
if (deletingFiles) {
displayText = "hello user\n> delete\nDeleting files... " + Math.floor(deleteProgress) + "%";
}
consoleText.setText(displayText);
}
// Handle keyboard input
function handleKeyInput(key) {
if (!keyboardVisible || deletingFiles) return;
// Only allow typing/deleting if player owns the ID
if (!storage.hasId) {
if (key === "delete" || key === "backspace" || key === "enter" || key.length === 1) {
showMessage("You need the ID from the shop to use the console.");
return;
}
}
if (key === "delete") {
currentInput = "";
startDeleteSequence();
} else if (key === "enter") {
if (currentInput.toLowerCase() === "delete") {
startDeleteSequence();
} else if (currentInput.toLowerCase() === "play") {
// Hide console and restart game
hideConsole();
isHaunted = false;
storage.creepy = false;
startGame();
} else {
updateConsoleText();
}
} else if (key === "backspace") {
if (currentInput.length > 0) {
currentInput = currentInput.slice(0, -1);
updateConsoleText();
}
} else if (key.length === 1) {
currentInput += key;
updateConsoleText();
}
}
// Start delete file sequence
function startDeleteSequence() {
deletingFiles = true;
deleteProgress = 0;
updateConsoleText();
var deleteInterval = LK.setInterval(function () {
deleteProgress += Math.random() * 15 + 5;
if (deleteProgress >= 95) {
deleteProgress = 95;
LK.clearInterval(deleteInterval);
// Show tomato when almost done
deleteTimeout = LK.setTimeout(function () {
showTomatoInConsole();
}, 500);
}
updateConsoleText();
}, 200);
}
// Show tomato appearing in console
function showTomatoInConsole() {
if (!consoleOverlay) return;
// Create tomato in console
var consoleTomato = new Tomato();
consoleTomato.init();
consoleTomato.x = 1024;
consoleTomato.y = 1000;
consoleTomato.scaleX = 2;
consoleTomato.scaleY = 2;
consoleOverlay.addChild(consoleTomato);
// Play Mr. Tomato's special voice line (use 'creepy' or 'jumpscare' sound for effect)
LK.getSound('creepy').play();
// After a short delay, try to "crash" the game, or show error text if not possible
LK.setTimeout(function () {
if (consoleOverlay) {
// Try to crash: show error text and block all input
consoleOverlay.destroy();
consoleOverlay = null;
consoleBg = null;
consoleText = null;
consoleVisible = false;
keyboardVisible = false;
currentInput = "";
deletingFiles = false;
deleteProgress = 0;
if (deleteTimeout) {
LK.clearTimeout(deleteTimeout);
deleteTimeout = null;
}
// Show a fake crash/error overlay
var crashOverlay = new Container();
game.addChild(crashOverlay);
var crashBg = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 30,
x: 1024,
y: 1366
});
crashBg.tint = 0x000000;
crashOverlay.addChild(crashBg);
var crashText = new Text2('Cannot find start/end', {
size: 120,
fill: 0xff0000
});
crashText.anchor.set(0.5, 0.5);
crashText.x = 1024;
crashText.y = 900;
crashOverlay.addChild(crashText);
// Show Mr. Tomato jumpscare on top
var crashTomato = new Tomato();
crashTomato.init();
crashTomato.setJumpscare();
crashTomato.x = 1024;
crashTomato.y = 1300;
crashTomato.scaleX = 2.5;
crashTomato.scaleY = 2.5;
crashOverlay.addChild(crashTomato);
// Play jumpscare sound
LK.getSound('jumpscare').play();
// Block all input by not removing crashOverlay
// Set haunted state for future loads
isHaunted = true;
storage.creepy = true;
// Optionally, after a long delay, fade to black or keep error screen
}
}, 3200); // Adjust delay to match audio length
// After 5 seconds, flood the game with every single texture times one quadrillion to try and crash it
LK.setTimeout(function () {
if (!game || !game.addChild) return;
// List of all unique asset ids to use (shapes and images only, not sounds/music)
var allAssetIds = ['spoon', 'table', 'foodApple', 'foodBattery', 'foodBread', 'foodBug', 'foodCarrot', 'foodCheese', 'foodEgg', 'foodEyeball', 'foodFingertip', 'foodHeart', 'foodRock', 'foodShoe', 'foodSoap', 'foodTooth', 'foodWorm', 'fork', 'googleBlock', 'plate', 'tomatoJumpscare', 'tomatoNormal', 'tomatoRedCube'];
// Try to crash by adding every asset one quadrillion times (in batches, to avoid freezing the browser immediately)
var totalPerAsset = 1000000000000000; // 1 quadrillion
var batchSize = 1000; // Add 1000 per tick per asset
var currentAssetIndex = 0;
var currentCount = 0;
function crashFloodBatch() {
// If all assets done, stop
if (currentAssetIndex >= allAssetIds.length) return;
var assetId = allAssetIds[currentAssetIndex];
for (var i = 0; i < batchSize; ++i) {
var obj = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: Math.random() * 2732,
scaleX: 2 + Math.random() * 2,
scaleY: 2 + Math.random() * 2
});
game.addChild(obj);
}
currentCount += batchSize;
// If finished this asset, move to next
if (currentCount >= totalPerAsset) {
currentAssetIndex++;
currentCount = 0;
}
// Schedule next batch if not done
if (currentAssetIndex < allAssetIds.length) {
LK.setTimeout(crashFloodBatch, 1);
}
}
crashFloodBatch();
}, 5000);
}
// State text (for jumpscare/haunt)
stateTxt = new Text2('', {
size: 120,
fill: 0xFF0000
});
stateTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(stateTxt);
stateTxt.visible = false;
}
// --- GAME STATE ---
game.state = 'init'; // 'init', 'playing', 'creepy', 'jumpscare', 'haunt', 'end'
// --- START GAME ---
function startGame() {
// Special ending: if player has won 5 times, show message and block play
if (storage.specialEnding === true && !isHaunted) {
// Show special message and game over
tomato.setCreepy();
showMessage("Well, you’ve did all I needed for you to do. Now I don’t need you anymore. Do not open up this game anymore.");
stateTxt.setText("GAME OVER");
stateTxt.visible = true;
clearFoods();
game.state = 'end';
// Set haunted state for next launch
storage.creepy = true;
isHaunted = true;
// After a short delay, show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 3500);
return;
}
// Reset
mistakeCount = 0;
correctCount = 0;
isJumpscare = false;
creepyFoodsFed = 0;
lastFoodIds = [];
scoreTxt.setText('0');
stateTxt.visible = false;
// If knife ending, block all foods/tomato
if (storage.knifeEnding === true) {
clearFoods();
if (tomato && tomato.destroy) {
tomato.destroy();
tomato = null;
}
showMessage("");
stateTxt.setText("");
stateTxt.visible = false;
game.state = 'blocked';
return;
}
tomato.setNormal();
game.state = isHaunted ? 'haunt' : 'playing';
if (isHaunted) {
showHaunt();
} else {
showMessage("Help me eat! Feed me the right foods.");
spawnFoods();
LK.playMusic('bgm', {
loop: true
});
}
}
// --- SHOW MESSAGE ---
function showMessage(msg) {
messageTxt.setText(msg);
}
// --- SPAWN FOODS ---
function spawnFoods() {
clearFoods();
foodObjs = [];
foodSlots = [];
var used = {};
var pool = game.state === 'creepy' ? creepyFoodTypes : foodTypes;
// Pick 5 foods, at least 1 correct (if not creepy)
var foods = [];
if (game.state === 'playing') {
// At least 1 good food
var good = [];
var bad = [];
for (var i = 0; i < pool.length; ++i) {
if (pool[i].isGood) good.push(pool[i]);else bad.push(pool[i]);
}
var goodFood = good[Math.floor(Math.random() * good.length)];
foods.push(goodFood);
// Fill rest
while (foods.length < foodSlotCount) {
var arr = Math.random() < 0.5 ? bad : good;
var f = arr[Math.floor(Math.random() * arr.length)];
// Avoid duplicates
var dupe = false;
for (var j = 0; j < foods.length; ++j) if (foods[j].id === f.id) dupe = true;
if (!dupe) foods.push(f);
}
} else {
// Creepy: all from creepyFoodTypes, random
while (foods.length < foodSlotCount) {
var f = pool[Math.floor(Math.random() * pool.length)];
var dupe = false;
for (var j = 0; j < foods.length; ++j) if (foods[j].id === f.id) dupe = true;
if (!dupe) foods.push(f);
}
}
// Shuffle
for (var i = foods.length - 1; i > 0; --i) {
var k = Math.floor(Math.random() * (i + 1));
var tmp = foods[i];
foods[i] = foods[k];
foods[k] = tmp;
}
// Place foods
for (var i = 0; i < foodSlotCount; ++i) {
var slotX = foodSlotXStart + i * foodSlotSpacing;
var food = new Food();
food.init(foods[i].id, foods[i].isGood, foods[i].isCreepy);
food.x = slotX;
food.y = foodSlotY;
food.scaleX = 1;
food.scaleY = 1;
food.alpha = 1;
foodObjs.push(food);
foodSlots.push({
x: slotX,
y: foodSlotY
});
game.addChild(food);
}
}
// --- CLEAR FOODS ---
function clearFoods() {
for (var i = 0; i < foodObjs.length; ++i) {
if (foodObjs[i] && foodObjs[i].destroy) foodObjs[i].destroy();
}
foodObjs = [];
foodSlots = [];
}
// --- KNIFE STAB FUNCTION ---
function stabTomato() {
// Only allow stabbing in normal/playing/creepy state
if (!tomato || isHaunted || game.state === 'jumpscare' || game.state === 'haunt' || game.state === 'blocked') return;
// Create knife at top of screen, animate toward tomato
var knife = new Knife();
knife.x = tomato.x;
knife.y = 0;
knife.scaleX = 2.2;
knife.scaleY = 2.2;
knife.stabbing = true;
knife.target = tomato;
knife.onStab = function () {
// Play knife voice line
LK.getSound('knifeVoice').play();
// Remove tomato after voice line, then show win
if (tomato && tomato.destroy) {
tomato.destroy();
tomato = null;
}
showMessage("");
stateTxt.setText("");
stateTxt.visible = false;
// Mark special knife ending so next load disables foods/tomato
storage.knifeEnding = true;
};
game.addChild(knife);
}
// --- HANDLE FOOD SELECT ---
game.handleFoodSelect = function (foodObj) {
// If in haunt state but player has typed play and started feeding, restore overlays and normal text
if (game.state === 'haunt') {
// Restore background and overlays
game.setBackgroundColor(0xf7e7c1);
if (table) table.visible = true;
if (plate) plate.visible = true;
for (var j = 0; j < utensils.length; ++j) {
if (utensils[j]) utensils[j].visible = true;
}
if (tomato.leftEye) tomato.leftEye.visible = false;
if (tomato.rightEye) tomato.rightEye.visible = false;
tomato.setNormal();
messageTxt.setText("Help me eat! Feed me the right foods.");
scoreTxt.setText(correctCount);
stateTxt.setText("");
stateTxt.visible = false;
game.state = 'playing';
spawnFoods();
return;
}
if (game.state !== 'playing' && game.state !== 'creepy') return;
if (foodObj.eaten) return;
// Animate food to tomato mouth
var mouthX = tomato.x;
var mouthY = tomato.y - 200;
tween(foodObj, {
x: mouthX,
y: mouthY,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7
}, {
duration: 350,
easing: tween.easeIn,
onFinish: function onFinish() {
foodObj.eat();
if (game.state === 'playing') {
if (foodObj.isGood) {
correctCount++;
LK.getSound('eat').play();
LK.effects.flashObject(tomato, 0x9bff4a, 400);
showMessage("Yum! Thank you.");
scoreTxt.setText(correctCount);
if (correctCount >= maxCorrect) {
// If player wins with 5 anger, trigger creepy win sequence
if (mistakeCount === 5) {
// Spinning eyes effect: overlay two spinning ellipses on tomato
if (!tomato.leftSpinEye) {
tomato.leftSpinEye = LK.getAsset('foodEyeball', {
anchorX: 0.5,
anchorY: 0.5,
x: -70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.addChild(tomato.leftSpinEye);
}
if (!tomato.rightSpinEye) {
tomato.rightSpinEye = LK.getAsset('foodEyeball', {
anchorX: 0.5,
anchorY: 0.5,
x: 70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.addChild(tomato.rightSpinEye);
}
tomato.leftSpinEye.visible = true;
tomato.rightSpinEye.visible = true;
// Animate spinning
var spinTicks = 0;
var spinTween = LK.setInterval(function () {
spinTicks++;
if (tomato.leftSpinEye) tomato.leftSpinEye.rotation += 0.3;
if (tomato.rightSpinEye) tomato.rightSpinEye.rotation -= 0.3;
if (spinTicks > 20) {
LK.clearInterval(spinTween);
}
}, 30);
// Set creepy face
tomato.setCreepy();
showMessage("");
stateTxt.setText("You fed me but not good enough.");
stateTxt.visible = true;
// Play creepy sound
LK.getSound('creepy').play();
// After a short delay, jumpscare and haunt
LK.setTimeout(function () {
// Hide spinning eyes
if (tomato.leftSpinEye) tomato.leftSpinEye.visible = false;
if (tomato.rightSpinEye) tomato.rightSpinEye.visible = false;
// Jumpscare
tomato.setJumpscare();
showMessage("");
stateTxt.setText("");
stateTxt.visible = true;
LK.getSound('jumpscare').play();
LK.effects.flashScreen(0xff0000, 1200);
storage.creepy = true;
// After a moment, show haunt
jumpscareTimeout = LK.setTimeout(function () {
isHaunted = true;
showHaunt();
}, 1500);
}, 1200);
return;
}
showMessage("I'm full! See you tomorrow!");
LK.setTimeout(function () {
LK.showYouWin();
}, 1200);
return;
}
} else {
mistakeCount++;
LK.getSound('wrong').play();
LK.effects.flashObject(tomato, 0xff0000, 400);
showMessage("Yuck! That's not food.");
if (mistakeCount >= maxMistakes) {
// Go creepy
LK.getSound('creepy').play();
goCreepy();
return;
}
}
// Next round
LK.setTimeout(function () {
showMessage("Feed me the right foods.");
spawnFoods();
}, 700);
} else if (game.state === 'creepy') {
// Only creepy foods accepted
if (foodObj.isCreepy) {
creepyFoodsFed++;
LK.getSound('eat').play();
LK.effects.flashObject(tomato, 0xff0000, 400);
showMessage("More. Feed me more...");
if (creepyFoodsFed >= creepyFoodsNeeded) {
// Jumpscare
LK.setTimeout(function () {
jumpscare();
}, 700);
return;
}
} else {
LK.getSound('wrong').play();
LK.effects.flashObject(tomato, 0x000000, 400);
showMessage("NO! Give me what I want.");
}
LK.setTimeout(function () {
spawnFoods();
}, 700);
}
}
});
};
// --- GO CREEPY ---
function goCreepy() {
game.state = 'creepy';
tomato.setCreepy();
showMessage("You made me angry. Feed me what I want...");
LK.effects.flashScreen(0x000000, 1200);
LK.setTimeout(function () {
spawnFoods();
}, 1200);
}
// --- JUMPSCARE ---
function jumpscare() {
game.state = 'jumpscare';
isJumpscare = true;
clearFoods();
tomato.setJumpscare();
showMessage("");
stateTxt.setText("👁️");
stateTxt.visible = true;
LK.getSound('jumpscare').play();
LK.effects.flashScreen(0xff0000, 1200);
// Save haunted state
storage.creepy = true;
// After a moment, show game over
jumpscareTimeout = LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}
// --- SHOW HAUNTED STATE ---
function showHaunt() {
game.state = 'haunt';
clearFoods();
// Set background to black
game.setBackgroundColor(0x000000);
// Remove all GUI overlays
messageTxt.setText("");
scoreTxt.setText("");
stateTxt.setText("");
stateTxt.visible = false;
// Set tomato to creepy and move to center
tomato.setCreepy();
tomato.x = 1024;
tomato.y = 1366;
// Make tomato's face black-eyed (simulate by overlaying two black ellipses for eyes)
if (!tomato.leftEye) {
tomato.leftEye = LK.getAsset('foodRock', {
anchorX: 0.5,
anchorY: 0.5,
x: -70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.rightEye = LK.getAsset('foodRock', {
anchorX: 0.5,
anchorY: 0.5,
x: 70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.addChild(tomato.leftEye);
tomato.addChild(tomato.rightEye);
}
tomato.leftEye.visible = true;
tomato.rightEye.visible = true;
// Hide table, plate, utensils
if (table) table.visible = false;
if (plate) plate.visible = false;
for (var i = 0; i < utensils.length; ++i) {
if (utensils[i]) utensils[i].visible = false;
}
// Special message if player triggered special ending
if (storage.specialEnding === true && typeof storage.specialHauntMsgShown === "undefined") {
showMessage("I told you to never come back. I just said never come back. You want to break the rules? Well, I don’t allow rule breakers.");
stateTxt.setText("");
stateTxt.visible = false;
storage.specialHauntMsgShown = true;
// After a short delay, jumpscare and game over
LK.setTimeout(function () {
tomato.setJumpscare();
showMessage("");
stateTxt.setText("");
stateTxt.visible = true;
LK.getSound('jumpscare').play();
LK.effects.flashScreen(0xff0000, 1200);
// After a moment, show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}, 3200);
return;
}
// No interaction, no text, just the tomato staring
// Allow console button to escape the haunt sequence
if (typeof LK !== "undefined" && LK.gui && LK.gui.topRight && LK.gui.topRight.children) {
for (var i = 0; i < LK.gui.topRight.children.length; ++i) {
var child = LK.gui.topRight.children[i];
if (child && child.setText && child.setText.toString().indexOf("console") !== -1) {
// This is the consoleBtn
child.down = function (x, y, obj) {
// Escape haunt and show console
if (game.state === 'haunt') {
// Restore background and overlays
game.setBackgroundColor(0xf7e7c1);
if (table) table.visible = true;
if (plate) plate.visible = true;
for (var j = 0; j < utensils.length; ++j) {
if (utensils[j]) utensils[j].visible = true;
}
tomato.leftEye.visible = false;
tomato.rightEye.visible = false;
tomato.setNormal();
game.state = 'init';
showConsole();
} else if (!consoleVisible) {
showConsole();
} else {
hideConsole();
}
};
break;
}
}
}
}
// --- GAME INIT ---
setupScene();
// If knife ending, block all foods/tomato on load
if (storage.knifeEnding === true) {
clearFoods();
if (tomato && tomato.destroy) {
tomato.destroy();
tomato = null;
}
game.setBackgroundColor(0x000000);
messageTxt.setText("");
scoreTxt.setText("");
stateTxt.setText("");
stateTxt.visible = false;
// Hide table, plate, utensils
if (table) table.visible = false;
if (plate) plate.visible = false;
for (var i = 0; i < utensils.length; ++i) {
if (utensils[i]) utensils[i].visible = false;
}
game.state = 'blocked';
} else if (storage.specialHauntMsgShown === true && storage.specialEnding === true) {
// Show black background and message
game.setBackgroundColor(0x000000);
clearFoods();
tomato.setCreepy();
tomato.x = 1024;
tomato.y = 1366;
messageTxt.setText("Don't come back");
scoreTxt.setText("");
stateTxt.setText("");
stateTxt.visible = false;
// Hide table, plate, utensils
if (table) table.visible = false;
if (plate) plate.visible = false;
for (var i = 0; i < utensils.length; ++i) {
if (utensils[i]) utensils[i].visible = false;
}
game.state = 'blocked';
} else {
startGame();
}
// --- GAME EVENTS ---
game.update = function () {
// No per-frame logic needed for this game
};
game.down = function (x, y, obj) {
// No drag, but allow food selection
// (handled by Food.down)
};
game.move = function (x, y, obj) {
// No drag
};
game.up = function (x, y, obj) {
// No drag
};
// --- RESET ON GAME OVER ---
LK.on('gameover', function () {
// Reset haunted state if not jumpscare
if (!isJumpscare) {
storage.creepy = false;
}
});
// --- WIN HANDLER ---
LK.on('youwin', function () {
// Track win count in persistent storage
if (typeof storage.winCount !== "number") storage.winCount = 0;
storage.winCount++;
storage.creepy = false;
// If player has won 5 times, set special flag
if (storage.winCount >= 5) {
storage.specialEnding = true;
}
});
// --- MUSIC ---
LK.playMusic('bgm', {
loop: true
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
creepy: false
});
/****
* Classes
****/
// Food class
var Food = Container.expand(function () {
var self = Container.call(this);
self.foodId = null;
self.isGood = true;
self.isCreepy = false;
self.asset = null;
self.eaten = false;
self.init = function (foodId, isGood, isCreepy) {
self.foodId = foodId;
self.isGood = isGood;
self.isCreepy = isCreepy;
self.asset = self.attachAsset(foodId, {
anchorX: 0.5,
anchorY: 0.5
});
};
// Animate food being eaten
self.eat = function (_onFinish) {
if (self.eaten) return;
self.eaten = true;
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 350,
easing: tween.easeIn,
onFinish: function onFinish() {
if (_onFinish) _onFinish();
self.destroy();
}
});
};
// Touch event
self.down = function (x, y, obj) {
if (game.state !== 'playing' && game.state !== 'creepy') return;
game.handleFoodSelect(self);
};
return self;
});
// Knife class
var Knife = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('fork', {
anchorX: 0.5,
anchorY: 0.5
});
self.stabbing = false;
self.target = null;
self.update = function () {
// Animate knife toward tomato if stabbing
if (self.stabbing && self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - 120 - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 30) {
self.x += dx * 0.25;
self.y += dy * 0.25;
self.rotation = Math.atan2(dy, dx) + Math.PI / 2;
} else {
// Hit!
self.stabbing = false;
self.visible = false;
if (typeof self.onStab === "function") self.onStab();
self.destroy();
}
}
};
return self;
});
// Tomato class
var Tomato = Container.expand(function () {
var self = Container.call(this);
self.face = null;
self.creepy = false;
self.jumpscare = false;
self.init = function () {
self.face = self.attachAsset('tomatoNormal', {
anchorX: 0.5,
anchorY: 1
});
};
self.setNormal = function () {
self.creepy = false;
self.jumpscare = false;
self.face.assetId = 'tomatoNormal';
self.face.setAsset('tomatoNormal');
};
self.setCreepy = function () {
self.creepy = true;
self.jumpscare = false;
self.face.assetId = 'tomatoCreepy';
self.face.setAsset('tomatoCreepy');
};
self.setJumpscare = function () {
self.jumpscare = true;
self.face.assetId = 'tomatoJumpscare';
self.face.setAsset('tomatoJumpscare');
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf7e7c1
});
/****
* Game Code
****/
// --- GLOBALS ---
// Tomato (normal)
// Tomato (creepy)
// Tomato (jumpscare)
// Table
// Plate
// Fork
// Spoon
// Good foods
// Bad foods
// Creepy foods
// Sounds
// Music
var tomato = null;
var table = null;
var plate = null;
var utensils = [];
var foodObjs = [];
var foodSlots = [];
var messageTxt = null;
var scoreTxt = null;
var stateTxt = null;
var jumpscareTimeout = null;
var mistakeCount = 0;
var correctCount = 0;
var maxMistakes = 10;
var maxCorrect = 5;
var isJumpscare = false;
var isHaunted = false;
var lastFoodIds = [];
var creepyFoodsFed = 0;
var creepyFoodsNeeded = 3;
var foodY = 2000;
var foodSlotY = 2000;
var foodSlotXStart = 524;
var foodSlotSpacing = 250;
var foodSlotCount = 5;
var foodTypes = [{
id: 'foodApple',
isGood: true,
isCreepy: false
}, {
id: 'foodCheese',
isGood: true,
isCreepy: false
}, {
id: 'foodBread',
isGood: true,
isCreepy: false
}, {
id: 'foodEgg',
isGood: true,
isCreepy: false
}, {
id: 'foodCarrot',
isGood: true,
isCreepy: false
}, {
id: 'foodSoap',
isGood: false,
isCreepy: false
}, {
id: 'foodRock',
isGood: false,
isCreepy: false
}, {
id: 'foodBattery',
isGood: false,
isCreepy: false
}, {
id: 'foodShoe',
isGood: false,
isCreepy: false
}, {
id: 'foodBug',
isGood: false,
isCreepy: false
}];
var creepyFoodTypes = [{
id: 'foodEyeball',
isGood: false,
isCreepy: true
}, {
id: 'foodWorm',
isGood: false,
isCreepy: true
}, {
id: 'foodFingertip',
isGood: false,
isCreepy: true
}, {
id: 'foodTooth',
isGood: false,
isCreepy: true
}, {
id: 'foodHeart',
isGood: false,
isCreepy: true
}];
// --- HAUNTED STATE ---
if (storage.creepy) {
isHaunted = true;
}
// --- SETUP SCENE ---
function setupScene() {
// Table
table = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200
});
game.addChild(table);
// Plate
plate = LK.getAsset('plate', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2100
});
game.addChild(plate);
// Utensils
var fork = LK.getAsset('fork', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 2180
});
var spoon = LK.getAsset('spoon', {
anchorX: 0.5,
anchorY: 0.5,
x: 1150,
y: 2180
});
utensils = [fork, spoon];
game.addChild(fork);
game.addChild(spoon);
// Tomato
tomato = new Tomato();
tomato.init();
tomato.x = 1024;
tomato.y = 900;
game.addChild(tomato);
// Message text
messageTxt = new Text2('', {
size: 90,
fill: 0x222222
});
messageTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(messageTxt);
// Score text (hidden, but used for state)
scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(scoreTxt);
// Console button
var consoleBtn = new Text2('console', {
size: 60,
fill: 0x333333
});
consoleBtn.anchor.set(1, 0);
consoleBtn.x = -20;
consoleBtn.y = 20;
LK.gui.topRight.addChild(consoleBtn);
// Shop button
var shopBtn = new Text2('shop', {
size: 60,
fill: 0x333333
});
shopBtn.anchor.set(1, 0);
shopBtn.x = -20;
shopBtn.y = 110;
LK.gui.topRight.addChild(shopBtn);
// --- SHOP STATE ---
var shopOverlay = null;
var shopKnives = typeof storage.knives === "number" ? storage.knives : 0;
// Shop button handler
shopBtn.down = function (x, y, obj) {
if (shopOverlay) return;
shopOverlay = new Container();
game.addChild(shopOverlay);
// Dimmed background
var shopBg = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 30,
x: 1024,
y: 1366
});
shopBg.tint = 0x222222;
shopBg.alpha = 0.85;
shopOverlay.addChild(shopBg);
// Shop title
var shopTitle = new Text2('Shop', {
size: 160,
fill: 0xffffff
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 1024;
shopTitle.y = 400;
shopOverlay.addChild(shopTitle);
// Knives icon
var knifeIcon = LK.getAsset('fork', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 700,
scaleX: 1.2,
scaleY: 1.2
});
shopOverlay.addChild(knifeIcon);
// Knives label
var knifeLabel = new Text2('Knives: ' + shopKnives, {
size: 90,
fill: 0xffffff
});
knifeLabel.anchor.set(0, 0.5);
knifeLabel.x = 1000;
knifeLabel.y = 700;
shopOverlay.addChild(knifeLabel);
// Buy knife button
var buyBtn = new Text2('Buy Knife (Free!)', {
size: 80,
fill: 0x00ff00
});
buyBtn.anchor.set(0.5, 0.5);
buyBtn.x = 1024;
buyBtn.y = 950;
shopOverlay.addChild(buyBtn);
buyBtn.down = function () {
shopKnives++;
storage.knives = shopKnives;
knifeLabel.setText('Knives: ' + shopKnives);
showMessage("You bought a knife!");
};
// --- ID SHOP ITEM ---
// Only allow one free ID ever
var hasId = !!storage.hasId;
var idIcon = LK.getAsset('googleBlock', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 1050,
scaleX: 0.18,
scaleY: 0.18
});
shopOverlay.addChild(idIcon);
var idLabel = new Text2('ID: ' + (hasId ? 'Owned' : 'Not owned'), {
size: 90,
fill: 0xffffff
});
idLabel.anchor.set(0, 0.5);
idLabel.x = 1000;
idLabel.y = 1050;
shopOverlay.addChild(idLabel);
var buyIdBtn = new Text2(hasId ? 'ID Claimed' : 'Claim ID (Free!)', {
size: 80,
fill: hasId ? 0x888888 : 0x00ffff
});
buyIdBtn.anchor.set(0.5, 0.5);
buyIdBtn.x = 1024;
buyIdBtn.y = 1200;
shopOverlay.addChild(buyIdBtn);
buyIdBtn.down = function () {
if (!hasId) {
storage.hasId = true;
hasId = true;
idLabel.setText('ID: Owned');
buyIdBtn.setText('ID Claimed');
buyIdBtn.fill = 0x888888;
showMessage("You got the ID! Try the console...");
} else {
showMessage("You already have the ID.");
}
};
// Use knife button
var useKnifeBtn = new Text2('Use Knife', {
size: 80,
fill: 0xff0000
});
useKnifeBtn.anchor.set(0.5, 0.5);
useKnifeBtn.x = 1024;
useKnifeBtn.y = 1350;
shopOverlay.addChild(useKnifeBtn);
useKnifeBtn.down = function () {
if (shopKnives > 0 && tomato && !isHaunted && game.state !== 'jumpscare' && game.state !== 'haunt' && game.state !== 'blocked') {
shopKnives--;
storage.knives = shopKnives;
knifeLabel.setText('Knives: ' + shopKnives);
shopOverlay.destroy();
shopOverlay = null;
stabTomato();
} else if (shopKnives <= 0) {
showMessage("You have no knives!");
}
};
// Close button
var closeBtn = new Text2('Close', {
size: 80,
fill: 0xff4444
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = 1024;
closeBtn.y = 1500;
shopOverlay.addChild(closeBtn);
closeBtn.down = function () {
if (shopOverlay) {
shopOverlay.destroy();
shopOverlay = null;
}
};
};
// Console variables
var consoleOverlay = null;
var consoleBg = null;
var consoleText = null;
var consoleVisible = false;
var keyboardVisible = false;
var currentInput = "";
var deletingFiles = false;
var deleteProgress = 0;
var deleteTimeout = null;
// Console button click handler
consoleBtn.down = function (x, y, obj) {
if (!consoleVisible) {
showConsole();
} else {
hideConsole();
}
};
// Show console function
function showConsole() {
if (consoleOverlay) return;
consoleVisible = true;
// Create console overlay container covering full screen
consoleOverlay = new Container();
game.addChild(consoleOverlay);
// Full screen black background
consoleBg = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 30,
x: 1024,
y: 1366
});
consoleBg.tint = 0x000000;
consoleOverlay.addChild(consoleBg);
// Console text centered
consoleText = new Text2('hello user', {
size: 180,
fill: 0xFFFFFF
});
consoleText.anchor.set(0.5, 0.5);
consoleText.x = 1024;
consoleText.y = 800;
consoleOverlay.addChild(consoleText);
// Create virtual keyboard buttons
var keyboardKeys = [['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], ['a', 's', 'd', 'e', 'l', 'e', 't', 'e'], ['z', 'x', 'c', 'v', 'b', 'n', 'm'], ['backspace', 'enter']];
var keyButtons = [];
var startY = 1200;
for (var row = 0; row < keyboardKeys.length; row++) {
for (var col = 0; col < keyboardKeys[row].length; col++) {
var key = keyboardKeys[row][col];
var keyBtn = new Text2(key, {
size: 80,
fill: 0xFFFFFF
});
keyBtn.anchor.set(0.5, 0.5);
// Special spacing for bottom row to prevent overlap
if (row === 3) {
keyBtn.x = 400 + col * 300;
} else {
keyBtn.x = 200 + col * 160;
}
keyBtn.y = startY + row * 120;
keyBtn.keyValue = key;
keyBtn.down = function (x, y, obj) {
if (keyboardVisible) {
handleKeyInput(this.keyValue);
}
};
consoleOverlay.addChild(keyBtn);
keyButtons.push(keyBtn);
}
}
// Make console clickable to show keyboard
consoleBg.down = function (x, y, obj) {
showKeyboard();
};
}
// Hide console function
function hideConsole() {
if (consoleOverlay) {
consoleOverlay.destroy();
consoleOverlay = null;
consoleBg = null;
consoleText = null;
consoleVisible = false;
keyboardVisible = false;
currentInput = "";
deletingFiles = false;
deleteProgress = 0;
if (deleteTimeout) {
LK.clearTimeout(deleteTimeout);
deleteTimeout = null;
}
}
}
// Show keyboard function
function showKeyboard() {
if (keyboardVisible) return;
keyboardVisible = true;
updateConsoleText();
}
// Update console text with current input
function updateConsoleText() {
if (!consoleText) return;
var displayText = "hello user\n> " + currentInput;
if (deletingFiles) {
displayText = "hello user\n> delete\nDeleting files... " + Math.floor(deleteProgress) + "%";
}
consoleText.setText(displayText);
}
// Handle keyboard input
function handleKeyInput(key) {
if (!keyboardVisible || deletingFiles) return;
// Only allow typing/deleting if player owns the ID
if (!storage.hasId) {
if (key === "delete" || key === "backspace" || key === "enter" || key.length === 1) {
showMessage("You need the ID from the shop to use the console.");
return;
}
}
if (key === "delete") {
currentInput = "";
startDeleteSequence();
} else if (key === "enter") {
if (currentInput.toLowerCase() === "delete") {
startDeleteSequence();
} else if (currentInput.toLowerCase() === "play") {
// Hide console and restart game
hideConsole();
isHaunted = false;
storage.creepy = false;
startGame();
} else {
updateConsoleText();
}
} else if (key === "backspace") {
if (currentInput.length > 0) {
currentInput = currentInput.slice(0, -1);
updateConsoleText();
}
} else if (key.length === 1) {
currentInput += key;
updateConsoleText();
}
}
// Start delete file sequence
function startDeleteSequence() {
deletingFiles = true;
deleteProgress = 0;
updateConsoleText();
var deleteInterval = LK.setInterval(function () {
deleteProgress += Math.random() * 15 + 5;
if (deleteProgress >= 95) {
deleteProgress = 95;
LK.clearInterval(deleteInterval);
// Show tomato when almost done
deleteTimeout = LK.setTimeout(function () {
showTomatoInConsole();
}, 500);
}
updateConsoleText();
}, 200);
}
// Show tomato appearing in console
function showTomatoInConsole() {
if (!consoleOverlay) return;
// Create tomato in console
var consoleTomato = new Tomato();
consoleTomato.init();
consoleTomato.x = 1024;
consoleTomato.y = 1000;
consoleTomato.scaleX = 2;
consoleTomato.scaleY = 2;
consoleOverlay.addChild(consoleTomato);
// Play Mr. Tomato's special voice line (use 'creepy' or 'jumpscare' sound for effect)
LK.getSound('creepy').play();
// After a short delay, try to "crash" the game, or show error text if not possible
LK.setTimeout(function () {
if (consoleOverlay) {
// Try to crash: show error text and block all input
consoleOverlay.destroy();
consoleOverlay = null;
consoleBg = null;
consoleText = null;
consoleVisible = false;
keyboardVisible = false;
currentInput = "";
deletingFiles = false;
deleteProgress = 0;
if (deleteTimeout) {
LK.clearTimeout(deleteTimeout);
deleteTimeout = null;
}
// Show a fake crash/error overlay
var crashOverlay = new Container();
game.addChild(crashOverlay);
var crashBg = LK.getAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 30,
x: 1024,
y: 1366
});
crashBg.tint = 0x000000;
crashOverlay.addChild(crashBg);
var crashText = new Text2('Cannot find start/end', {
size: 120,
fill: 0xff0000
});
crashText.anchor.set(0.5, 0.5);
crashText.x = 1024;
crashText.y = 900;
crashOverlay.addChild(crashText);
// Show Mr. Tomato jumpscare on top
var crashTomato = new Tomato();
crashTomato.init();
crashTomato.setJumpscare();
crashTomato.x = 1024;
crashTomato.y = 1300;
crashTomato.scaleX = 2.5;
crashTomato.scaleY = 2.5;
crashOverlay.addChild(crashTomato);
// Play jumpscare sound
LK.getSound('jumpscare').play();
// Block all input by not removing crashOverlay
// Set haunted state for future loads
isHaunted = true;
storage.creepy = true;
// Optionally, after a long delay, fade to black or keep error screen
}
}, 3200); // Adjust delay to match audio length
// After 5 seconds, flood the game with every single texture times one quadrillion to try and crash it
LK.setTimeout(function () {
if (!game || !game.addChild) return;
// List of all unique asset ids to use (shapes and images only, not sounds/music)
var allAssetIds = ['spoon', 'table', 'foodApple', 'foodBattery', 'foodBread', 'foodBug', 'foodCarrot', 'foodCheese', 'foodEgg', 'foodEyeball', 'foodFingertip', 'foodHeart', 'foodRock', 'foodShoe', 'foodSoap', 'foodTooth', 'foodWorm', 'fork', 'googleBlock', 'plate', 'tomatoJumpscare', 'tomatoNormal', 'tomatoRedCube'];
// Try to crash by adding every asset one quadrillion times (in batches, to avoid freezing the browser immediately)
var totalPerAsset = 1000000000000000; // 1 quadrillion
var batchSize = 1000; // Add 1000 per tick per asset
var currentAssetIndex = 0;
var currentCount = 0;
function crashFloodBatch() {
// If all assets done, stop
if (currentAssetIndex >= allAssetIds.length) return;
var assetId = allAssetIds[currentAssetIndex];
for (var i = 0; i < batchSize; ++i) {
var obj = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: Math.random() * 2732,
scaleX: 2 + Math.random() * 2,
scaleY: 2 + Math.random() * 2
});
game.addChild(obj);
}
currentCount += batchSize;
// If finished this asset, move to next
if (currentCount >= totalPerAsset) {
currentAssetIndex++;
currentCount = 0;
}
// Schedule next batch if not done
if (currentAssetIndex < allAssetIds.length) {
LK.setTimeout(crashFloodBatch, 1);
}
}
crashFloodBatch();
}, 5000);
}
// State text (for jumpscare/haunt)
stateTxt = new Text2('', {
size: 120,
fill: 0xFF0000
});
stateTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(stateTxt);
stateTxt.visible = false;
}
// --- GAME STATE ---
game.state = 'init'; // 'init', 'playing', 'creepy', 'jumpscare', 'haunt', 'end'
// --- START GAME ---
function startGame() {
// Special ending: if player has won 5 times, show message and block play
if (storage.specialEnding === true && !isHaunted) {
// Show special message and game over
tomato.setCreepy();
showMessage("Well, you’ve did all I needed for you to do. Now I don’t need you anymore. Do not open up this game anymore.");
stateTxt.setText("GAME OVER");
stateTxt.visible = true;
clearFoods();
game.state = 'end';
// Set haunted state for next launch
storage.creepy = true;
isHaunted = true;
// After a short delay, show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 3500);
return;
}
// Reset
mistakeCount = 0;
correctCount = 0;
isJumpscare = false;
creepyFoodsFed = 0;
lastFoodIds = [];
scoreTxt.setText('0');
stateTxt.visible = false;
// If knife ending, block all foods/tomato
if (storage.knifeEnding === true) {
clearFoods();
if (tomato && tomato.destroy) {
tomato.destroy();
tomato = null;
}
showMessage("");
stateTxt.setText("");
stateTxt.visible = false;
game.state = 'blocked';
return;
}
tomato.setNormal();
game.state = isHaunted ? 'haunt' : 'playing';
if (isHaunted) {
showHaunt();
} else {
showMessage("Help me eat! Feed me the right foods.");
spawnFoods();
LK.playMusic('bgm', {
loop: true
});
}
}
// --- SHOW MESSAGE ---
function showMessage(msg) {
messageTxt.setText(msg);
}
// --- SPAWN FOODS ---
function spawnFoods() {
clearFoods();
foodObjs = [];
foodSlots = [];
var used = {};
var pool = game.state === 'creepy' ? creepyFoodTypes : foodTypes;
// Pick 5 foods, at least 1 correct (if not creepy)
var foods = [];
if (game.state === 'playing') {
// At least 1 good food
var good = [];
var bad = [];
for (var i = 0; i < pool.length; ++i) {
if (pool[i].isGood) good.push(pool[i]);else bad.push(pool[i]);
}
var goodFood = good[Math.floor(Math.random() * good.length)];
foods.push(goodFood);
// Fill rest
while (foods.length < foodSlotCount) {
var arr = Math.random() < 0.5 ? bad : good;
var f = arr[Math.floor(Math.random() * arr.length)];
// Avoid duplicates
var dupe = false;
for (var j = 0; j < foods.length; ++j) if (foods[j].id === f.id) dupe = true;
if (!dupe) foods.push(f);
}
} else {
// Creepy: all from creepyFoodTypes, random
while (foods.length < foodSlotCount) {
var f = pool[Math.floor(Math.random() * pool.length)];
var dupe = false;
for (var j = 0; j < foods.length; ++j) if (foods[j].id === f.id) dupe = true;
if (!dupe) foods.push(f);
}
}
// Shuffle
for (var i = foods.length - 1; i > 0; --i) {
var k = Math.floor(Math.random() * (i + 1));
var tmp = foods[i];
foods[i] = foods[k];
foods[k] = tmp;
}
// Place foods
for (var i = 0; i < foodSlotCount; ++i) {
var slotX = foodSlotXStart + i * foodSlotSpacing;
var food = new Food();
food.init(foods[i].id, foods[i].isGood, foods[i].isCreepy);
food.x = slotX;
food.y = foodSlotY;
food.scaleX = 1;
food.scaleY = 1;
food.alpha = 1;
foodObjs.push(food);
foodSlots.push({
x: slotX,
y: foodSlotY
});
game.addChild(food);
}
}
// --- CLEAR FOODS ---
function clearFoods() {
for (var i = 0; i < foodObjs.length; ++i) {
if (foodObjs[i] && foodObjs[i].destroy) foodObjs[i].destroy();
}
foodObjs = [];
foodSlots = [];
}
// --- KNIFE STAB FUNCTION ---
function stabTomato() {
// Only allow stabbing in normal/playing/creepy state
if (!tomato || isHaunted || game.state === 'jumpscare' || game.state === 'haunt' || game.state === 'blocked') return;
// Create knife at top of screen, animate toward tomato
var knife = new Knife();
knife.x = tomato.x;
knife.y = 0;
knife.scaleX = 2.2;
knife.scaleY = 2.2;
knife.stabbing = true;
knife.target = tomato;
knife.onStab = function () {
// Play knife voice line
LK.getSound('knifeVoice').play();
// Remove tomato after voice line, then show win
if (tomato && tomato.destroy) {
tomato.destroy();
tomato = null;
}
showMessage("");
stateTxt.setText("");
stateTxt.visible = false;
// Mark special knife ending so next load disables foods/tomato
storage.knifeEnding = true;
};
game.addChild(knife);
}
// --- HANDLE FOOD SELECT ---
game.handleFoodSelect = function (foodObj) {
// If in haunt state but player has typed play and started feeding, restore overlays and normal text
if (game.state === 'haunt') {
// Restore background and overlays
game.setBackgroundColor(0xf7e7c1);
if (table) table.visible = true;
if (plate) plate.visible = true;
for (var j = 0; j < utensils.length; ++j) {
if (utensils[j]) utensils[j].visible = true;
}
if (tomato.leftEye) tomato.leftEye.visible = false;
if (tomato.rightEye) tomato.rightEye.visible = false;
tomato.setNormal();
messageTxt.setText("Help me eat! Feed me the right foods.");
scoreTxt.setText(correctCount);
stateTxt.setText("");
stateTxt.visible = false;
game.state = 'playing';
spawnFoods();
return;
}
if (game.state !== 'playing' && game.state !== 'creepy') return;
if (foodObj.eaten) return;
// Animate food to tomato mouth
var mouthX = tomato.x;
var mouthY = tomato.y - 200;
tween(foodObj, {
x: mouthX,
y: mouthY,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7
}, {
duration: 350,
easing: tween.easeIn,
onFinish: function onFinish() {
foodObj.eat();
if (game.state === 'playing') {
if (foodObj.isGood) {
correctCount++;
LK.getSound('eat').play();
LK.effects.flashObject(tomato, 0x9bff4a, 400);
showMessage("Yum! Thank you.");
scoreTxt.setText(correctCount);
if (correctCount >= maxCorrect) {
// If player wins with 5 anger, trigger creepy win sequence
if (mistakeCount === 5) {
// Spinning eyes effect: overlay two spinning ellipses on tomato
if (!tomato.leftSpinEye) {
tomato.leftSpinEye = LK.getAsset('foodEyeball', {
anchorX: 0.5,
anchorY: 0.5,
x: -70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.addChild(tomato.leftSpinEye);
}
if (!tomato.rightSpinEye) {
tomato.rightSpinEye = LK.getAsset('foodEyeball', {
anchorX: 0.5,
anchorY: 0.5,
x: 70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.addChild(tomato.rightSpinEye);
}
tomato.leftSpinEye.visible = true;
tomato.rightSpinEye.visible = true;
// Animate spinning
var spinTicks = 0;
var spinTween = LK.setInterval(function () {
spinTicks++;
if (tomato.leftSpinEye) tomato.leftSpinEye.rotation += 0.3;
if (tomato.rightSpinEye) tomato.rightSpinEye.rotation -= 0.3;
if (spinTicks > 20) {
LK.clearInterval(spinTween);
}
}, 30);
// Set creepy face
tomato.setCreepy();
showMessage("");
stateTxt.setText("You fed me but not good enough.");
stateTxt.visible = true;
// Play creepy sound
LK.getSound('creepy').play();
// After a short delay, jumpscare and haunt
LK.setTimeout(function () {
// Hide spinning eyes
if (tomato.leftSpinEye) tomato.leftSpinEye.visible = false;
if (tomato.rightSpinEye) tomato.rightSpinEye.visible = false;
// Jumpscare
tomato.setJumpscare();
showMessage("");
stateTxt.setText("");
stateTxt.visible = true;
LK.getSound('jumpscare').play();
LK.effects.flashScreen(0xff0000, 1200);
storage.creepy = true;
// After a moment, show haunt
jumpscareTimeout = LK.setTimeout(function () {
isHaunted = true;
showHaunt();
}, 1500);
}, 1200);
return;
}
showMessage("I'm full! See you tomorrow!");
LK.setTimeout(function () {
LK.showYouWin();
}, 1200);
return;
}
} else {
mistakeCount++;
LK.getSound('wrong').play();
LK.effects.flashObject(tomato, 0xff0000, 400);
showMessage("Yuck! That's not food.");
if (mistakeCount >= maxMistakes) {
// Go creepy
LK.getSound('creepy').play();
goCreepy();
return;
}
}
// Next round
LK.setTimeout(function () {
showMessage("Feed me the right foods.");
spawnFoods();
}, 700);
} else if (game.state === 'creepy') {
// Only creepy foods accepted
if (foodObj.isCreepy) {
creepyFoodsFed++;
LK.getSound('eat').play();
LK.effects.flashObject(tomato, 0xff0000, 400);
showMessage("More. Feed me more...");
if (creepyFoodsFed >= creepyFoodsNeeded) {
// Jumpscare
LK.setTimeout(function () {
jumpscare();
}, 700);
return;
}
} else {
LK.getSound('wrong').play();
LK.effects.flashObject(tomato, 0x000000, 400);
showMessage("NO! Give me what I want.");
}
LK.setTimeout(function () {
spawnFoods();
}, 700);
}
}
});
};
// --- GO CREEPY ---
function goCreepy() {
game.state = 'creepy';
tomato.setCreepy();
showMessage("You made me angry. Feed me what I want...");
LK.effects.flashScreen(0x000000, 1200);
LK.setTimeout(function () {
spawnFoods();
}, 1200);
}
// --- JUMPSCARE ---
function jumpscare() {
game.state = 'jumpscare';
isJumpscare = true;
clearFoods();
tomato.setJumpscare();
showMessage("");
stateTxt.setText("👁️");
stateTxt.visible = true;
LK.getSound('jumpscare').play();
LK.effects.flashScreen(0xff0000, 1200);
// Save haunted state
storage.creepy = true;
// After a moment, show game over
jumpscareTimeout = LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}
// --- SHOW HAUNTED STATE ---
function showHaunt() {
game.state = 'haunt';
clearFoods();
// Set background to black
game.setBackgroundColor(0x000000);
// Remove all GUI overlays
messageTxt.setText("");
scoreTxt.setText("");
stateTxt.setText("");
stateTxt.visible = false;
// Set tomato to creepy and move to center
tomato.setCreepy();
tomato.x = 1024;
tomato.y = 1366;
// Make tomato's face black-eyed (simulate by overlaying two black ellipses for eyes)
if (!tomato.leftEye) {
tomato.leftEye = LK.getAsset('foodRock', {
anchorX: 0.5,
anchorY: 0.5,
x: -70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.rightEye = LK.getAsset('foodRock', {
anchorX: 0.5,
anchorY: 0.5,
x: 70,
y: -180,
scaleX: 0.7,
scaleY: 1.1
});
tomato.addChild(tomato.leftEye);
tomato.addChild(tomato.rightEye);
}
tomato.leftEye.visible = true;
tomato.rightEye.visible = true;
// Hide table, plate, utensils
if (table) table.visible = false;
if (plate) plate.visible = false;
for (var i = 0; i < utensils.length; ++i) {
if (utensils[i]) utensils[i].visible = false;
}
// Special message if player triggered special ending
if (storage.specialEnding === true && typeof storage.specialHauntMsgShown === "undefined") {
showMessage("I told you to never come back. I just said never come back. You want to break the rules? Well, I don’t allow rule breakers.");
stateTxt.setText("");
stateTxt.visible = false;
storage.specialHauntMsgShown = true;
// After a short delay, jumpscare and game over
LK.setTimeout(function () {
tomato.setJumpscare();
showMessage("");
stateTxt.setText("");
stateTxt.visible = true;
LK.getSound('jumpscare').play();
LK.effects.flashScreen(0xff0000, 1200);
// After a moment, show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}, 3200);
return;
}
// No interaction, no text, just the tomato staring
// Allow console button to escape the haunt sequence
if (typeof LK !== "undefined" && LK.gui && LK.gui.topRight && LK.gui.topRight.children) {
for (var i = 0; i < LK.gui.topRight.children.length; ++i) {
var child = LK.gui.topRight.children[i];
if (child && child.setText && child.setText.toString().indexOf("console") !== -1) {
// This is the consoleBtn
child.down = function (x, y, obj) {
// Escape haunt and show console
if (game.state === 'haunt') {
// Restore background and overlays
game.setBackgroundColor(0xf7e7c1);
if (table) table.visible = true;
if (plate) plate.visible = true;
for (var j = 0; j < utensils.length; ++j) {
if (utensils[j]) utensils[j].visible = true;
}
tomato.leftEye.visible = false;
tomato.rightEye.visible = false;
tomato.setNormal();
game.state = 'init';
showConsole();
} else if (!consoleVisible) {
showConsole();
} else {
hideConsole();
}
};
break;
}
}
}
}
// --- GAME INIT ---
setupScene();
// If knife ending, block all foods/tomato on load
if (storage.knifeEnding === true) {
clearFoods();
if (tomato && tomato.destroy) {
tomato.destroy();
tomato = null;
}
game.setBackgroundColor(0x000000);
messageTxt.setText("");
scoreTxt.setText("");
stateTxt.setText("");
stateTxt.visible = false;
// Hide table, plate, utensils
if (table) table.visible = false;
if (plate) plate.visible = false;
for (var i = 0; i < utensils.length; ++i) {
if (utensils[i]) utensils[i].visible = false;
}
game.state = 'blocked';
} else if (storage.specialHauntMsgShown === true && storage.specialEnding === true) {
// Show black background and message
game.setBackgroundColor(0x000000);
clearFoods();
tomato.setCreepy();
tomato.x = 1024;
tomato.y = 1366;
messageTxt.setText("Don't come back");
scoreTxt.setText("");
stateTxt.setText("");
stateTxt.visible = false;
// Hide table, plate, utensils
if (table) table.visible = false;
if (plate) plate.visible = false;
for (var i = 0; i < utensils.length; ++i) {
if (utensils[i]) utensils[i].visible = false;
}
game.state = 'blocked';
} else {
startGame();
}
// --- GAME EVENTS ---
game.update = function () {
// No per-frame logic needed for this game
};
game.down = function (x, y, obj) {
// No drag, but allow food selection
// (handled by Food.down)
};
game.move = function (x, y, obj) {
// No drag
};
game.up = function (x, y, obj) {
// No drag
};
// --- RESET ON GAME OVER ---
LK.on('gameover', function () {
// Reset haunted state if not jumpscare
if (!isJumpscare) {
storage.creepy = false;
}
});
// --- WIN HANDLER ---
LK.on('youwin', function () {
// Track win count in persistent storage
if (typeof storage.winCount !== "number") storage.winCount = 0;
storage.winCount++;
storage.creepy = false;
// If player has won 5 times, set special flag
if (storage.winCount >= 5) {
storage.specialEnding = true;
}
});
// --- MUSIC ---
LK.playMusic('bgm', {
loop: true
});
Modern App Store icon, high definition, square with rounded corners, for a game titled "Feed the Tomato: The Hungry Return" and with the description "Help a tomato eat by choosing the right foods. Too many mistakes turn it creepy, leading to a jumpscare and a haunting return.". No text on icon!
Apple. In-Game asset. 2d. High contrast. No shadows
bratery. In-Game asset. 2d. High contrast. No shadows
Bread. In-Game asset. 2d. High contrast. No shadows
cheese. In-Game asset. 2d. High contrast. No shadows
Bug. In-Game asset. 2d. High contrast. No shadows
Egg. In-Game asset. 2d. High contrast. No shadows
Eyeball. In-Game asset. 2d. High contrast. No shadows
Fingertip. In-Game asset. 2d. High contrast. No shadows
A real heart. In-Game asset. 2d. High contrast. No shadows
Rock. In-Game asset. 2d. High contrast. No shadows
Shoe. In-Game asset. 2d. High contrast. No shadows
Tooth. In-Game asset. 2d. High contrast. No shadows
Plate. In-Game asset. 2d. High contrast. No shadows
Soap. In-Game asset. 2d. High contrast. No shadows
Fork. In-Game asset. 2d. High contrast. No shadows
Carrot. In-Game asset. 2d. High contrast. No shadows
Worm. In-Game asset. 2d. High contrast. No shadows
tomato with eyes. In-Game asset. 2d. High contrast. No shadows
Error. In-Game asset. 2d. High contrast. No shadows