/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Cell = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.state = 'empty'; // 'empty', 'X', 'O'
// Cell background
var background = self.attachAsset('cellBackground', {
anchorX: 0.5,
anchorY: 0.5
});
self.mark = null;
self.placeMark = function (markType) {
if (self.state !== 'empty') return false;
self.state = markType;
if (markType === 'X') {
var xAssetName = 'xMark';
if (currentSkin === 'blue') xAssetName = 'xMark_blue';else if (currentSkin === 'purple') xAssetName = 'xMark_purple';else if (currentSkin === 'orange') xAssetName = 'xMark_orange';else if (currentSkin === 'pink') xAssetName = 'xMark_pink';else if (currentSkin === 'red') xAssetName = 'xMark_red';else if (currentSkin === 'green') xAssetName = 'xMark_green';
self.mark = self.attachAsset(xAssetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
tween(self.mark, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
LK.getSound('placeX').play();
} else if (markType === 'O') {
var oAssetName = 'oMark';
if (currentSkin === 'blue') oAssetName = 'oMark_blue';else if (currentSkin === 'purple') oAssetName = 'oMark_purple';else if (currentSkin === 'orange') oAssetName = 'oMark_orange';else if (currentSkin === 'pink') oAssetName = 'oMark_pink';else if (currentSkin === 'red') oAssetName = 'oMark_red';else if (currentSkin === 'green') oAssetName = 'oMark_green';
self.mark = self.attachAsset(oAssetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
tween(self.mark, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
LK.getSound('placeO').play();
}
return true;
};
self.highlight = function () {
tween(background, {
tint: 0xf39c12
}, {
duration: 300,
easing: tween.easeInOut
});
};
self.down = function (x, y, obj) {
if (gameState === 'playing') {
handleCellClick(self);
}
};
return self;
});
var GameBoard = Container.expand(function () {
var self = Container.call(this);
// Grid background
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
// Grid lines - vertical
var vLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -100,
rotation: Math.PI / 2
});
var vLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
rotation: Math.PI / 2
});
// Grid lines - horizontal
var hLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -100
});
var hLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 100
});
self.cells = [];
// Create 3x3 grid of cells
for (var row = 0; row < 3; row++) {
self.cells[row] = [];
for (var col = 0; col < 3; col++) {
var cell = new Cell(row, col);
cell.x = (col - 1) * 200;
cell.y = (row - 1) * 200;
self.cells[row][col] = cell;
self.addChild(cell);
}
}
self.drawWinningLine = function (winningCells) {
// Calculate start and end positions
var startCell = winningCells[0];
var endCell = winningCells[2];
var startX = startCell.x;
var startY = startCell.y;
var endX = endCell.x;
var endY = endCell.y;
// Calculate line properties
var deltaX = endX - startX;
var deltaY = endY - startY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var angle = Math.atan2(deltaY, deltaX);
// Calculate center position for the line
var centerX = (startX + endX) / 2;
var centerY = (startY + endY) / 2;
// Create winning line with proper dimensions and positioning
var winningLine = self.attachAsset('winningLine', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
rotation: angle,
scaleX: 0,
width: distance + 40,
height: 8
});
// Animate the line drawing
tween(winningLine, {
scaleX: 1
}, {
duration: 800,
easing: tween.easeOut
});
// Add glowing effect by tweening alpha repeatedly
function glowEffect() {
tween(winningLine, {
alpha: 0.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(winningLine, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: glowEffect
});
}
});
}
glowEffect();
};
return self;
});
var GameBoard4x4 = Container.expand(function () {
var self = Container.call(this);
// Grid background - larger for 4x4
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8
});
// Grid lines - vertical (3 lines for 4x4)
var vLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -200,
rotation: Math.PI / 2,
scaleY: 1.8
});
var vLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -66,
rotation: Math.PI / 2,
scaleY: 1.8
});
var vLine3 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 66,
rotation: Math.PI / 2,
scaleY: 1.8
});
var vLine4 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
rotation: Math.PI / 2,
scaleY: 1.8
});
// Grid lines - horizontal (3 lines for 4x4)
var hLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -200,
scaleX: 1.8
});
var hLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -66,
scaleX: 1.8
});
var hLine3 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 66,
scaleX: 1.8
});
var hLine4 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 200,
scaleX: 1.8
});
self.cells = [];
// Create 4x4 grid of cells
for (var row = 0; row < 4; row++) {
self.cells[row] = [];
for (var col = 0; col < 4; col++) {
var cell = new Cell(row, col);
cell.x = (col - 1.5) * 133;
cell.y = (row - 1.5) * 133;
// Scale down cells for 4x4 grid
var background = cell.children[0];
if (background) {
background.scaleX = 0.65;
background.scaleY = 0.65;
}
self.cells[row][col] = cell;
self.addChild(cell);
}
}
self.drawWinningLine = function (winningCells) {
// Calculate start and end positions
var startCell = winningCells[0];
var endCell = winningCells[2];
var startX = startCell.x;
var startY = startCell.y;
var endX = endCell.x;
var endY = endCell.y;
// Calculate line properties
var deltaX = endX - startX;
var deltaY = endY - startY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var angle = Math.atan2(deltaY, deltaX);
// Calculate center position for the line
var centerX = (startX + endX) / 2;
var centerY = (startY + endY) / 2;
// Create winning line with proper dimensions and positioning
var winningLine = self.attachAsset('winningLine', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
rotation: angle,
scaleX: 0,
width: distance + 40,
height: 8
});
// Animate the line drawing
tween(winningLine, {
scaleX: 1
}, {
duration: 800,
easing: tween.easeOut
});
// Add glowing effect by tweening alpha repeatedly
function glowEffect() {
tween(winningLine, {
alpha: 0.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(winningLine, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: glowEffect
});
}
});
}
glowEffect();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a252f
});
/****
* Game Code
****/
var gameBoard;
var currentPlayer = 'X';
var gameState = 'menu'; // 'menu', 'modeSelection', 'settings', 'playing', 'gameOver', 'storyMode'
var gameMode = 'twoPlayer'; // 'twoPlayer', 'ai', 'story', 'fourByFour', 'pvp'
var aiDifficulty = 'easy'; // 'veryEasy', 'easy', 'medium'
var boardSize = 3; // Board size for current game mode
var pvpTimer = 0; // Timer for PvP mode (in seconds)
var pvpMaxTime = 600; // 10 minutes in seconds
var pvpTimerText; // Text display for timer
var pvpIntervalId = null; // Timer interval ID
var playerXScore = 0;
var playerOScore = 0;
var drawScore = 0;
var currentLanguage = storage.language || 'spanish';
var currentSkin = storage.currentSkin || 'default';
var hasActiveGame = false;
// UI Elements
var currentPlayerText;
var scoreText;
var resultText;
// Menu UI
var menuContainer = new Container();
var menuTitle = new Text2('TIC TAC TOE', {
size: 160,
fill: 0xffffff
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 2048 / 2;
menuTitle.y = 700;
menuContainer.addChild(menuTitle);
var playButton = new Text2('JUGAR', {
size: 120,
fill: 0x2ecc71
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 2048 / 2;
playButton.y = 1000;
menuContainer.addChild(playButton);
var instructionsButton = new Text2('INSTRUCCIONES', {
size: 90,
fill: 0x3498db
});
instructionsButton.anchor.set(0.5, 0.5);
instructionsButton.x = 2048 / 2;
instructionsButton.y = 1150;
menuContainer.addChild(instructionsButton);
var settingsButton = new Text2('AJUSTES', {
size: 90,
fill: 0x9b59b6
});
settingsButton.anchor.set(0.5, 0.5);
settingsButton.x = 2048 / 2;
settingsButton.y = 1300;
menuContainer.addChild(settingsButton);
var skinsButton = new Text2('SKINS', {
size: 90,
fill: 0xf39c12
});
skinsButton.anchor.set(0.5, 0.5);
skinsButton.x = 2048 / 2;
skinsButton.y = 1450;
menuContainer.addChild(skinsButton);
var saveCurrentGameButton = new Text2('GUARDAR PARTIDA', {
size: 80,
fill: 0x27ae60
});
saveCurrentGameButton.anchor.set(0.5, 0.5);
saveCurrentGameButton.x = 2048 / 2;
saveCurrentGameButton.y = 1750;
saveCurrentGameButton.visible = false;
menuContainer.addChild(saveCurrentGameButton);
var statsButton = new Text2('ESTADÍSTICAS', {
size: 80,
fill: 0xe67e22
});
statsButton.anchor.set(0.5, 0.5);
statsButton.x = 2048 / 2;
statsButton.y = 1600;
menuContainer.addChild(statsButton);
var instructionsText = new Text2('Toca una celda vacía para colocar tu marca.\nConsigue tres en línea para ganar.\n¡Alterna turnos con tu oponente!', {
size: 70,
fill: 0xffffff,
align: 'center'
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 2048 / 2;
instructionsText.y = 2000;
instructionsText.visible = false;
menuContainer.addChild(instructionsText);
game.addChild(menuContainer);
// Initialize language
updateAllTexts();
// Game Mode Selection UI
var gameModeContainer = new Container();
var gameModeTitle = new Text2('SELECCIONA MODO DE JUEGO', {
size: 120,
fill: 0xffffff
});
gameModeTitle.anchor.set(0.5, 0.5);
gameModeTitle.x = 2048 / 2;
gameModeTitle.y = 700;
gameModeContainer.addChild(gameModeTitle);
var twoPlayerButton = new Text2('2 JUGADORES', {
size: 100,
fill: 0x2ecc71
});
twoPlayerButton.anchor.set(0.5, 0.5);
twoPlayerButton.x = 2048 / 2;
twoPlayerButton.y = 1100;
gameModeContainer.addChild(twoPlayerButton);
var aiButton = new Text2('VS INTELIGENCIA ARTIFICIAL', {
size: 90,
fill: 0xe74c3c
});
aiButton.anchor.set(0.5, 0.5);
aiButton.x = 2048 / 2;
aiButton.y = 1300;
gameModeContainer.addChild(aiButton);
var storyModeButton = new Text2('MODO HISTORIA', {
size: 90,
fill: 0xf39c12
});
storyModeButton.anchor.set(0.5, 0.5);
storyModeButton.x = 2048 / 2;
storyModeButton.y = 1500;
gameModeContainer.addChild(storyModeButton);
var fourByFourButton = new Text2('MODO 4X4', {
size: 90,
fill: 0x9b59b6
});
fourByFourButton.anchor.set(0.5, 0.5);
fourByFourButton.x = 2048 / 2;
fourByFourButton.y = 1600;
gameModeContainer.addChild(fourByFourButton);
var pvpButton = new Text2('MODO PVP', {
size: 90,
fill: 0xe74c3c
});
pvpButton.anchor.set(0.5, 0.5);
pvpButton.x = 2048 / 2;
pvpButton.y = 1750;
gameModeContainer.addChild(pvpButton);
var backButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 2048 / 2;
backButton.y = 1900;
gameModeContainer.addChild(backButton);
gameModeContainer.visible = false;
game.addChild(gameModeContainer);
// Settings UI
var settingsContainer = new Container();
var settingsTitle = new Text2('AJUSTES', {
size: 120,
fill: 0xffffff
});
settingsTitle.anchor.set(0.5, 0.5);
settingsTitle.x = 2048 / 2;
settingsTitle.y = 700;
settingsContainer.addChild(settingsTitle);
var languageLabel = new Text2('IDIOMA:', {
size: 90,
fill: 0xffffff
});
languageLabel.anchor.set(0.5, 0.5);
languageLabel.x = 2048 / 2;
languageLabel.y = 1000;
settingsContainer.addChild(languageLabel);
var spanishButton = new Text2('ESPAÑOL', {
size: 80,
fill: 0x2ecc71
});
spanishButton.anchor.set(0.5, 0.5);
spanishButton.x = 2048 / 2;
spanishButton.y = 1200;
settingsContainer.addChild(spanishButton);
var englishButton = new Text2('ENGLISH', {
size: 80,
fill: 0x3498db
});
englishButton.anchor.set(0.5, 0.5);
englishButton.x = 2048 / 2;
englishButton.y = 1400;
settingsContainer.addChild(englishButton);
var settingsBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
settingsBackButton.anchor.set(0.5, 0.5);
settingsBackButton.x = 2048 / 2;
settingsBackButton.y = 1600;
settingsContainer.addChild(settingsBackButton);
settingsContainer.visible = false;
game.addChild(settingsContainer);
// Skins UI
var skinsContainer = new Container();
var skinsTitle = new Text2('SKINS', {
size: 120,
fill: 0xffffff
});
skinsTitle.anchor.set(0.5, 0.5);
skinsTitle.x = 2048 / 2;
skinsTitle.y = 600;
skinsContainer.addChild(skinsTitle);
var skinLabel = new Text2('SELECCIONA ESTILO:', {
size: 80,
fill: 0xffffff
});
skinLabel.anchor.set(0.5, 0.5);
skinLabel.x = 2048 / 2;
skinLabel.y = 800;
skinsContainer.addChild(skinLabel);
// Default skin button
var defaultSkinButton = new Text2('CLÁSICO', {
size: 70,
fill: 0x2ecc71
});
defaultSkinButton.anchor.set(0.5, 0.5);
defaultSkinButton.x = 2048 / 2;
defaultSkinButton.y = 1000;
skinsContainer.addChild(defaultSkinButton);
// Blue skin button
var blueSkinButton = new Text2('AZUL', {
size: 70,
fill: 0x3498db
});
blueSkinButton.anchor.set(0.5, 0.5);
blueSkinButton.x = 2048 / 2;
blueSkinButton.y = 1200;
skinsContainer.addChild(blueSkinButton);
// Purple skin button
var purpleSkinButton = new Text2('MORADO', {
size: 70,
fill: 0x9b59b6
});
purpleSkinButton.anchor.set(0.5, 0.5);
purpleSkinButton.x = 2048 / 2;
purpleSkinButton.y = 1400;
skinsContainer.addChild(purpleSkinButton);
// Orange skin button
var orangeSkinButton = new Text2('NARANJA', {
size: 70,
fill: 0xf39c12
});
orangeSkinButton.anchor.set(0.5, 0.5);
orangeSkinButton.x = 2048 / 2;
orangeSkinButton.y = 1600;
skinsContainer.addChild(orangeSkinButton);
// Pink skin button
var pinkSkinButton = new Text2('ROSA', {
size: 70,
fill: 0xe91e63
});
pinkSkinButton.anchor.set(0.5, 0.5);
pinkSkinButton.x = 2048 / 2;
pinkSkinButton.y = 1650;
skinsContainer.addChild(pinkSkinButton);
// Red skin button
var redSkinButton = new Text2('ROJO', {
size: 70,
fill: 0xff0000
});
redSkinButton.anchor.set(0.5, 0.5);
redSkinButton.x = 2048 / 2;
redSkinButton.y = 1750;
skinsContainer.addChild(redSkinButton);
// Green skin button
var greenSkinButton = new Text2('VERDE', {
size: 70,
fill: 0x00ff00
});
greenSkinButton.anchor.set(0.5, 0.5);
greenSkinButton.x = 2048 / 2;
greenSkinButton.y = 1850;
skinsContainer.addChild(greenSkinButton);
var skinsBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
skinsBackButton.anchor.set(0.5, 0.5);
skinsBackButton.x = 2048 / 2;
skinsBackButton.y = 1950;
skinsContainer.addChild(skinsBackButton);
skinsContainer.visible = false;
game.addChild(skinsContainer);
// Statistics UI
var statsContainer = new Container();
var statsTitle = new Text2('ESTADÍSTICAS', {
size: 120,
fill: 0xffffff
});
statsTitle.anchor.set(0.5, 0.5);
statsTitle.x = 2048 / 2;
statsTitle.y = 700;
statsContainer.addChild(statsTitle);
var aiStatsLabel = new Text2('CONTRA IA:', {
size: 90,
fill: 0xffffff
});
aiStatsLabel.anchor.set(0.5, 0.5);
aiStatsLabel.x = 2048 / 2;
aiStatsLabel.y = 1000;
statsContainer.addChild(aiStatsLabel);
var winsText = new Text2('GANADAS: 0', {
size: 80,
fill: 0x2ecc71
});
winsText.anchor.set(0.5, 0.5);
winsText.x = 2048 / 2;
winsText.y = 1200;
statsContainer.addChild(winsText);
var lossesText = new Text2('PERDIDAS: 0', {
size: 80,
fill: 0xe74c3c
});
lossesText.anchor.set(0.5, 0.5);
lossesText.x = 2048 / 2;
lossesText.y = 1400;
statsContainer.addChild(lossesText);
var statsBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
statsBackButton.anchor.set(0.5, 0.5);
statsBackButton.x = 2048 / 2;
statsBackButton.y = 1600;
statsContainer.addChild(statsBackButton);
statsContainer.visible = false;
game.addChild(statsContainer);
// Story Mode UI
var storyModeContainer = new Container();
var storyModeTitle = new Text2('MODO HISTORIA', {
size: 120,
fill: 0xffffff
});
storyModeTitle.anchor.set(0.5, 0.5);
storyModeTitle.x = 2048 / 2;
storyModeTitle.y = 700;
storyModeContainer.addChild(storyModeTitle);
var level1Button = new Text2('NIVEL 1 - MUY FÁCIL', {
size: 90,
fill: 0x2ecc71
});
level1Button.anchor.set(0.5, 0.5);
level1Button.x = 2048 / 2;
level1Button.y = 1000;
storyModeContainer.addChild(level1Button);
var level2Button = new Text2('NIVEL 2 - FÁCIL', {
size: 90,
fill: 0xf39c12
});
level2Button.anchor.set(0.5, 0.5);
level2Button.x = 2048 / 2;
level2Button.y = 1200;
storyModeContainer.addChild(level2Button);
var level3Button = new Text2('NIVEL 3 - MEDIO', {
size: 90,
fill: 0xe74c3c
});
level3Button.anchor.set(0.5, 0.5);
level3Button.x = 2048 / 2;
level3Button.y = 1400;
storyModeContainer.addChild(level3Button);
var storyBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
storyBackButton.anchor.set(0.5, 0.5);
storyBackButton.x = 2048 / 2;
storyBackButton.y = 1600;
storyModeContainer.addChild(storyBackButton);
storyModeContainer.visible = false;
game.addChild(storyModeContainer);
// Play button event
playButton.down = function (x, y, obj) {
showGameModeSelection();
};
// Instructions button event
instructionsButton.down = function (x, y, obj) {
instructionsText.visible = !instructionsText.visible;
};
// Settings button event
settingsButton.down = function (x, y, obj) {
showSettings();
};
// Skins button event
skinsButton.down = function (x, y, obj) {
showSkins();
};
// Save current game button event
saveCurrentGameButton.down = function (x, y, obj) {
if (gameBoard && gameState !== 'menu') {
saveGame();
}
};
// Statistics button event
statsButton.down = function (x, y, obj) {
showStats();
};
// Game mode button events
twoPlayerButton.down = function (x, y, obj) {
gameMode = 'twoPlayer';
startGame();
};
aiButton.down = function (x, y, obj) {
gameMode = 'ai';
startGame();
};
storyModeButton.down = function (x, y, obj) {
showStoryModeSelection();
};
fourByFourButton.down = function (x, y, obj) {
gameMode = 'fourByFour';
boardSize = 4;
startGame();
};
pvpButton.down = function (x, y, obj) {
gameMode = 'pvp';
startGame();
};
backButton.down = function (x, y, obj) {
showMainMenu();
};
// Language selection events
spanishButton.down = function (x, y, obj) {
setLanguage('spanish');
};
englishButton.down = function (x, y, obj) {
setLanguage('english');
};
settingsBackButton.down = function (x, y, obj) {
showMainMenu();
};
// Skin selection events
defaultSkinButton.down = function (x, y, obj) {
setSkin('default');
};
blueSkinButton.down = function (x, y, obj) {
setSkin('blue');
};
purpleSkinButton.down = function (x, y, obj) {
setSkin('purple');
};
orangeSkinButton.down = function (x, y, obj) {
setSkin('orange');
};
pinkSkinButton.down = function (x, y, obj) {
setSkin('pink');
};
redSkinButton.down = function (x, y, obj) {
setSkin('red');
};
greenSkinButton.down = function (x, y, obj) {
setSkin('green');
};
skinsBackButton.down = function (x, y, obj) {
showMainMenu();
};
// Stats back button event
statsBackButton.down = function (x, y, obj) {
showMainMenu();
};
// Story mode level button events
level1Button.down = function (x, y, obj) {
gameMode = 'story';
aiDifficulty = 'veryEasy';
startGame();
};
level2Button.down = function (x, y, obj) {
gameMode = 'story';
aiDifficulty = 'easy';
startGame();
};
level3Button.down = function (x, y, obj) {
gameMode = 'story';
aiDifficulty = 'medium';
startGame();
};
storyBackButton.down = function (x, y, obj) {
showGameModeSelection();
};
// Start game function
function startGame() {
menuContainer.visible = false;
gameModeContainer.visible = false;
storyModeContainer.visible = false;
gameState = 'playing';
hasActiveGame = true;
// Initialize game board
if (gameMode === 'fourByFour') {
gameBoard = new GameBoard4x4();
} else {
gameBoard = new GameBoard();
}
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2 - 100;
game.addChild(gameBoard);
// Current player indicator
currentPlayerText = new Text2('Player X\'s Turn', {
size: 80,
fill: 0xFFFFFF
});
currentPlayerText.anchor.set(0.5, 0.5);
currentPlayerText.x = 2048 / 2;
currentPlayerText.y = 400;
game.addChild(currentPlayerText);
// Score display
scoreText = new Text2('X: 0 | O: 0 | Draws: 0', {
size: 60,
fill: 0xBDC3C7
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 2048 / 2;
scoreText.y = 300;
game.addChild(scoreText);
// PvP Timer (only for PvP mode)
if (gameMode === 'pvp') {
pvpTimer = 0;
pvpTimerText = new Text2('Time: 00:00 / 10:00', {
size: 70,
fill: 0xf39c12
});
pvpTimerText.anchor.set(0.5, 0.5);
pvpTimerText.x = 2048 / 2;
pvpTimerText.y = 200;
game.addChild(pvpTimerText);
// Start PvP timer
pvpIntervalId = LK.setInterval(function () {
pvpTimer++;
updatePvPTimer();
if (pvpTimer >= pvpMaxTime) {
handlePvPTimeEnd();
}
}, 1000);
}
// Result text (initially hidden)
resultText = new Text2('', {
size: 100,
fill: 0xF39C12
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 2048 / 2;
resultText.y = 2000;
resultText.alpha = 0;
game.addChild(resultText);
// Save button will be created when game is paused/left, not during active play
// Nueva Ronda button
var newRoundButton = new Text2('NUEVA RONDA', {
size: 70,
fill: 0x9b59b6
});
newRoundButton.anchor.set(0.5, 0.5);
newRoundButton.x = 2048 / 2;
newRoundButton.y = 2200;
game.addChild(newRoundButton);
// Nueva Ronda button event
newRoundButton.down = function (x, y, obj) {
if (gameState === 'playing') {
startNewRound();
}
};
// Back button during gameplay
var gameBackButton = new Text2('VOLVER', {
size: 70,
fill: 0x95a5a6
});
gameBackButton.anchor.set(0.5, 0.5);
gameBackButton.x = 2048 / 2;
gameBackButton.y = 2400;
game.addChild(gameBackButton);
gameBackButton.down = function (x, y, obj) {
if (gameState === 'playing') {
resetGame();
showMainMenu();
}
};
}
function handleCellClick(cell) {
if (cell.placeMark(currentPlayer)) {
var winner = checkWinner();
if (winner) {
handleGameEnd(winner);
} else if (isBoardFull()) {
handleGameEnd('draw');
} else {
// Switch players
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
updateCurrentPlayerText();
// If AI mode or story mode and now it's O's turn (AI), make AI move after a short delay
if ((gameMode === 'ai' || gameMode === 'story') && currentPlayer === 'O') {
LK.setTimeout(function () {
makeAIMove();
}, 500);
}
}
}
}
function checkWinner() {
var cells = gameBoard.cells;
var size = boardSize;
// Check rows - need 3 in a row for win
for (var row = 0; row < size; row++) {
for (var col = 0; col <= size - 3; col++) {
if (cells[row][col].state === cells[row][col + 1].state && cells[row][col + 1].state === cells[row][col + 2].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row][col + 1], cells[row][col + 2]]);
return cells[row][col].state;
}
}
}
// Check columns - need 3 in a row for win
for (var col = 0; col < size; col++) {
for (var row = 0; row <= size - 3; row++) {
if (cells[row][col].state === cells[row + 1][col].state && cells[row + 1][col].state === cells[row + 2][col].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row + 1][col], cells[row + 2][col]]);
return cells[row][col].state;
}
}
}
// Check diagonals (top-left to bottom-right)
for (var row = 0; row <= size - 3; row++) {
for (var col = 0; col <= size - 3; col++) {
if (cells[row][col].state === cells[row + 1][col + 1].state && cells[row + 1][col + 1].state === cells[row + 2][col + 2].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row + 1][col + 1], cells[row + 2][col + 2]]);
return cells[row][col].state;
}
}
}
// Check diagonals (top-right to bottom-left)
for (var row = 0; row <= size - 3; row++) {
for (var col = 2; col < size; col++) {
if (cells[row][col].state === cells[row + 1][col - 1].state && cells[row + 1][col - 1].state === cells[row + 2][col - 2].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row + 1][col - 1], cells[row + 2][col - 2]]);
return cells[row][col].state;
}
}
}
return null;
}
function isBoardFull() {
var cells = gameBoard.cells;
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
if (cells[row][col].state === 'empty') {
return false;
}
}
}
return true;
}
function highlightWinningCells(winningCells) {
for (var i = 0; i < winningCells.length; i++) {
winningCells[i].highlight();
}
// Draw winning line animation immediately
gameBoard.drawWinningLine(winningCells);
}
function handleGameEnd(result) {
// Clear PvP timer if active
if (pvpIntervalId) {
LK.clearInterval(pvpIntervalId);
pvpIntervalId = null;
}
if (result === 'X') {
playerXScore++;
if (gameMode === 'ai' || gameMode === 'story') {
if (gameMode === 'story') {
resultText.setText('You Win! - Level ' + (aiDifficulty === 'veryEasy' ? '1' : aiDifficulty === 'easy' ? '2' : '3') + ' Complete!');
} else {
resultText.setText('You Win!');
}
// Track AI statistics - player win
var aiWins = storage.aiWins || 0;
storage.aiWins = aiWins;
var aiLosses = storage.aiLosses || 0;
aiLosses++;
storage.aiLosses = aiLosses;
} else {
resultText.setText('Player X Wins!');
}
resultText.tint = 0xe74c3c;
LK.getSound('win').play();
LK.setScore(LK.getScore() + 10);
} else if (result === 'O') {
playerOScore++;
if (gameMode === 'ai' || gameMode === 'story') {
if (gameMode === 'story') {
resultText.setText('AI Wins! - Try Again Level ' + (aiDifficulty === 'veryEasy' ? '1' : aiDifficulty === 'easy' ? '2' : '3'));
} else {
resultText.setText('AI Wins!');
}
// Track AI statistics - AI win
var aiWins = storage.aiWins || 0;
aiWins++;
storage.aiWins = aiWins;
var aiLosses = storage.aiLosses || 0;
storage.aiLosses = aiLosses;
} else {
resultText.setText('Player O Wins!');
}
resultText.tint = 0x2ecc71;
LK.getSound('win').play();
LK.setScore(LK.getScore() + 10);
} else if (result === 'draw') {
drawScore++;
resultText.setText('It\'s a Draw!');
resultText.tint = 0xf39c12;
LK.getSound('draw').play();
LK.setScore(LK.getScore() + 5);
}
updateScoreText();
showResult();
// In PvP mode, continue playing until time runs out
if (gameMode === 'pvp') {
LK.setTimeout(function () {
startNewRound();
}, 1500);
} else {
// Auto start new round after 2 seconds for other modes
LK.setTimeout(function () {
startNewRound();
}, 2000);
}
}
function showResult() {
resultText.alpha = 1;
resultText.y = 1800;
tween(resultText, {
y: 1600
}, {
duration: 500,
easing: tween.easeOut
});
}
function updateCurrentPlayerText() {
if (gameMode === 'ai' || gameMode === 'story') {
if (currentPlayer === 'X') {
currentPlayerText.setText('Your Turn');
currentPlayerText.tint = 0xe74c3c;
} else {
if (gameMode === 'story') {
currentPlayerText.setText('AI Turn - ' + aiDifficulty.charAt(0).toUpperCase() + aiDifficulty.slice(1));
} else {
currentPlayerText.setText('AI\'s Turn');
}
currentPlayerText.tint = 0x2ecc71;
}
} else {
if (currentPlayer === 'X') {
currentPlayerText.setText('Player X\'s Turn');
currentPlayerText.tint = 0xe74c3c;
} else {
currentPlayerText.setText('Player O\'s Turn');
currentPlayerText.tint = 0x2ecc71;
}
}
}
function updateScoreText() {
scoreText.setText('X: ' + playerXScore + ' | O: ' + playerOScore + ' | Draws: ' + drawScore);
}
function makeAIMove() {
if (gameState !== 'playing') return;
var cells = gameBoard.cells;
var emptyCells = [];
// Find all empty cells
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
if (cells[row][col].state === 'empty') {
emptyCells.push(cells[row][col]);
}
}
}
if (emptyCells.length === 0) return;
var selectedCell = null;
if (gameMode === 'story') {
if (aiDifficulty === 'veryEasy') {
// Very Easy: 80% random moves, 20% optimal
if (Math.random() < 0.8) {
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
} else {
selectedCell = getBestMove(cells, emptyCells) || emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
} else if (aiDifficulty === 'easy') {
// Easy: 60% random moves, 40% optimal
if (Math.random() < 0.6) {
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
} else {
selectedCell = getBestMove(cells, emptyCells) || emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
} else if (aiDifficulty === 'medium') {
// Medium: 30% random moves, 70% optimal
if (Math.random() < 0.3) {
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
} else {
selectedCell = getBestMove(cells, emptyCells) || emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
}
} else {
// Regular AI mode: choose random empty cell
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
}
if (selectedCell) {
handleCellClick(selectedCell);
}
}
function getBestMove(cells, emptyCells) {
// Check if AI can win
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
cell.state = 'O';
if (checkWinningMove(cells, 'O')) {
cell.state = 'empty';
return cell;
}
cell.state = 'empty';
}
// Check if AI needs to block player
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
cell.state = 'X';
if (checkWinningMove(cells, 'X')) {
cell.state = 'empty';
return cell;
}
cell.state = 'empty';
}
// Take center if available
if (cells[1][1].state === 'empty') {
return cells[1][1];
}
// Take corners
var corners = [cells[0][0], cells[0][2], cells[2][0], cells[2][2]];
for (var i = 0; i < corners.length; i++) {
if (corners[i].state === 'empty') {
return corners[i];
}
}
// Take any available cell
return emptyCells[0];
}
function checkWinningMove(cells, player) {
// Check rows
for (var row = 0; row < 3; row++) {
if (cells[row][0].state === player && cells[row][1].state === player && cells[row][2].state === player) {
return true;
}
}
// Check columns
for (var col = 0; col < 3; col++) {
if (cells[0][col].state === player && cells[1][col].state === player && cells[2][col].state === player) {
return true;
}
}
// Check diagonals
if (cells[0][0].state === player && cells[1][1].state === player && cells[2][2].state === player) {
return true;
}
if (cells[0][2].state === player && cells[1][1].state === player && cells[2][0].state === player) {
return true;
}
return false;
}
function startNewRound() {
// Remove current game board
if (gameBoard) {
game.removeChild(gameBoard);
gameBoard = null;
}
// Reset game state but keep scores
currentPlayer = 'X';
gameState = 'playing';
// Create new game board
if (gameMode === 'fourByFour') {
gameBoard = new GameBoard4x4();
} else {
gameBoard = new GameBoard();
}
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2 - 100;
game.addChild(gameBoard);
// Update UI
updateCurrentPlayerText();
resultText.alpha = 0;
}
function showGameModeSelection() {
menuContainer.visible = false;
gameModeContainer.visible = true;
gameState = 'modeSelection';
}
function showMainMenu() {
gameModeContainer.visible = false;
settingsContainer.visible = false;
skinsContainer.visible = false;
statsContainer.visible = false;
storyModeContainer.visible = false;
menuContainer.visible = true;
gameState = 'menu';
// Show save button only if there's an active game to save
if (saveCurrentGameButton) {
saveCurrentGameButton.visible = hasActiveGame && gameBoard !== null;
}
}
function showSettings() {
menuContainer.visible = false;
settingsContainer.visible = true;
gameState = 'settings';
updateLanguageButtons();
}
function showSkins() {
menuContainer.visible = false;
skinsContainer.visible = true;
gameState = 'skins';
updateSkinButtons();
}
function showStats() {
menuContainer.visible = false;
statsContainer.visible = true;
gameState = 'stats';
updateStatsDisplay();
}
function showStoryModeSelection() {
gameModeContainer.visible = false;
storyModeContainer.visible = true;
gameState = 'storyMode';
}
function updateStatsDisplay() {
var aiWins = storage.aiWins || 0;
var aiLosses = storage.aiLosses || 0;
if (currentLanguage === 'english') {
winsText.setText('WINS: ' + aiWins);
lossesText.setText('LOSSES: ' + aiLosses);
} else {
winsText.setText('GANADAS: ' + aiWins);
lossesText.setText('PERDIDAS: ' + aiLosses);
}
}
function showComingSoonMessage() {
var comingSoonText = new Text2('PRÓXIMAMENTE', {
size: 100,
fill: 0xf39c12
});
comingSoonText.anchor.set(0.5, 0.5);
comingSoonText.x = 2048 / 2;
comingSoonText.y = 2732 / 2;
comingSoonText.alpha = 0;
game.addChild(comingSoonText);
tween(comingSoonText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
LK.setTimeout(function () {
tween(comingSoonText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
game.removeChild(comingSoonText);
}
});
}, 2000);
}
function setSkin(skin) {
currentSkin = skin;
storage.currentSkin = skin;
updateSkinButtons();
}
function updateSkinButtons() {
if (!skinsContainer || !defaultSkinButton || !blueSkinButton || !purpleSkinButton || !orangeSkinButton || !pinkSkinButton || !redSkinButton || !greenSkinButton) return;
// Reset all button colors
defaultSkinButton.tint = 0x95a5a6;
blueSkinButton.tint = 0x95a5a6;
purpleSkinButton.tint = 0x95a5a6;
orangeSkinButton.tint = 0x95a5a6;
pinkSkinButton.tint = 0x95a5a6;
redSkinButton.tint = 0x95a5a6;
greenSkinButton.tint = 0x95a5a6;
// Highlight selected skin
if (currentSkin === 'default') {
defaultSkinButton.tint = 0x2ecc71;
} else if (currentSkin === 'blue') {
blueSkinButton.tint = 0x3498db;
} else if (currentSkin === 'purple') {
purpleSkinButton.tint = 0x9b59b6;
} else if (currentSkin === 'orange') {
orangeSkinButton.tint = 0xf39c12;
} else if (currentSkin === 'pink') {
pinkSkinButton.tint = 0xe91e63;
} else if (currentSkin === 'red') {
redSkinButton.tint = 0xff0000;
} else if (currentSkin === 'green') {
greenSkinButton.tint = 0x00ff00;
}
}
function setLanguage(language) {
currentLanguage = language;
storage.language = language;
updateAllTexts();
updateLanguageButtons();
}
function updateLanguageButtons() {
if (!settingsContainer || !spanishButton || !englishButton) return; // Safety check to prevent errors
if (currentLanguage === 'spanish') {
spanishButton.tint = 0x2ecc71;
englishButton.tint = 0x95a5a6;
} else {
spanishButton.tint = 0x95a5a6;
englishButton.tint = 0x3498db;
}
}
function updatePvPTimer() {
if (!pvpTimerText) return;
var minutes = Math.floor(pvpTimer / 60);
var seconds = pvpTimer % 60;
var timeStr = (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
pvpTimerText.setText('Time: ' + timeStr + ' / 10:00');
}
function handlePvPTimeEnd() {
// Clear timer
if (pvpIntervalId) {
LK.clearInterval(pvpIntervalId);
pvpIntervalId = null;
}
// Determine winner based on score
var winnerText = '';
if (playerXScore > playerOScore) {
winnerText = 'Player X Wins! (' + playerXScore + ' - ' + playerOScore + ')';
resultText.tint = 0xe74c3c;
} else if (playerOScore > playerXScore) {
winnerText = 'Player O Wins! (' + playerOScore + ' - ' + playerXScore + ')';
resultText.tint = 0x2ecc71;
} else {
winnerText = 'It\'s a Tie! (' + playerXScore + ' - ' + playerOScore + ')';
resultText.tint = 0xf39c12;
}
resultText.setText(winnerText);
showResult();
gameState = 'gameOver';
// Show final results for longer
LK.setTimeout(function () {
resetGame();
showMainMenu();
}, 4000);
}
function updateAllTexts() {
if (currentLanguage === 'english') {
// Update menu texts
menuTitle.setText('TIC TAC TOE');
playButton.setText('PLAY');
instructionsButton.setText('INSTRUCTIONS');
settingsButton.setText('SETTINGS');
skinsButton.setText('SKINS');
if (statsButton) statsButton.setText('STATISTICS');
if (saveCurrentGameButton) saveCurrentGameButton.setText('SAVE GAME');
instructionsText.setText('Touch an empty cell to place your mark.\nGet three in a line to win.\nAlternate turns with your opponent!');
// Update game mode texts
if (gameModeTitle) gameModeTitle.setText('SELECT GAME MODE');
if (twoPlayerButton) twoPlayerButton.setText('2 PLAYERS');
if (aiButton) aiButton.setText('VS ARTIFICIAL INTELLIGENCE');
if (storyModeButton) storyModeButton.setText('STORY MODE');
if (fourByFourButton) fourByFourButton.setText('4X4 MODE');
if (pvpButton) pvpButton.setText('PVP MODE');
if (backButton) backButton.setText('BACK');
// Update settings texts
if (settingsTitle) settingsTitle.setText('SETTINGS');
if (languageLabel) languageLabel.setText('LANGUAGE:');
if (settingsBackButton) settingsBackButton.setText('BACK');
// Update skins texts
if (skinsTitle) skinsTitle.setText('SKINS');
if (skinLabel) skinLabel.setText('SELECT STYLE:');
if (defaultSkinButton) defaultSkinButton.setText('CLASSIC');
if (blueSkinButton) blueSkinButton.setText('BLUE');
if (purpleSkinButton) purpleSkinButton.setText('PURPLE');
if (orangeSkinButton) orangeSkinButton.setText('ORANGE');
if (pinkSkinButton) pinkSkinButton.setText('PINK');
if (redSkinButton) redSkinButton.setText('RED');
if (greenSkinButton) greenSkinButton.setText('GREEN');
if (skinsBackButton) skinsBackButton.setText('BACK');
// Update stats texts
if (statsTitle) statsTitle.setText('STATISTICS');
if (aiStatsLabel) aiStatsLabel.setText('VS AI:');
if (statsBackButton) statsBackButton.setText('BACK');
// Update story mode texts
if (storyModeTitle) storyModeTitle.setText('STORY MODE');
if (level1Button) level1Button.setText('LEVEL 1 - VERY EASY');
if (level2Button) level2Button.setText('LEVEL 2 - EASY');
if (level3Button) level3Button.setText('LEVEL 3 - MEDIUM');
if (storyBackButton) storyBackButton.setText('BACK');
} else {
// Spanish texts (default)
menuTitle.setText('TIC TAC TOE');
playButton.setText('JUGAR');
instructionsButton.setText('INSTRUCCIONES');
settingsButton.setText('AJUSTES');
skinsButton.setText('SKINS');
if (statsButton) statsButton.setText('ESTADÍSTICAS');
if (saveCurrentGameButton) saveCurrentGameButton.setText('GUARDAR PARTIDA');
instructionsText.setText('Toca una celda vacía para colocar tu marca.\nConsigue tres en línea para ganar.\n¡Alterna turnos con tu oponente!');
// Update game mode texts
if (gameModeTitle) gameModeTitle.setText('SELECCIONA MODO DE JUEGO');
if (twoPlayerButton) twoPlayerButton.setText('2 JUGADORES');
if (aiButton) aiButton.setText('VS INTELIGENCIA ARTIFICIAL');
if (storyModeButton) storyModeButton.setText('MODO HISTORIA');
if (fourByFourButton) fourByFourButton.setText('MODO 4X4');
if (pvpButton) pvpButton.setText('MODO PVP');
if (backButton) backButton.setText('VOLVER');
// Update settings texts
if (settingsTitle) settingsTitle.setText('AJUSTES');
if (languageLabel) languageLabel.setText('IDIOMA:');
if (settingsBackButton) settingsBackButton.setText('VOLVER');
// Update skins texts
if (skinsTitle) skinsTitle.setText('SKINS');
if (skinLabel) skinLabel.setText('SELECCIONA ESTILO:');
if (defaultSkinButton) defaultSkinButton.setText('CLÁSICO');
if (blueSkinButton) blueSkinButton.setText('AZUL');
if (purpleSkinButton) purpleSkinButton.setText('MORADO');
if (orangeSkinButton) orangeSkinButton.setText('NARANJA');
if (pinkSkinButton) pinkSkinButton.setText('ROSA');
if (redSkinButton) redSkinButton.setText('ROJO');
if (greenSkinButton) greenSkinButton.setText('VERDE');
if (skinsBackButton) skinsBackButton.setText('VOLVER');
// Update stats texts
if (statsTitle) statsTitle.setText('ESTADÍSTICAS');
if (aiStatsLabel) aiStatsLabel.setText('CONTRA IA:');
if (statsBackButton) statsBackButton.setText('VOLVER');
// Update story mode texts
if (storyModeTitle) storyModeTitle.setText('MODO HISTORIA');
if (level1Button) level1Button.setText('NIVEL 1 - MUY FÁCIL');
if (level2Button) level2Button.setText('NIVEL 2 - FÁCIL');
if (level3Button) level3Button.setText('NIVEL 3 - MEDIO');
if (storyBackButton) storyBackButton.setText('VOLVER');
}
}
function saveGame() {
if (!gameBoard) return;
// Flatten board state to single array for storage compatibility
var flatBoardState = [];
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
flatBoardState.push(gameBoard.cells[row][col].state);
}
}
// Store each property separately to ensure compatibility
storage.savedGameCurrentPlayer = currentPlayer;
storage.savedGamePlayerXScore = playerXScore;
storage.savedGamePlayerOScore = playerOScore;
storage.savedGameDrawScore = drawScore;
storage.savedGameMode = gameMode;
storage.savedGameBoardState = flatBoardState;
storage.savedGameBoardSize = boardSize;
storage.hasSavedGame = true;
// Show save confirmation
var saveText = new Text2('JUEGO GUARDADO', {
size: 80,
fill: 0x27ae60
});
saveText.anchor.set(0.5, 0.5);
saveText.x = 2048 / 2;
saveText.y = 2732 / 2;
saveText.alpha = 0;
game.addChild(saveText);
tween(saveText, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
LK.setTimeout(function () {
tween(saveText, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
game.removeChild(saveText);
}
});
}, 1500);
}
function loadGame() {
if (!storage.hasSavedGame) return false;
// Restore game state from flattened storage
currentPlayer = storage.savedGameCurrentPlayer;
playerXScore = storage.savedGamePlayerXScore;
playerOScore = storage.savedGamePlayerOScore;
drawScore = storage.savedGameDrawScore;
gameMode = storage.savedGameMode;
var flatBoardState = storage.savedGameBoardState;
// Set board size based on game mode
if (gameMode === 'fourByFour') {
boardSize = 4;
gameBoard = new GameBoard4x4();
} else {
boardSize = 3;
gameBoard = new GameBoard();
}
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2 - 100;
game.addChild(gameBoard);
// Restore board state from flat array
var stateIndex = 0;
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
var cellState = flatBoardState[stateIndex];
if (cellState !== 'empty') {
gameBoard.cells[row][col].placeMark(cellState);
}
stateIndex++;
}
}
// Update UI
updateCurrentPlayerText();
updateScoreText();
return true;
}
function resetGame() {
// Clear PvP timer if active
if (pvpIntervalId) {
LK.clearInterval(pvpIntervalId);
pvpIntervalId = null;
}
// Remove game board and UI, show menu again
if (gameBoard) {
game.removeChild(gameBoard);
gameBoard = null;
}
if (currentPlayerText) {
game.removeChild(currentPlayerText);
currentPlayerText = null;
}
if (scoreText) {
game.removeChild(scoreText);
scoreText = null;
}
if (resultText) {
game.removeChild(resultText);
resultText = null;
}
if (pvpTimerText) {
game.removeChild(pvpTimerText);
pvpTimerText = null;
}
// Remove specific game UI elements
var gameChildren = game.children.slice();
for (var i = 0; i < gameChildren.length; i++) {
var child = gameChildren[i];
// Remove any text elements that are game UI buttons (not menu containers)
if (child.setText && child !== menuContainer && child !== gameModeContainer && child !== settingsContainer && child !== skinsContainer && child !== statsContainer && child !== storyModeContainer) {
// Check if it's a game UI button by checking common game UI text
if (child.getText && typeof child.getText === 'function') {
var text = child.getText();
if (text && (text.indexOf('NUEVA RONDA') !== -1 || text.indexOf('VOLVER') !== -1 || text.indexOf('NEW ROUND') !== -1 || text.indexOf('BACK') !== -1 || text.indexOf('Player') !== -1 || text.indexOf('Your Turn') !== -1 || text.indexOf('AI') !== -1 || text.indexOf('X:') !== -1)) {
game.removeChild(child);
}
} else {
// Remove any other non-container UI elements that might be game-related
game.removeChild(child);
}
}
}
menuContainer.visible = true;
gameState = 'menu';
currentPlayer = 'X';
boardSize = 3; // Reset to default board size
hasActiveGame = false;
}
// Touch anywhere to restart when game is over
game.down = function (x, y, obj) {
// Prevent board interaction in menu
if (gameState === 'menu') {
return;
}
};
game.update = function () {
// Game logic is handled through events
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Cell = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.state = 'empty'; // 'empty', 'X', 'O'
// Cell background
var background = self.attachAsset('cellBackground', {
anchorX: 0.5,
anchorY: 0.5
});
self.mark = null;
self.placeMark = function (markType) {
if (self.state !== 'empty') return false;
self.state = markType;
if (markType === 'X') {
var xAssetName = 'xMark';
if (currentSkin === 'blue') xAssetName = 'xMark_blue';else if (currentSkin === 'purple') xAssetName = 'xMark_purple';else if (currentSkin === 'orange') xAssetName = 'xMark_orange';else if (currentSkin === 'pink') xAssetName = 'xMark_pink';else if (currentSkin === 'red') xAssetName = 'xMark_red';else if (currentSkin === 'green') xAssetName = 'xMark_green';
self.mark = self.attachAsset(xAssetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
tween(self.mark, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
LK.getSound('placeX').play();
} else if (markType === 'O') {
var oAssetName = 'oMark';
if (currentSkin === 'blue') oAssetName = 'oMark_blue';else if (currentSkin === 'purple') oAssetName = 'oMark_purple';else if (currentSkin === 'orange') oAssetName = 'oMark_orange';else if (currentSkin === 'pink') oAssetName = 'oMark_pink';else if (currentSkin === 'red') oAssetName = 'oMark_red';else if (currentSkin === 'green') oAssetName = 'oMark_green';
self.mark = self.attachAsset(oAssetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
tween(self.mark, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
LK.getSound('placeO').play();
}
return true;
};
self.highlight = function () {
tween(background, {
tint: 0xf39c12
}, {
duration: 300,
easing: tween.easeInOut
});
};
self.down = function (x, y, obj) {
if (gameState === 'playing') {
handleCellClick(self);
}
};
return self;
});
var GameBoard = Container.expand(function () {
var self = Container.call(this);
// Grid background
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5
});
// Grid lines - vertical
var vLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -100,
rotation: Math.PI / 2
});
var vLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
rotation: Math.PI / 2
});
// Grid lines - horizontal
var hLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -100
});
var hLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 100
});
self.cells = [];
// Create 3x3 grid of cells
for (var row = 0; row < 3; row++) {
self.cells[row] = [];
for (var col = 0; col < 3; col++) {
var cell = new Cell(row, col);
cell.x = (col - 1) * 200;
cell.y = (row - 1) * 200;
self.cells[row][col] = cell;
self.addChild(cell);
}
}
self.drawWinningLine = function (winningCells) {
// Calculate start and end positions
var startCell = winningCells[0];
var endCell = winningCells[2];
var startX = startCell.x;
var startY = startCell.y;
var endX = endCell.x;
var endY = endCell.y;
// Calculate line properties
var deltaX = endX - startX;
var deltaY = endY - startY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var angle = Math.atan2(deltaY, deltaX);
// Calculate center position for the line
var centerX = (startX + endX) / 2;
var centerY = (startY + endY) / 2;
// Create winning line with proper dimensions and positioning
var winningLine = self.attachAsset('winningLine', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
rotation: angle,
scaleX: 0,
width: distance + 40,
height: 8
});
// Animate the line drawing
tween(winningLine, {
scaleX: 1
}, {
duration: 800,
easing: tween.easeOut
});
// Add glowing effect by tweening alpha repeatedly
function glowEffect() {
tween(winningLine, {
alpha: 0.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(winningLine, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: glowEffect
});
}
});
}
glowEffect();
};
return self;
});
var GameBoard4x4 = Container.expand(function () {
var self = Container.call(this);
// Grid background - larger for 4x4
var gridBg = self.attachAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8
});
// Grid lines - vertical (3 lines for 4x4)
var vLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -200,
rotation: Math.PI / 2,
scaleY: 1.8
});
var vLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -66,
rotation: Math.PI / 2,
scaleY: 1.8
});
var vLine3 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 66,
rotation: Math.PI / 2,
scaleY: 1.8
});
var vLine4 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
rotation: Math.PI / 2,
scaleY: 1.8
});
// Grid lines - horizontal (3 lines for 4x4)
var hLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -200,
scaleX: 1.8
});
var hLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -66,
scaleX: 1.8
});
var hLine3 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 66,
scaleX: 1.8
});
var hLine4 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 200,
scaleX: 1.8
});
self.cells = [];
// Create 4x4 grid of cells
for (var row = 0; row < 4; row++) {
self.cells[row] = [];
for (var col = 0; col < 4; col++) {
var cell = new Cell(row, col);
cell.x = (col - 1.5) * 133;
cell.y = (row - 1.5) * 133;
// Scale down cells for 4x4 grid
var background = cell.children[0];
if (background) {
background.scaleX = 0.65;
background.scaleY = 0.65;
}
self.cells[row][col] = cell;
self.addChild(cell);
}
}
self.drawWinningLine = function (winningCells) {
// Calculate start and end positions
var startCell = winningCells[0];
var endCell = winningCells[2];
var startX = startCell.x;
var startY = startCell.y;
var endX = endCell.x;
var endY = endCell.y;
// Calculate line properties
var deltaX = endX - startX;
var deltaY = endY - startY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var angle = Math.atan2(deltaY, deltaX);
// Calculate center position for the line
var centerX = (startX + endX) / 2;
var centerY = (startY + endY) / 2;
// Create winning line with proper dimensions and positioning
var winningLine = self.attachAsset('winningLine', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
rotation: angle,
scaleX: 0,
width: distance + 40,
height: 8
});
// Animate the line drawing
tween(winningLine, {
scaleX: 1
}, {
duration: 800,
easing: tween.easeOut
});
// Add glowing effect by tweening alpha repeatedly
function glowEffect() {
tween(winningLine, {
alpha: 0.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(winningLine, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: glowEffect
});
}
});
}
glowEffect();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a252f
});
/****
* Game Code
****/
var gameBoard;
var currentPlayer = 'X';
var gameState = 'menu'; // 'menu', 'modeSelection', 'settings', 'playing', 'gameOver', 'storyMode'
var gameMode = 'twoPlayer'; // 'twoPlayer', 'ai', 'story', 'fourByFour', 'pvp'
var aiDifficulty = 'easy'; // 'veryEasy', 'easy', 'medium'
var boardSize = 3; // Board size for current game mode
var pvpTimer = 0; // Timer for PvP mode (in seconds)
var pvpMaxTime = 600; // 10 minutes in seconds
var pvpTimerText; // Text display for timer
var pvpIntervalId = null; // Timer interval ID
var playerXScore = 0;
var playerOScore = 0;
var drawScore = 0;
var currentLanguage = storage.language || 'spanish';
var currentSkin = storage.currentSkin || 'default';
var hasActiveGame = false;
// UI Elements
var currentPlayerText;
var scoreText;
var resultText;
// Menu UI
var menuContainer = new Container();
var menuTitle = new Text2('TIC TAC TOE', {
size: 160,
fill: 0xffffff
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 2048 / 2;
menuTitle.y = 700;
menuContainer.addChild(menuTitle);
var playButton = new Text2('JUGAR', {
size: 120,
fill: 0x2ecc71
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 2048 / 2;
playButton.y = 1000;
menuContainer.addChild(playButton);
var instructionsButton = new Text2('INSTRUCCIONES', {
size: 90,
fill: 0x3498db
});
instructionsButton.anchor.set(0.5, 0.5);
instructionsButton.x = 2048 / 2;
instructionsButton.y = 1150;
menuContainer.addChild(instructionsButton);
var settingsButton = new Text2('AJUSTES', {
size: 90,
fill: 0x9b59b6
});
settingsButton.anchor.set(0.5, 0.5);
settingsButton.x = 2048 / 2;
settingsButton.y = 1300;
menuContainer.addChild(settingsButton);
var skinsButton = new Text2('SKINS', {
size: 90,
fill: 0xf39c12
});
skinsButton.anchor.set(0.5, 0.5);
skinsButton.x = 2048 / 2;
skinsButton.y = 1450;
menuContainer.addChild(skinsButton);
var saveCurrentGameButton = new Text2('GUARDAR PARTIDA', {
size: 80,
fill: 0x27ae60
});
saveCurrentGameButton.anchor.set(0.5, 0.5);
saveCurrentGameButton.x = 2048 / 2;
saveCurrentGameButton.y = 1750;
saveCurrentGameButton.visible = false;
menuContainer.addChild(saveCurrentGameButton);
var statsButton = new Text2('ESTADÍSTICAS', {
size: 80,
fill: 0xe67e22
});
statsButton.anchor.set(0.5, 0.5);
statsButton.x = 2048 / 2;
statsButton.y = 1600;
menuContainer.addChild(statsButton);
var instructionsText = new Text2('Toca una celda vacía para colocar tu marca.\nConsigue tres en línea para ganar.\n¡Alterna turnos con tu oponente!', {
size: 70,
fill: 0xffffff,
align: 'center'
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 2048 / 2;
instructionsText.y = 2000;
instructionsText.visible = false;
menuContainer.addChild(instructionsText);
game.addChild(menuContainer);
// Initialize language
updateAllTexts();
// Game Mode Selection UI
var gameModeContainer = new Container();
var gameModeTitle = new Text2('SELECCIONA MODO DE JUEGO', {
size: 120,
fill: 0xffffff
});
gameModeTitle.anchor.set(0.5, 0.5);
gameModeTitle.x = 2048 / 2;
gameModeTitle.y = 700;
gameModeContainer.addChild(gameModeTitle);
var twoPlayerButton = new Text2('2 JUGADORES', {
size: 100,
fill: 0x2ecc71
});
twoPlayerButton.anchor.set(0.5, 0.5);
twoPlayerButton.x = 2048 / 2;
twoPlayerButton.y = 1100;
gameModeContainer.addChild(twoPlayerButton);
var aiButton = new Text2('VS INTELIGENCIA ARTIFICIAL', {
size: 90,
fill: 0xe74c3c
});
aiButton.anchor.set(0.5, 0.5);
aiButton.x = 2048 / 2;
aiButton.y = 1300;
gameModeContainer.addChild(aiButton);
var storyModeButton = new Text2('MODO HISTORIA', {
size: 90,
fill: 0xf39c12
});
storyModeButton.anchor.set(0.5, 0.5);
storyModeButton.x = 2048 / 2;
storyModeButton.y = 1500;
gameModeContainer.addChild(storyModeButton);
var fourByFourButton = new Text2('MODO 4X4', {
size: 90,
fill: 0x9b59b6
});
fourByFourButton.anchor.set(0.5, 0.5);
fourByFourButton.x = 2048 / 2;
fourByFourButton.y = 1600;
gameModeContainer.addChild(fourByFourButton);
var pvpButton = new Text2('MODO PVP', {
size: 90,
fill: 0xe74c3c
});
pvpButton.anchor.set(0.5, 0.5);
pvpButton.x = 2048 / 2;
pvpButton.y = 1750;
gameModeContainer.addChild(pvpButton);
var backButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 2048 / 2;
backButton.y = 1900;
gameModeContainer.addChild(backButton);
gameModeContainer.visible = false;
game.addChild(gameModeContainer);
// Settings UI
var settingsContainer = new Container();
var settingsTitle = new Text2('AJUSTES', {
size: 120,
fill: 0xffffff
});
settingsTitle.anchor.set(0.5, 0.5);
settingsTitle.x = 2048 / 2;
settingsTitle.y = 700;
settingsContainer.addChild(settingsTitle);
var languageLabel = new Text2('IDIOMA:', {
size: 90,
fill: 0xffffff
});
languageLabel.anchor.set(0.5, 0.5);
languageLabel.x = 2048 / 2;
languageLabel.y = 1000;
settingsContainer.addChild(languageLabel);
var spanishButton = new Text2('ESPAÑOL', {
size: 80,
fill: 0x2ecc71
});
spanishButton.anchor.set(0.5, 0.5);
spanishButton.x = 2048 / 2;
spanishButton.y = 1200;
settingsContainer.addChild(spanishButton);
var englishButton = new Text2('ENGLISH', {
size: 80,
fill: 0x3498db
});
englishButton.anchor.set(0.5, 0.5);
englishButton.x = 2048 / 2;
englishButton.y = 1400;
settingsContainer.addChild(englishButton);
var settingsBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
settingsBackButton.anchor.set(0.5, 0.5);
settingsBackButton.x = 2048 / 2;
settingsBackButton.y = 1600;
settingsContainer.addChild(settingsBackButton);
settingsContainer.visible = false;
game.addChild(settingsContainer);
// Skins UI
var skinsContainer = new Container();
var skinsTitle = new Text2('SKINS', {
size: 120,
fill: 0xffffff
});
skinsTitle.anchor.set(0.5, 0.5);
skinsTitle.x = 2048 / 2;
skinsTitle.y = 600;
skinsContainer.addChild(skinsTitle);
var skinLabel = new Text2('SELECCIONA ESTILO:', {
size: 80,
fill: 0xffffff
});
skinLabel.anchor.set(0.5, 0.5);
skinLabel.x = 2048 / 2;
skinLabel.y = 800;
skinsContainer.addChild(skinLabel);
// Default skin button
var defaultSkinButton = new Text2('CLÁSICO', {
size: 70,
fill: 0x2ecc71
});
defaultSkinButton.anchor.set(0.5, 0.5);
defaultSkinButton.x = 2048 / 2;
defaultSkinButton.y = 1000;
skinsContainer.addChild(defaultSkinButton);
// Blue skin button
var blueSkinButton = new Text2('AZUL', {
size: 70,
fill: 0x3498db
});
blueSkinButton.anchor.set(0.5, 0.5);
blueSkinButton.x = 2048 / 2;
blueSkinButton.y = 1200;
skinsContainer.addChild(blueSkinButton);
// Purple skin button
var purpleSkinButton = new Text2('MORADO', {
size: 70,
fill: 0x9b59b6
});
purpleSkinButton.anchor.set(0.5, 0.5);
purpleSkinButton.x = 2048 / 2;
purpleSkinButton.y = 1400;
skinsContainer.addChild(purpleSkinButton);
// Orange skin button
var orangeSkinButton = new Text2('NARANJA', {
size: 70,
fill: 0xf39c12
});
orangeSkinButton.anchor.set(0.5, 0.5);
orangeSkinButton.x = 2048 / 2;
orangeSkinButton.y = 1600;
skinsContainer.addChild(orangeSkinButton);
// Pink skin button
var pinkSkinButton = new Text2('ROSA', {
size: 70,
fill: 0xe91e63
});
pinkSkinButton.anchor.set(0.5, 0.5);
pinkSkinButton.x = 2048 / 2;
pinkSkinButton.y = 1650;
skinsContainer.addChild(pinkSkinButton);
// Red skin button
var redSkinButton = new Text2('ROJO', {
size: 70,
fill: 0xff0000
});
redSkinButton.anchor.set(0.5, 0.5);
redSkinButton.x = 2048 / 2;
redSkinButton.y = 1750;
skinsContainer.addChild(redSkinButton);
// Green skin button
var greenSkinButton = new Text2('VERDE', {
size: 70,
fill: 0x00ff00
});
greenSkinButton.anchor.set(0.5, 0.5);
greenSkinButton.x = 2048 / 2;
greenSkinButton.y = 1850;
skinsContainer.addChild(greenSkinButton);
var skinsBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
skinsBackButton.anchor.set(0.5, 0.5);
skinsBackButton.x = 2048 / 2;
skinsBackButton.y = 1950;
skinsContainer.addChild(skinsBackButton);
skinsContainer.visible = false;
game.addChild(skinsContainer);
// Statistics UI
var statsContainer = new Container();
var statsTitle = new Text2('ESTADÍSTICAS', {
size: 120,
fill: 0xffffff
});
statsTitle.anchor.set(0.5, 0.5);
statsTitle.x = 2048 / 2;
statsTitle.y = 700;
statsContainer.addChild(statsTitle);
var aiStatsLabel = new Text2('CONTRA IA:', {
size: 90,
fill: 0xffffff
});
aiStatsLabel.anchor.set(0.5, 0.5);
aiStatsLabel.x = 2048 / 2;
aiStatsLabel.y = 1000;
statsContainer.addChild(aiStatsLabel);
var winsText = new Text2('GANADAS: 0', {
size: 80,
fill: 0x2ecc71
});
winsText.anchor.set(0.5, 0.5);
winsText.x = 2048 / 2;
winsText.y = 1200;
statsContainer.addChild(winsText);
var lossesText = new Text2('PERDIDAS: 0', {
size: 80,
fill: 0xe74c3c
});
lossesText.anchor.set(0.5, 0.5);
lossesText.x = 2048 / 2;
lossesText.y = 1400;
statsContainer.addChild(lossesText);
var statsBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
statsBackButton.anchor.set(0.5, 0.5);
statsBackButton.x = 2048 / 2;
statsBackButton.y = 1600;
statsContainer.addChild(statsBackButton);
statsContainer.visible = false;
game.addChild(statsContainer);
// Story Mode UI
var storyModeContainer = new Container();
var storyModeTitle = new Text2('MODO HISTORIA', {
size: 120,
fill: 0xffffff
});
storyModeTitle.anchor.set(0.5, 0.5);
storyModeTitle.x = 2048 / 2;
storyModeTitle.y = 700;
storyModeContainer.addChild(storyModeTitle);
var level1Button = new Text2('NIVEL 1 - MUY FÁCIL', {
size: 90,
fill: 0x2ecc71
});
level1Button.anchor.set(0.5, 0.5);
level1Button.x = 2048 / 2;
level1Button.y = 1000;
storyModeContainer.addChild(level1Button);
var level2Button = new Text2('NIVEL 2 - FÁCIL', {
size: 90,
fill: 0xf39c12
});
level2Button.anchor.set(0.5, 0.5);
level2Button.x = 2048 / 2;
level2Button.y = 1200;
storyModeContainer.addChild(level2Button);
var level3Button = new Text2('NIVEL 3 - MEDIO', {
size: 90,
fill: 0xe74c3c
});
level3Button.anchor.set(0.5, 0.5);
level3Button.x = 2048 / 2;
level3Button.y = 1400;
storyModeContainer.addChild(level3Button);
var storyBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
storyBackButton.anchor.set(0.5, 0.5);
storyBackButton.x = 2048 / 2;
storyBackButton.y = 1600;
storyModeContainer.addChild(storyBackButton);
storyModeContainer.visible = false;
game.addChild(storyModeContainer);
// Play button event
playButton.down = function (x, y, obj) {
showGameModeSelection();
};
// Instructions button event
instructionsButton.down = function (x, y, obj) {
instructionsText.visible = !instructionsText.visible;
};
// Settings button event
settingsButton.down = function (x, y, obj) {
showSettings();
};
// Skins button event
skinsButton.down = function (x, y, obj) {
showSkins();
};
// Save current game button event
saveCurrentGameButton.down = function (x, y, obj) {
if (gameBoard && gameState !== 'menu') {
saveGame();
}
};
// Statistics button event
statsButton.down = function (x, y, obj) {
showStats();
};
// Game mode button events
twoPlayerButton.down = function (x, y, obj) {
gameMode = 'twoPlayer';
startGame();
};
aiButton.down = function (x, y, obj) {
gameMode = 'ai';
startGame();
};
storyModeButton.down = function (x, y, obj) {
showStoryModeSelection();
};
fourByFourButton.down = function (x, y, obj) {
gameMode = 'fourByFour';
boardSize = 4;
startGame();
};
pvpButton.down = function (x, y, obj) {
gameMode = 'pvp';
startGame();
};
backButton.down = function (x, y, obj) {
showMainMenu();
};
// Language selection events
spanishButton.down = function (x, y, obj) {
setLanguage('spanish');
};
englishButton.down = function (x, y, obj) {
setLanguage('english');
};
settingsBackButton.down = function (x, y, obj) {
showMainMenu();
};
// Skin selection events
defaultSkinButton.down = function (x, y, obj) {
setSkin('default');
};
blueSkinButton.down = function (x, y, obj) {
setSkin('blue');
};
purpleSkinButton.down = function (x, y, obj) {
setSkin('purple');
};
orangeSkinButton.down = function (x, y, obj) {
setSkin('orange');
};
pinkSkinButton.down = function (x, y, obj) {
setSkin('pink');
};
redSkinButton.down = function (x, y, obj) {
setSkin('red');
};
greenSkinButton.down = function (x, y, obj) {
setSkin('green');
};
skinsBackButton.down = function (x, y, obj) {
showMainMenu();
};
// Stats back button event
statsBackButton.down = function (x, y, obj) {
showMainMenu();
};
// Story mode level button events
level1Button.down = function (x, y, obj) {
gameMode = 'story';
aiDifficulty = 'veryEasy';
startGame();
};
level2Button.down = function (x, y, obj) {
gameMode = 'story';
aiDifficulty = 'easy';
startGame();
};
level3Button.down = function (x, y, obj) {
gameMode = 'story';
aiDifficulty = 'medium';
startGame();
};
storyBackButton.down = function (x, y, obj) {
showGameModeSelection();
};
// Start game function
function startGame() {
menuContainer.visible = false;
gameModeContainer.visible = false;
storyModeContainer.visible = false;
gameState = 'playing';
hasActiveGame = true;
// Initialize game board
if (gameMode === 'fourByFour') {
gameBoard = new GameBoard4x4();
} else {
gameBoard = new GameBoard();
}
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2 - 100;
game.addChild(gameBoard);
// Current player indicator
currentPlayerText = new Text2('Player X\'s Turn', {
size: 80,
fill: 0xFFFFFF
});
currentPlayerText.anchor.set(0.5, 0.5);
currentPlayerText.x = 2048 / 2;
currentPlayerText.y = 400;
game.addChild(currentPlayerText);
// Score display
scoreText = new Text2('X: 0 | O: 0 | Draws: 0', {
size: 60,
fill: 0xBDC3C7
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 2048 / 2;
scoreText.y = 300;
game.addChild(scoreText);
// PvP Timer (only for PvP mode)
if (gameMode === 'pvp') {
pvpTimer = 0;
pvpTimerText = new Text2('Time: 00:00 / 10:00', {
size: 70,
fill: 0xf39c12
});
pvpTimerText.anchor.set(0.5, 0.5);
pvpTimerText.x = 2048 / 2;
pvpTimerText.y = 200;
game.addChild(pvpTimerText);
// Start PvP timer
pvpIntervalId = LK.setInterval(function () {
pvpTimer++;
updatePvPTimer();
if (pvpTimer >= pvpMaxTime) {
handlePvPTimeEnd();
}
}, 1000);
}
// Result text (initially hidden)
resultText = new Text2('', {
size: 100,
fill: 0xF39C12
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 2048 / 2;
resultText.y = 2000;
resultText.alpha = 0;
game.addChild(resultText);
// Save button will be created when game is paused/left, not during active play
// Nueva Ronda button
var newRoundButton = new Text2('NUEVA RONDA', {
size: 70,
fill: 0x9b59b6
});
newRoundButton.anchor.set(0.5, 0.5);
newRoundButton.x = 2048 / 2;
newRoundButton.y = 2200;
game.addChild(newRoundButton);
// Nueva Ronda button event
newRoundButton.down = function (x, y, obj) {
if (gameState === 'playing') {
startNewRound();
}
};
// Back button during gameplay
var gameBackButton = new Text2('VOLVER', {
size: 70,
fill: 0x95a5a6
});
gameBackButton.anchor.set(0.5, 0.5);
gameBackButton.x = 2048 / 2;
gameBackButton.y = 2400;
game.addChild(gameBackButton);
gameBackButton.down = function (x, y, obj) {
if (gameState === 'playing') {
resetGame();
showMainMenu();
}
};
}
function handleCellClick(cell) {
if (cell.placeMark(currentPlayer)) {
var winner = checkWinner();
if (winner) {
handleGameEnd(winner);
} else if (isBoardFull()) {
handleGameEnd('draw');
} else {
// Switch players
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
updateCurrentPlayerText();
// If AI mode or story mode and now it's O's turn (AI), make AI move after a short delay
if ((gameMode === 'ai' || gameMode === 'story') && currentPlayer === 'O') {
LK.setTimeout(function () {
makeAIMove();
}, 500);
}
}
}
}
function checkWinner() {
var cells = gameBoard.cells;
var size = boardSize;
// Check rows - need 3 in a row for win
for (var row = 0; row < size; row++) {
for (var col = 0; col <= size - 3; col++) {
if (cells[row][col].state === cells[row][col + 1].state && cells[row][col + 1].state === cells[row][col + 2].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row][col + 1], cells[row][col + 2]]);
return cells[row][col].state;
}
}
}
// Check columns - need 3 in a row for win
for (var col = 0; col < size; col++) {
for (var row = 0; row <= size - 3; row++) {
if (cells[row][col].state === cells[row + 1][col].state && cells[row + 1][col].state === cells[row + 2][col].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row + 1][col], cells[row + 2][col]]);
return cells[row][col].state;
}
}
}
// Check diagonals (top-left to bottom-right)
for (var row = 0; row <= size - 3; row++) {
for (var col = 0; col <= size - 3; col++) {
if (cells[row][col].state === cells[row + 1][col + 1].state && cells[row + 1][col + 1].state === cells[row + 2][col + 2].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row + 1][col + 1], cells[row + 2][col + 2]]);
return cells[row][col].state;
}
}
}
// Check diagonals (top-right to bottom-left)
for (var row = 0; row <= size - 3; row++) {
for (var col = 2; col < size; col++) {
if (cells[row][col].state === cells[row + 1][col - 1].state && cells[row + 1][col - 1].state === cells[row + 2][col - 2].state && cells[row][col].state !== 'empty') {
highlightWinningCells([cells[row][col], cells[row + 1][col - 1], cells[row + 2][col - 2]]);
return cells[row][col].state;
}
}
}
return null;
}
function isBoardFull() {
var cells = gameBoard.cells;
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
if (cells[row][col].state === 'empty') {
return false;
}
}
}
return true;
}
function highlightWinningCells(winningCells) {
for (var i = 0; i < winningCells.length; i++) {
winningCells[i].highlight();
}
// Draw winning line animation immediately
gameBoard.drawWinningLine(winningCells);
}
function handleGameEnd(result) {
// Clear PvP timer if active
if (pvpIntervalId) {
LK.clearInterval(pvpIntervalId);
pvpIntervalId = null;
}
if (result === 'X') {
playerXScore++;
if (gameMode === 'ai' || gameMode === 'story') {
if (gameMode === 'story') {
resultText.setText('You Win! - Level ' + (aiDifficulty === 'veryEasy' ? '1' : aiDifficulty === 'easy' ? '2' : '3') + ' Complete!');
} else {
resultText.setText('You Win!');
}
// Track AI statistics - player win
var aiWins = storage.aiWins || 0;
storage.aiWins = aiWins;
var aiLosses = storage.aiLosses || 0;
aiLosses++;
storage.aiLosses = aiLosses;
} else {
resultText.setText('Player X Wins!');
}
resultText.tint = 0xe74c3c;
LK.getSound('win').play();
LK.setScore(LK.getScore() + 10);
} else if (result === 'O') {
playerOScore++;
if (gameMode === 'ai' || gameMode === 'story') {
if (gameMode === 'story') {
resultText.setText('AI Wins! - Try Again Level ' + (aiDifficulty === 'veryEasy' ? '1' : aiDifficulty === 'easy' ? '2' : '3'));
} else {
resultText.setText('AI Wins!');
}
// Track AI statistics - AI win
var aiWins = storage.aiWins || 0;
aiWins++;
storage.aiWins = aiWins;
var aiLosses = storage.aiLosses || 0;
storage.aiLosses = aiLosses;
} else {
resultText.setText('Player O Wins!');
}
resultText.tint = 0x2ecc71;
LK.getSound('win').play();
LK.setScore(LK.getScore() + 10);
} else if (result === 'draw') {
drawScore++;
resultText.setText('It\'s a Draw!');
resultText.tint = 0xf39c12;
LK.getSound('draw').play();
LK.setScore(LK.getScore() + 5);
}
updateScoreText();
showResult();
// In PvP mode, continue playing until time runs out
if (gameMode === 'pvp') {
LK.setTimeout(function () {
startNewRound();
}, 1500);
} else {
// Auto start new round after 2 seconds for other modes
LK.setTimeout(function () {
startNewRound();
}, 2000);
}
}
function showResult() {
resultText.alpha = 1;
resultText.y = 1800;
tween(resultText, {
y: 1600
}, {
duration: 500,
easing: tween.easeOut
});
}
function updateCurrentPlayerText() {
if (gameMode === 'ai' || gameMode === 'story') {
if (currentPlayer === 'X') {
currentPlayerText.setText('Your Turn');
currentPlayerText.tint = 0xe74c3c;
} else {
if (gameMode === 'story') {
currentPlayerText.setText('AI Turn - ' + aiDifficulty.charAt(0).toUpperCase() + aiDifficulty.slice(1));
} else {
currentPlayerText.setText('AI\'s Turn');
}
currentPlayerText.tint = 0x2ecc71;
}
} else {
if (currentPlayer === 'X') {
currentPlayerText.setText('Player X\'s Turn');
currentPlayerText.tint = 0xe74c3c;
} else {
currentPlayerText.setText('Player O\'s Turn');
currentPlayerText.tint = 0x2ecc71;
}
}
}
function updateScoreText() {
scoreText.setText('X: ' + playerXScore + ' | O: ' + playerOScore + ' | Draws: ' + drawScore);
}
function makeAIMove() {
if (gameState !== 'playing') return;
var cells = gameBoard.cells;
var emptyCells = [];
// Find all empty cells
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
if (cells[row][col].state === 'empty') {
emptyCells.push(cells[row][col]);
}
}
}
if (emptyCells.length === 0) return;
var selectedCell = null;
if (gameMode === 'story') {
if (aiDifficulty === 'veryEasy') {
// Very Easy: 80% random moves, 20% optimal
if (Math.random() < 0.8) {
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
} else {
selectedCell = getBestMove(cells, emptyCells) || emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
} else if (aiDifficulty === 'easy') {
// Easy: 60% random moves, 40% optimal
if (Math.random() < 0.6) {
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
} else {
selectedCell = getBestMove(cells, emptyCells) || emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
} else if (aiDifficulty === 'medium') {
// Medium: 30% random moves, 70% optimal
if (Math.random() < 0.3) {
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
} else {
selectedCell = getBestMove(cells, emptyCells) || emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
}
} else {
// Regular AI mode: choose random empty cell
var randomIndex = Math.floor(Math.random() * emptyCells.length);
selectedCell = emptyCells[randomIndex];
}
if (selectedCell) {
handleCellClick(selectedCell);
}
}
function getBestMove(cells, emptyCells) {
// Check if AI can win
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
cell.state = 'O';
if (checkWinningMove(cells, 'O')) {
cell.state = 'empty';
return cell;
}
cell.state = 'empty';
}
// Check if AI needs to block player
for (var i = 0; i < emptyCells.length; i++) {
var cell = emptyCells[i];
cell.state = 'X';
if (checkWinningMove(cells, 'X')) {
cell.state = 'empty';
return cell;
}
cell.state = 'empty';
}
// Take center if available
if (cells[1][1].state === 'empty') {
return cells[1][1];
}
// Take corners
var corners = [cells[0][0], cells[0][2], cells[2][0], cells[2][2]];
for (var i = 0; i < corners.length; i++) {
if (corners[i].state === 'empty') {
return corners[i];
}
}
// Take any available cell
return emptyCells[0];
}
function checkWinningMove(cells, player) {
// Check rows
for (var row = 0; row < 3; row++) {
if (cells[row][0].state === player && cells[row][1].state === player && cells[row][2].state === player) {
return true;
}
}
// Check columns
for (var col = 0; col < 3; col++) {
if (cells[0][col].state === player && cells[1][col].state === player && cells[2][col].state === player) {
return true;
}
}
// Check diagonals
if (cells[0][0].state === player && cells[1][1].state === player && cells[2][2].state === player) {
return true;
}
if (cells[0][2].state === player && cells[1][1].state === player && cells[2][0].state === player) {
return true;
}
return false;
}
function startNewRound() {
// Remove current game board
if (gameBoard) {
game.removeChild(gameBoard);
gameBoard = null;
}
// Reset game state but keep scores
currentPlayer = 'X';
gameState = 'playing';
// Create new game board
if (gameMode === 'fourByFour') {
gameBoard = new GameBoard4x4();
} else {
gameBoard = new GameBoard();
}
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2 - 100;
game.addChild(gameBoard);
// Update UI
updateCurrentPlayerText();
resultText.alpha = 0;
}
function showGameModeSelection() {
menuContainer.visible = false;
gameModeContainer.visible = true;
gameState = 'modeSelection';
}
function showMainMenu() {
gameModeContainer.visible = false;
settingsContainer.visible = false;
skinsContainer.visible = false;
statsContainer.visible = false;
storyModeContainer.visible = false;
menuContainer.visible = true;
gameState = 'menu';
// Show save button only if there's an active game to save
if (saveCurrentGameButton) {
saveCurrentGameButton.visible = hasActiveGame && gameBoard !== null;
}
}
function showSettings() {
menuContainer.visible = false;
settingsContainer.visible = true;
gameState = 'settings';
updateLanguageButtons();
}
function showSkins() {
menuContainer.visible = false;
skinsContainer.visible = true;
gameState = 'skins';
updateSkinButtons();
}
function showStats() {
menuContainer.visible = false;
statsContainer.visible = true;
gameState = 'stats';
updateStatsDisplay();
}
function showStoryModeSelection() {
gameModeContainer.visible = false;
storyModeContainer.visible = true;
gameState = 'storyMode';
}
function updateStatsDisplay() {
var aiWins = storage.aiWins || 0;
var aiLosses = storage.aiLosses || 0;
if (currentLanguage === 'english') {
winsText.setText('WINS: ' + aiWins);
lossesText.setText('LOSSES: ' + aiLosses);
} else {
winsText.setText('GANADAS: ' + aiWins);
lossesText.setText('PERDIDAS: ' + aiLosses);
}
}
function showComingSoonMessage() {
var comingSoonText = new Text2('PRÓXIMAMENTE', {
size: 100,
fill: 0xf39c12
});
comingSoonText.anchor.set(0.5, 0.5);
comingSoonText.x = 2048 / 2;
comingSoonText.y = 2732 / 2;
comingSoonText.alpha = 0;
game.addChild(comingSoonText);
tween(comingSoonText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
LK.setTimeout(function () {
tween(comingSoonText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
game.removeChild(comingSoonText);
}
});
}, 2000);
}
function setSkin(skin) {
currentSkin = skin;
storage.currentSkin = skin;
updateSkinButtons();
}
function updateSkinButtons() {
if (!skinsContainer || !defaultSkinButton || !blueSkinButton || !purpleSkinButton || !orangeSkinButton || !pinkSkinButton || !redSkinButton || !greenSkinButton) return;
// Reset all button colors
defaultSkinButton.tint = 0x95a5a6;
blueSkinButton.tint = 0x95a5a6;
purpleSkinButton.tint = 0x95a5a6;
orangeSkinButton.tint = 0x95a5a6;
pinkSkinButton.tint = 0x95a5a6;
redSkinButton.tint = 0x95a5a6;
greenSkinButton.tint = 0x95a5a6;
// Highlight selected skin
if (currentSkin === 'default') {
defaultSkinButton.tint = 0x2ecc71;
} else if (currentSkin === 'blue') {
blueSkinButton.tint = 0x3498db;
} else if (currentSkin === 'purple') {
purpleSkinButton.tint = 0x9b59b6;
} else if (currentSkin === 'orange') {
orangeSkinButton.tint = 0xf39c12;
} else if (currentSkin === 'pink') {
pinkSkinButton.tint = 0xe91e63;
} else if (currentSkin === 'red') {
redSkinButton.tint = 0xff0000;
} else if (currentSkin === 'green') {
greenSkinButton.tint = 0x00ff00;
}
}
function setLanguage(language) {
currentLanguage = language;
storage.language = language;
updateAllTexts();
updateLanguageButtons();
}
function updateLanguageButtons() {
if (!settingsContainer || !spanishButton || !englishButton) return; // Safety check to prevent errors
if (currentLanguage === 'spanish') {
spanishButton.tint = 0x2ecc71;
englishButton.tint = 0x95a5a6;
} else {
spanishButton.tint = 0x95a5a6;
englishButton.tint = 0x3498db;
}
}
function updatePvPTimer() {
if (!pvpTimerText) return;
var minutes = Math.floor(pvpTimer / 60);
var seconds = pvpTimer % 60;
var timeStr = (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
pvpTimerText.setText('Time: ' + timeStr + ' / 10:00');
}
function handlePvPTimeEnd() {
// Clear timer
if (pvpIntervalId) {
LK.clearInterval(pvpIntervalId);
pvpIntervalId = null;
}
// Determine winner based on score
var winnerText = '';
if (playerXScore > playerOScore) {
winnerText = 'Player X Wins! (' + playerXScore + ' - ' + playerOScore + ')';
resultText.tint = 0xe74c3c;
} else if (playerOScore > playerXScore) {
winnerText = 'Player O Wins! (' + playerOScore + ' - ' + playerXScore + ')';
resultText.tint = 0x2ecc71;
} else {
winnerText = 'It\'s a Tie! (' + playerXScore + ' - ' + playerOScore + ')';
resultText.tint = 0xf39c12;
}
resultText.setText(winnerText);
showResult();
gameState = 'gameOver';
// Show final results for longer
LK.setTimeout(function () {
resetGame();
showMainMenu();
}, 4000);
}
function updateAllTexts() {
if (currentLanguage === 'english') {
// Update menu texts
menuTitle.setText('TIC TAC TOE');
playButton.setText('PLAY');
instructionsButton.setText('INSTRUCTIONS');
settingsButton.setText('SETTINGS');
skinsButton.setText('SKINS');
if (statsButton) statsButton.setText('STATISTICS');
if (saveCurrentGameButton) saveCurrentGameButton.setText('SAVE GAME');
instructionsText.setText('Touch an empty cell to place your mark.\nGet three in a line to win.\nAlternate turns with your opponent!');
// Update game mode texts
if (gameModeTitle) gameModeTitle.setText('SELECT GAME MODE');
if (twoPlayerButton) twoPlayerButton.setText('2 PLAYERS');
if (aiButton) aiButton.setText('VS ARTIFICIAL INTELLIGENCE');
if (storyModeButton) storyModeButton.setText('STORY MODE');
if (fourByFourButton) fourByFourButton.setText('4X4 MODE');
if (pvpButton) pvpButton.setText('PVP MODE');
if (backButton) backButton.setText('BACK');
// Update settings texts
if (settingsTitle) settingsTitle.setText('SETTINGS');
if (languageLabel) languageLabel.setText('LANGUAGE:');
if (settingsBackButton) settingsBackButton.setText('BACK');
// Update skins texts
if (skinsTitle) skinsTitle.setText('SKINS');
if (skinLabel) skinLabel.setText('SELECT STYLE:');
if (defaultSkinButton) defaultSkinButton.setText('CLASSIC');
if (blueSkinButton) blueSkinButton.setText('BLUE');
if (purpleSkinButton) purpleSkinButton.setText('PURPLE');
if (orangeSkinButton) orangeSkinButton.setText('ORANGE');
if (pinkSkinButton) pinkSkinButton.setText('PINK');
if (redSkinButton) redSkinButton.setText('RED');
if (greenSkinButton) greenSkinButton.setText('GREEN');
if (skinsBackButton) skinsBackButton.setText('BACK');
// Update stats texts
if (statsTitle) statsTitle.setText('STATISTICS');
if (aiStatsLabel) aiStatsLabel.setText('VS AI:');
if (statsBackButton) statsBackButton.setText('BACK');
// Update story mode texts
if (storyModeTitle) storyModeTitle.setText('STORY MODE');
if (level1Button) level1Button.setText('LEVEL 1 - VERY EASY');
if (level2Button) level2Button.setText('LEVEL 2 - EASY');
if (level3Button) level3Button.setText('LEVEL 3 - MEDIUM');
if (storyBackButton) storyBackButton.setText('BACK');
} else {
// Spanish texts (default)
menuTitle.setText('TIC TAC TOE');
playButton.setText('JUGAR');
instructionsButton.setText('INSTRUCCIONES');
settingsButton.setText('AJUSTES');
skinsButton.setText('SKINS');
if (statsButton) statsButton.setText('ESTADÍSTICAS');
if (saveCurrentGameButton) saveCurrentGameButton.setText('GUARDAR PARTIDA');
instructionsText.setText('Toca una celda vacía para colocar tu marca.\nConsigue tres en línea para ganar.\n¡Alterna turnos con tu oponente!');
// Update game mode texts
if (gameModeTitle) gameModeTitle.setText('SELECCIONA MODO DE JUEGO');
if (twoPlayerButton) twoPlayerButton.setText('2 JUGADORES');
if (aiButton) aiButton.setText('VS INTELIGENCIA ARTIFICIAL');
if (storyModeButton) storyModeButton.setText('MODO HISTORIA');
if (fourByFourButton) fourByFourButton.setText('MODO 4X4');
if (pvpButton) pvpButton.setText('MODO PVP');
if (backButton) backButton.setText('VOLVER');
// Update settings texts
if (settingsTitle) settingsTitle.setText('AJUSTES');
if (languageLabel) languageLabel.setText('IDIOMA:');
if (settingsBackButton) settingsBackButton.setText('VOLVER');
// Update skins texts
if (skinsTitle) skinsTitle.setText('SKINS');
if (skinLabel) skinLabel.setText('SELECCIONA ESTILO:');
if (defaultSkinButton) defaultSkinButton.setText('CLÁSICO');
if (blueSkinButton) blueSkinButton.setText('AZUL');
if (purpleSkinButton) purpleSkinButton.setText('MORADO');
if (orangeSkinButton) orangeSkinButton.setText('NARANJA');
if (pinkSkinButton) pinkSkinButton.setText('ROSA');
if (redSkinButton) redSkinButton.setText('ROJO');
if (greenSkinButton) greenSkinButton.setText('VERDE');
if (skinsBackButton) skinsBackButton.setText('VOLVER');
// Update stats texts
if (statsTitle) statsTitle.setText('ESTADÍSTICAS');
if (aiStatsLabel) aiStatsLabel.setText('CONTRA IA:');
if (statsBackButton) statsBackButton.setText('VOLVER');
// Update story mode texts
if (storyModeTitle) storyModeTitle.setText('MODO HISTORIA');
if (level1Button) level1Button.setText('NIVEL 1 - MUY FÁCIL');
if (level2Button) level2Button.setText('NIVEL 2 - FÁCIL');
if (level3Button) level3Button.setText('NIVEL 3 - MEDIO');
if (storyBackButton) storyBackButton.setText('VOLVER');
}
}
function saveGame() {
if (!gameBoard) return;
// Flatten board state to single array for storage compatibility
var flatBoardState = [];
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
flatBoardState.push(gameBoard.cells[row][col].state);
}
}
// Store each property separately to ensure compatibility
storage.savedGameCurrentPlayer = currentPlayer;
storage.savedGamePlayerXScore = playerXScore;
storage.savedGamePlayerOScore = playerOScore;
storage.savedGameDrawScore = drawScore;
storage.savedGameMode = gameMode;
storage.savedGameBoardState = flatBoardState;
storage.savedGameBoardSize = boardSize;
storage.hasSavedGame = true;
// Show save confirmation
var saveText = new Text2('JUEGO GUARDADO', {
size: 80,
fill: 0x27ae60
});
saveText.anchor.set(0.5, 0.5);
saveText.x = 2048 / 2;
saveText.y = 2732 / 2;
saveText.alpha = 0;
game.addChild(saveText);
tween(saveText, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
LK.setTimeout(function () {
tween(saveText, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
game.removeChild(saveText);
}
});
}, 1500);
}
function loadGame() {
if (!storage.hasSavedGame) return false;
// Restore game state from flattened storage
currentPlayer = storage.savedGameCurrentPlayer;
playerXScore = storage.savedGamePlayerXScore;
playerOScore = storage.savedGamePlayerOScore;
drawScore = storage.savedGameDrawScore;
gameMode = storage.savedGameMode;
var flatBoardState = storage.savedGameBoardState;
// Set board size based on game mode
if (gameMode === 'fourByFour') {
boardSize = 4;
gameBoard = new GameBoard4x4();
} else {
boardSize = 3;
gameBoard = new GameBoard();
}
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2 - 100;
game.addChild(gameBoard);
// Restore board state from flat array
var stateIndex = 0;
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
var cellState = flatBoardState[stateIndex];
if (cellState !== 'empty') {
gameBoard.cells[row][col].placeMark(cellState);
}
stateIndex++;
}
}
// Update UI
updateCurrentPlayerText();
updateScoreText();
return true;
}
function resetGame() {
// Clear PvP timer if active
if (pvpIntervalId) {
LK.clearInterval(pvpIntervalId);
pvpIntervalId = null;
}
// Remove game board and UI, show menu again
if (gameBoard) {
game.removeChild(gameBoard);
gameBoard = null;
}
if (currentPlayerText) {
game.removeChild(currentPlayerText);
currentPlayerText = null;
}
if (scoreText) {
game.removeChild(scoreText);
scoreText = null;
}
if (resultText) {
game.removeChild(resultText);
resultText = null;
}
if (pvpTimerText) {
game.removeChild(pvpTimerText);
pvpTimerText = null;
}
// Remove specific game UI elements
var gameChildren = game.children.slice();
for (var i = 0; i < gameChildren.length; i++) {
var child = gameChildren[i];
// Remove any text elements that are game UI buttons (not menu containers)
if (child.setText && child !== menuContainer && child !== gameModeContainer && child !== settingsContainer && child !== skinsContainer && child !== statsContainer && child !== storyModeContainer) {
// Check if it's a game UI button by checking common game UI text
if (child.getText && typeof child.getText === 'function') {
var text = child.getText();
if (text && (text.indexOf('NUEVA RONDA') !== -1 || text.indexOf('VOLVER') !== -1 || text.indexOf('NEW ROUND') !== -1 || text.indexOf('BACK') !== -1 || text.indexOf('Player') !== -1 || text.indexOf('Your Turn') !== -1 || text.indexOf('AI') !== -1 || text.indexOf('X:') !== -1)) {
game.removeChild(child);
}
} else {
// Remove any other non-container UI elements that might be game-related
game.removeChild(child);
}
}
}
menuContainer.visible = true;
gameState = 'menu';
currentPlayer = 'X';
boardSize = 3; // Reset to default board size
hasActiveGame = false;
}
// Touch anywhere to restart when game is over
game.down = function (x, y, obj) {
// Prevent board interaction in menu
if (gameState === 'menu') {
return;
}
};
game.update = function () {
// Game logic is handled through events
};