/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var FallingNumber = Container.expand(function (value) {
var self = Container.call(this);
self.value = value;
self.speed = 3 + Math.random() * 2;
self.collected = false;
var numberGraphics = self.attachAsset('number' + value, {
anchorX: 0.5,
anchorY: 0.5
});
var numberText = new Text2(value.toString(), {
size: 60,
fill: '#ffffff'
});
numberText.anchor.set(0.5, 0.5);
self.addChild(numberText);
self.update = function () {
if (!self.collected) {
self.y += self.speed;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
self.move = function (targetX) {
self.x = Math.max(60, Math.min(2048 - 60, targetX));
};
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var SpikeBall = Container.expand(function () {
var self = Container.call(this);
self.speed = 2 + Math.random() * 3;
self.rotationSpeed = 0.1 + Math.random() * 0.1;
var spikeGraphics = self.attachAsset('spikeBall', {
anchorX: 0.5,
anchorY: 0.5
});
// Add spikes as visual elements
for (var i = 0; i < 8; i++) {
var spike = LK.getAsset('spikeBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.8
});
var angle = i / 8 * Math.PI * 2;
spike.x = Math.cos(angle) * 40;
spike.y = Math.sin(angle) * 40;
spike.rotation = angle + Math.PI / 2;
self.addChild(spike);
}
self.update = function () {
self.y += self.speed;
self.rotation += self.rotationSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'playing'
var player = null;
var fallingNumbers = [];
var spikeBalls = [];
var collectedNumbers = [];
var gameMode = 'subtraction'; // 'addition' or 'subtraction'
var targetSum = 0;
var currentSum = 20; // Start with 20 for subtraction mode
var lives = 3;
var gameSpeed = 1;
var spawnTimer = 0;
var spawnRate = 90;
// Menu UI Elements
var menuTitle = new Text2('MATH CATCHER', {
size: 80,
fill: '#ffffff'
});
menuTitle.anchor.set(0.5, 0.5);
LK.gui.center.addChild(menuTitle);
menuTitle.y = -300;
var additionButton = new Text2('SUMA (+)', {
size: 60,
fill: '#4caf50'
});
additionButton.anchor.set(0.5, 0.5);
LK.gui.center.addChild(additionButton);
additionButton.y = -100;
var subtractionButton = new Text2('RESTA (-)', {
size: 60,
fill: '#2196f3'
});
subtractionButton.anchor.set(0.5, 0.5);
LK.gui.center.addChild(subtractionButton);
subtractionButton.y = 0;
var menuInstructions = new Text2('Selecciona un modo de juego', {
size: 40,
fill: '#ffffff'
});
menuInstructions.anchor.set(0.5, 0.5);
LK.gui.center.addChild(menuInstructions);
menuInstructions.y = 150;
// Game UI Elements (hidden initially)
var targetSumText = new Text2('Target: 0', {
size: 60,
fill: '#ffffff'
});
targetSumText.anchor.set(0.5, 0);
LK.gui.top.addChild(targetSumText);
targetSumText.y = 80;
targetSumText.visible = false;
var currentSumText = new Text2('Current: 0', {
size: 50,
fill: '#ffeb3b'
});
currentSumText.anchor.set(0.5, 0);
LK.gui.top.addChild(currentSumText);
currentSumText.y = 160;
currentSumText.visible = false;
var livesText = new Text2('Lives: 3', {
size: 50,
fill: '#f44336'
});
livesText.anchor.set(1, 0);
LK.gui.topRight.addChild(livesText);
livesText.x = -20;
livesText.y = 20;
livesText.visible = false;
var scoreText = new Text2('Score: 0', {
size: 50,
fill: '#ffffff'
});
scoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreText);
scoreText.x = 120;
scoreText.y = 20;
scoreText.visible = false;
var menuButton = new Text2('MENU', {
size: 40,
fill: '#ffffff'
});
menuButton.anchor.set(0, 0);
LK.gui.topLeft.addChild(menuButton);
menuButton.x = 20;
menuButton.y = 80;
menuButton.visible = false;
var instructionText = new Text2('Arrastra para atrapar números\ny restarlos para llegar al objetivo', {
size: 40,
fill: '#ffffff'
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
instructionText.y = -200;
instructionText.visible = false;
function generateTargetSum() {
if (gameMode === 'subtraction') {
targetSum = Math.floor(Math.random() * 15) + 1; // Target between 1-15
targetSumText.setText('Target: ' + targetSum);
} else {
targetSum = Math.floor(Math.random() * 20) + 5;
targetSumText.setText('Target: ' + targetSum);
}
}
function updateCurrentSum() {
if (gameMode === 'subtraction') {
currentSum = 20; // Reset to starting value
for (var i = 0; i < collectedNumbers.length; i++) {
currentSum -= collectedNumbers[i]; // Subtract instead of add
}
} else {
currentSum = 0;
for (var i = 0; i < collectedNumbers.length; i++) {
currentSum += collectedNumbers[i];
}
}
currentSumText.setText('Current: ' + currentSum);
}
function checkSum() {
if (currentSum === targetSum) {
LK.setScore(LK.getScore() + targetSum * 10);
LK.getSound('correct').play();
collectedNumbers = [];
generateTargetSum();
gameSpeed += 0.1;
spawnRate = Math.max(30, spawnRate - 2);
// Flash effect
tween(game, {}, {
duration: 200,
onFinish: function onFinish() {
LK.effects.flashScreen(0x00ff00, 300);
}
});
} else if (gameMode === 'subtraction' && currentSum < targetSum || gameMode === 'addition' && currentSum > targetSum) {
lives--;
LK.getSound('wrong').play();
collectedNumbers = [];
generateTargetSum();
if (lives <= 0) {
LK.showGameOver();
}
// Flash effect
LK.effects.flashScreen(0xff0000, 500);
}
}
function spawnNumber() {
var value = Math.floor(Math.random() * 10);
var number = new FallingNumber(value);
number.x = Math.random() * (2048 - 240) + 120;
number.y = -50;
number.speed *= gameSpeed;
fallingNumbers.push(number);
game.addChild(number);
}
function spawnSpikeBall() {
var spikeBall = new SpikeBall();
spikeBall.x = Math.random() * (2048 - 200) + 100;
spikeBall.y = -100;
spikeBall.speed *= gameSpeed;
spikeBalls.push(spikeBall);
game.addChild(spikeBall);
}
var dragNode = null;
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.move(x);
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (gameState === 'playing' && player) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
if (gameState === 'playing') {
dragNode = null;
}
};
function showMenu() {
gameState = 'menu';
// Show menu UI
menuTitle.visible = true;
additionButton.visible = true;
subtractionButton.visible = true;
menuInstructions.visible = true;
// Hide game UI
targetSumText.visible = false;
currentSumText.visible = false;
livesText.visible = false;
scoreText.visible = false;
menuButton.visible = false;
instructionText.visible = false;
// Clear game objects
if (player) {
player.destroy();
player = null;
}
for (var i = 0; i < fallingNumbers.length; i++) {
fallingNumbers[i].destroy();
}
fallingNumbers = [];
for (var i = 0; i < spikeBalls.length; i++) {
spikeBalls[i].destroy();
}
spikeBalls = [];
}
function startGame(mode) {
gameState = 'playing';
gameMode = mode;
// Hide menu UI
menuTitle.visible = false;
additionButton.visible = false;
subtractionButton.visible = false;
menuInstructions.visible = false;
// Show game UI
targetSumText.visible = true;
currentSumText.visible = true;
livesText.visible = true;
scoreText.visible = true;
menuButton.visible = true;
instructionText.visible = true;
// Initialize game
player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 - 100;
collectedNumbers = [];
spikeBalls = [];
lives = 3;
gameSpeed = 1;
spawnTimer = 0;
spawnRate = 90;
if (gameMode === 'subtraction') {
currentSum = 20;
instructionText.setText('Arrastra para atrapar números\ny restarlos para llegar al objetivo');
} else {
currentSum = 0;
instructionText.setText('Arrastra para atrapar números\ny sumarlos para llegar al objetivo');
}
generateTargetSum();
updateCurrentSum();
// Make instruction text disappear after 15 seconds
tween(instructionText, {
alpha: 0
}, {
duration: 15000,
onFinish: function onFinish() {
instructionText.visible = false;
instructionText.alpha = 1;
}
});
}
additionButton.down = function (x, y, obj) {
startGame('addition');
};
subtractionButton.down = function (x, y, obj) {
startGame('subtraction');
};
menuButton.down = function (x, y, obj) {
showMenu();
};
game.update = function () {
if (gameState !== 'playing') return;
spawnTimer++;
if (spawnTimer >= spawnRate) {
spawnNumber();
spawnTimer = 0;
}
// Spawn spike balls occasionally
if (LK.ticks % 180 === 0) {
spawnSpikeBall();
}
// Update spike balls
for (var i = spikeBalls.length - 1; i >= 0; i--) {
var spikeBall = spikeBalls[i];
if (spikeBall.lastY === undefined) spikeBall.lastY = spikeBall.y;
if (spikeBall.lastIntersecting === undefined) spikeBall.lastIntersecting = false;
// Check if off screen
if (spikeBall.lastY < 2732 + 100 && spikeBall.y >= 2732 + 100) {
spikeBall.destroy();
spikeBalls.splice(i, 1);
continue;
}
// Check collision with player
var currentIntersecting = spikeBall.intersects(player);
if (!spikeBall.lastIntersecting && currentIntersecting) {
lives--;
LK.getSound('wrong').play();
LK.effects.flashScreen(0xff0000, 500);
spikeBall.destroy();
spikeBalls.splice(i, 1);
if (lives <= 0) {
LK.showGameOver();
}
continue;
}
spikeBall.lastY = spikeBall.y;
spikeBall.lastIntersecting = currentIntersecting;
}
// Update falling numbers
for (var i = fallingNumbers.length - 1; i >= 0; i--) {
var number = fallingNumbers[i];
if (number.lastY === undefined) number.lastY = number.y;
if (number.lastIntersecting === undefined) number.lastIntersecting = false;
// Check if off screen
if (number.lastY < 2732 + 50 && number.y >= 2732 + 50) {
number.destroy();
fallingNumbers.splice(i, 1);
continue;
}
// Check collision with player
var currentIntersecting = number.intersects(player);
if (!number.lastIntersecting && currentIntersecting && !number.collected) {
number.collected = true;
collectedNumbers.push(number.value);
LK.getSound('collect').play();
// Animate collected number
tween(number, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
onFinish: function onFinish() {
number.destroy();
}
});
fallingNumbers.splice(i, 1);
updateCurrentSum();
checkSum();
continue;
}
number.lastY = number.y;
number.lastIntersecting = currentIntersecting;
}
// Update UI
scoreText.setText('Score: ' + LK.getScore());
livesText.setText('Lives: ' + lives);
};
// Start in menu state
showMenu(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var FallingNumber = Container.expand(function (value) {
var self = Container.call(this);
self.value = value;
self.speed = 3 + Math.random() * 2;
self.collected = false;
var numberGraphics = self.attachAsset('number' + value, {
anchorX: 0.5,
anchorY: 0.5
});
var numberText = new Text2(value.toString(), {
size: 60,
fill: '#ffffff'
});
numberText.anchor.set(0.5, 0.5);
self.addChild(numberText);
self.update = function () {
if (!self.collected) {
self.y += self.speed;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
self.move = function (targetX) {
self.x = Math.max(60, Math.min(2048 - 60, targetX));
};
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var SpikeBall = Container.expand(function () {
var self = Container.call(this);
self.speed = 2 + Math.random() * 3;
self.rotationSpeed = 0.1 + Math.random() * 0.1;
var spikeGraphics = self.attachAsset('spikeBall', {
anchorX: 0.5,
anchorY: 0.5
});
// Add spikes as visual elements
for (var i = 0; i < 8; i++) {
var spike = LK.getAsset('spikeBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.8
});
var angle = i / 8 * Math.PI * 2;
spike.x = Math.cos(angle) * 40;
spike.y = Math.sin(angle) * 40;
spike.rotation = angle + Math.PI / 2;
self.addChild(spike);
}
self.update = function () {
self.y += self.speed;
self.rotation += self.rotationSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'playing'
var player = null;
var fallingNumbers = [];
var spikeBalls = [];
var collectedNumbers = [];
var gameMode = 'subtraction'; // 'addition' or 'subtraction'
var targetSum = 0;
var currentSum = 20; // Start with 20 for subtraction mode
var lives = 3;
var gameSpeed = 1;
var spawnTimer = 0;
var spawnRate = 90;
// Menu UI Elements
var menuTitle = new Text2('MATH CATCHER', {
size: 80,
fill: '#ffffff'
});
menuTitle.anchor.set(0.5, 0.5);
LK.gui.center.addChild(menuTitle);
menuTitle.y = -300;
var additionButton = new Text2('SUMA (+)', {
size: 60,
fill: '#4caf50'
});
additionButton.anchor.set(0.5, 0.5);
LK.gui.center.addChild(additionButton);
additionButton.y = -100;
var subtractionButton = new Text2('RESTA (-)', {
size: 60,
fill: '#2196f3'
});
subtractionButton.anchor.set(0.5, 0.5);
LK.gui.center.addChild(subtractionButton);
subtractionButton.y = 0;
var menuInstructions = new Text2('Selecciona un modo de juego', {
size: 40,
fill: '#ffffff'
});
menuInstructions.anchor.set(0.5, 0.5);
LK.gui.center.addChild(menuInstructions);
menuInstructions.y = 150;
// Game UI Elements (hidden initially)
var targetSumText = new Text2('Target: 0', {
size: 60,
fill: '#ffffff'
});
targetSumText.anchor.set(0.5, 0);
LK.gui.top.addChild(targetSumText);
targetSumText.y = 80;
targetSumText.visible = false;
var currentSumText = new Text2('Current: 0', {
size: 50,
fill: '#ffeb3b'
});
currentSumText.anchor.set(0.5, 0);
LK.gui.top.addChild(currentSumText);
currentSumText.y = 160;
currentSumText.visible = false;
var livesText = new Text2('Lives: 3', {
size: 50,
fill: '#f44336'
});
livesText.anchor.set(1, 0);
LK.gui.topRight.addChild(livesText);
livesText.x = -20;
livesText.y = 20;
livesText.visible = false;
var scoreText = new Text2('Score: 0', {
size: 50,
fill: '#ffffff'
});
scoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreText);
scoreText.x = 120;
scoreText.y = 20;
scoreText.visible = false;
var menuButton = new Text2('MENU', {
size: 40,
fill: '#ffffff'
});
menuButton.anchor.set(0, 0);
LK.gui.topLeft.addChild(menuButton);
menuButton.x = 20;
menuButton.y = 80;
menuButton.visible = false;
var instructionText = new Text2('Arrastra para atrapar números\ny restarlos para llegar al objetivo', {
size: 40,
fill: '#ffffff'
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
instructionText.y = -200;
instructionText.visible = false;
function generateTargetSum() {
if (gameMode === 'subtraction') {
targetSum = Math.floor(Math.random() * 15) + 1; // Target between 1-15
targetSumText.setText('Target: ' + targetSum);
} else {
targetSum = Math.floor(Math.random() * 20) + 5;
targetSumText.setText('Target: ' + targetSum);
}
}
function updateCurrentSum() {
if (gameMode === 'subtraction') {
currentSum = 20; // Reset to starting value
for (var i = 0; i < collectedNumbers.length; i++) {
currentSum -= collectedNumbers[i]; // Subtract instead of add
}
} else {
currentSum = 0;
for (var i = 0; i < collectedNumbers.length; i++) {
currentSum += collectedNumbers[i];
}
}
currentSumText.setText('Current: ' + currentSum);
}
function checkSum() {
if (currentSum === targetSum) {
LK.setScore(LK.getScore() + targetSum * 10);
LK.getSound('correct').play();
collectedNumbers = [];
generateTargetSum();
gameSpeed += 0.1;
spawnRate = Math.max(30, spawnRate - 2);
// Flash effect
tween(game, {}, {
duration: 200,
onFinish: function onFinish() {
LK.effects.flashScreen(0x00ff00, 300);
}
});
} else if (gameMode === 'subtraction' && currentSum < targetSum || gameMode === 'addition' && currentSum > targetSum) {
lives--;
LK.getSound('wrong').play();
collectedNumbers = [];
generateTargetSum();
if (lives <= 0) {
LK.showGameOver();
}
// Flash effect
LK.effects.flashScreen(0xff0000, 500);
}
}
function spawnNumber() {
var value = Math.floor(Math.random() * 10);
var number = new FallingNumber(value);
number.x = Math.random() * (2048 - 240) + 120;
number.y = -50;
number.speed *= gameSpeed;
fallingNumbers.push(number);
game.addChild(number);
}
function spawnSpikeBall() {
var spikeBall = new SpikeBall();
spikeBall.x = Math.random() * (2048 - 200) + 100;
spikeBall.y = -100;
spikeBall.speed *= gameSpeed;
spikeBalls.push(spikeBall);
game.addChild(spikeBall);
}
var dragNode = null;
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.move(x);
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (gameState === 'playing' && player) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
if (gameState === 'playing') {
dragNode = null;
}
};
function showMenu() {
gameState = 'menu';
// Show menu UI
menuTitle.visible = true;
additionButton.visible = true;
subtractionButton.visible = true;
menuInstructions.visible = true;
// Hide game UI
targetSumText.visible = false;
currentSumText.visible = false;
livesText.visible = false;
scoreText.visible = false;
menuButton.visible = false;
instructionText.visible = false;
// Clear game objects
if (player) {
player.destroy();
player = null;
}
for (var i = 0; i < fallingNumbers.length; i++) {
fallingNumbers[i].destroy();
}
fallingNumbers = [];
for (var i = 0; i < spikeBalls.length; i++) {
spikeBalls[i].destroy();
}
spikeBalls = [];
}
function startGame(mode) {
gameState = 'playing';
gameMode = mode;
// Hide menu UI
menuTitle.visible = false;
additionButton.visible = false;
subtractionButton.visible = false;
menuInstructions.visible = false;
// Show game UI
targetSumText.visible = true;
currentSumText.visible = true;
livesText.visible = true;
scoreText.visible = true;
menuButton.visible = true;
instructionText.visible = true;
// Initialize game
player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 - 100;
collectedNumbers = [];
spikeBalls = [];
lives = 3;
gameSpeed = 1;
spawnTimer = 0;
spawnRate = 90;
if (gameMode === 'subtraction') {
currentSum = 20;
instructionText.setText('Arrastra para atrapar números\ny restarlos para llegar al objetivo');
} else {
currentSum = 0;
instructionText.setText('Arrastra para atrapar números\ny sumarlos para llegar al objetivo');
}
generateTargetSum();
updateCurrentSum();
// Make instruction text disappear after 15 seconds
tween(instructionText, {
alpha: 0
}, {
duration: 15000,
onFinish: function onFinish() {
instructionText.visible = false;
instructionText.alpha = 1;
}
});
}
additionButton.down = function (x, y, obj) {
startGame('addition');
};
subtractionButton.down = function (x, y, obj) {
startGame('subtraction');
};
menuButton.down = function (x, y, obj) {
showMenu();
};
game.update = function () {
if (gameState !== 'playing') return;
spawnTimer++;
if (spawnTimer >= spawnRate) {
spawnNumber();
spawnTimer = 0;
}
// Spawn spike balls occasionally
if (LK.ticks % 180 === 0) {
spawnSpikeBall();
}
// Update spike balls
for (var i = spikeBalls.length - 1; i >= 0; i--) {
var spikeBall = spikeBalls[i];
if (spikeBall.lastY === undefined) spikeBall.lastY = spikeBall.y;
if (spikeBall.lastIntersecting === undefined) spikeBall.lastIntersecting = false;
// Check if off screen
if (spikeBall.lastY < 2732 + 100 && spikeBall.y >= 2732 + 100) {
spikeBall.destroy();
spikeBalls.splice(i, 1);
continue;
}
// Check collision with player
var currentIntersecting = spikeBall.intersects(player);
if (!spikeBall.lastIntersecting && currentIntersecting) {
lives--;
LK.getSound('wrong').play();
LK.effects.flashScreen(0xff0000, 500);
spikeBall.destroy();
spikeBalls.splice(i, 1);
if (lives <= 0) {
LK.showGameOver();
}
continue;
}
spikeBall.lastY = spikeBall.y;
spikeBall.lastIntersecting = currentIntersecting;
}
// Update falling numbers
for (var i = fallingNumbers.length - 1; i >= 0; i--) {
var number = fallingNumbers[i];
if (number.lastY === undefined) number.lastY = number.y;
if (number.lastIntersecting === undefined) number.lastIntersecting = false;
// Check if off screen
if (number.lastY < 2732 + 50 && number.y >= 2732 + 50) {
number.destroy();
fallingNumbers.splice(i, 1);
continue;
}
// Check collision with player
var currentIntersecting = number.intersects(player);
if (!number.lastIntersecting && currentIntersecting && !number.collected) {
number.collected = true;
collectedNumbers.push(number.value);
LK.getSound('collect').play();
// Animate collected number
tween(number, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
onFinish: function onFinish() {
number.destroy();
}
});
fallingNumbers.splice(i, 1);
updateCurrentSum();
checkSum();
continue;
}
number.lastY = number.y;
number.lastIntersecting = currentIntersecting;
}
// Update UI
scoreText.setText('Score: ' + LK.getScore());
livesText.setText('Lives: ' + lives);
};
// Start in menu state
showMenu();