User prompt
También el nuevo modo que es " pvsp" qué dos jugadores se enfrentan y en que consiga mayor puntuación en 10 minutos, gana. Y 2 nuevos colores. ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
User prompt
Haz el tablero más grande en el modo "4x4" y se agrega nueva skin.
User prompt
Que no se separe el botón de estadísticas con el de botón de skins,juntalos. Además coloca un nuevo modo que es "4x4" qué el tablero es tamaño de 4x4 y tendrás que hacer 3 en raya.
User prompt
También arregla sobre que el botón de volver y reinicio están en el menú pero no quiero que estén ahí y además coloca ordenadamente los botones del menú..
User prompt
Cuando se toca un nivel, que solo aparezca el tablero de tic tac toe y no otra cosa. Y ARREGLA TODOS LOS ERRORES ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Ahora se puede tocar el botón de "modo historia" y te llevará a tres niveles. El primer nivel es con un bot de dificultad muy fácil. El segundo nivel es con un bot de dificultad fácil. Y el tercer nivel es un bot de dificultad medio ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Pero arregla el error de cuando vuelvo con el botón de "volver", aparece el texto " juego guardado" y también el de reinicio y volver en el menú. Arregla ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Se elimina el botón de "cargar juego". Además cuando estés en partida, va aver un botón de "volver" para volver al menú. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Pero que el botón de "guardar" solo sirve para si te fuistes del juego (osea no estas jugando). Si esta guardado, se guardará todo. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Arregla todos los errores y en el menú coloca un botón de "estadísticas" que puedes ver tus partidas ganadas y perdidas contra la IA. No te olvides arreglar los errores. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
/****
* 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';
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';
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.3,
scaleY: 1.3
});
// Grid lines - vertical (3 lines for 4x4)
var vLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -150,
rotation: Math.PI / 2,
scaleY: 1.3
});
var vLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: -50,
rotation: Math.PI / 2,
scaleY: 1.3
});
var vLine3 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 50,
rotation: Math.PI / 2,
scaleY: 1.3
});
var vLine4 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 150,
rotation: Math.PI / 2,
scaleY: 1.3
});
// Grid lines - horizontal (3 lines for 4x4)
var hLine1 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -150,
scaleX: 1.3
});
var hLine2 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: -50,
scaleX: 1.3
});
var hLine3 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 50,
scaleX: 1.3
});
var hLine4 = self.attachAsset('gridLine', {
anchorX: 0.5,
anchorY: 0.5,
y: 150,
scaleX: 1.3
});
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) * 100;
cell.y = (row - 1.5) * 100;
// Scale down cells for 4x4 grid
var background = cell.children[0];
if (background) {
background.scaleX = 0.5;
background.scaleY = 0.5;
}
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'
var aiDifficulty = 'easy'; // 'veryEasy', 'easy', 'medium'
var boardSize = 3; // Board size for current game mode
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 = 1700;
gameModeContainer.addChild(fourByFourButton);
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);
var skinsBackButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
skinsBackButton.anchor.set(0.5, 0.5);
skinsBackButton.x = 2048 / 2;
skinsBackButton.y = 1800;
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();
};
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');
};
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);
// 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) {
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();
// Auto start new round after 2 seconds
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) return;
// Reset all button colors
defaultSkinButton.tint = 0x95a5a6;
blueSkinButton.tint = 0x95a5a6;
purpleSkinButton.tint = 0x95a5a6;
orangeSkinButton.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;
}
}
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 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 (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 (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 (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 (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() {
// 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;
}
// 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
}; ===================================================================
--- original.js
+++ change.js
@@ -171,8 +171,145 @@
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.3,
+ scaleY: 1.3
+ });
+ // Grid lines - vertical (3 lines for 4x4)
+ var vLine1 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: -150,
+ rotation: Math.PI / 2,
+ scaleY: 1.3
+ });
+ var vLine2 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: -50,
+ rotation: Math.PI / 2,
+ scaleY: 1.3
+ });
+ var vLine3 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 50,
+ rotation: Math.PI / 2,
+ scaleY: 1.3
+ });
+ var vLine4 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 150,
+ rotation: Math.PI / 2,
+ scaleY: 1.3
+ });
+ // Grid lines - horizontal (3 lines for 4x4)
+ var hLine1 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ y: -150,
+ scaleX: 1.3
+ });
+ var hLine2 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ y: -50,
+ scaleX: 1.3
+ });
+ var hLine3 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ y: 50,
+ scaleX: 1.3
+ });
+ var hLine4 = self.attachAsset('gridLine', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ y: 150,
+ scaleX: 1.3
+ });
+ 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) * 100;
+ cell.y = (row - 1.5) * 100;
+ // Scale down cells for 4x4 grid
+ var background = cell.children[0];
+ if (background) {
+ background.scaleX = 0.5;
+ background.scaleY = 0.5;
+ }
+ 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
****/
@@ -185,10 +322,11 @@
****/
var gameBoard;
var currentPlayer = 'X';
var gameState = 'menu'; // 'menu', 'modeSelection', 'settings', 'playing', 'gameOver', 'storyMode'
-var gameMode = 'twoPlayer'; // 'twoPlayer', 'ai', 'story'
+var gameMode = 'twoPlayer'; // 'twoPlayer', 'ai', 'story', 'fourByFour'
var aiDifficulty = 'easy'; // 'veryEasy', 'easy', 'medium'
+var boardSize = 3; // Board size for current game mode
var playerXScore = 0;
var playerOScore = 0;
var drawScore = 0;
var currentLanguage = storage.language || 'spanish';
@@ -245,18 +383,18 @@
fill: 0x27ae60
});
saveCurrentGameButton.anchor.set(0.5, 0.5);
saveCurrentGameButton.x = 2048 / 2;
-saveCurrentGameButton.y = 1600;
+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 = 1750;
+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,
@@ -303,15 +441,23 @@
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 = 1700;
+gameModeContainer.addChild(fourByFourButton);
var backButton = new Text2('VOLVER', {
size: 80,
fill: 0x95a5a6
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 2048 / 2;
-backButton.y = 1700;
+backButton.y = 1900;
gameModeContainer.addChild(backButton);
gameModeContainer.visible = false;
game.addChild(gameModeContainer);
// Settings UI
@@ -547,8 +693,13 @@
};
storyModeButton.down = function (x, y, obj) {
showStoryModeSelection();
};
+fourByFourButton.down = function (x, y, obj) {
+ gameMode = 'fourByFour';
+ boardSize = 4;
+ startGame();
+};
backButton.down = function (x, y, obj) {
showMainMenu();
};
// Language selection events
@@ -607,9 +758,13 @@
storyModeContainer.visible = false;
gameState = 'playing';
hasActiveGame = true;
// Initialize game board
- gameBoard = new GameBoard();
+ 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
@@ -693,37 +848,51 @@
}
}
function checkWinner() {
var cells = gameBoard.cells;
- // Check rows
- for (var row = 0; row < 3; row++) {
- if (cells[row][0].state === cells[row][1].state && cells[row][1].state === cells[row][2].state && cells[row][0].state !== 'empty') {
- highlightWinningCells([cells[row][0], cells[row][1], cells[row][2]]);
- return cells[row][0].state;
+ 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
- for (var col = 0; col < 3; col++) {
- if (cells[0][col].state === cells[1][col].state && cells[1][col].state === cells[2][col].state && cells[0][col].state !== 'empty') {
- highlightWinningCells([cells[0][col], cells[1][col], cells[2][col]]);
- return cells[0][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
- if (cells[0][0].state === cells[1][1].state && cells[1][1].state === cells[2][2].state && cells[0][0].state !== 'empty') {
- highlightWinningCells([cells[0][0], cells[1][1], cells[2][2]]);
- return cells[0][0].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;
+ }
+ }
}
- if (cells[0][2].state === cells[1][1].state && cells[1][1].state === cells[2][0].state && cells[0][2].state !== 'empty') {
- highlightWinningCells([cells[0][2], cells[1][1], cells[2][0]]);
- return cells[0][2].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 < 3; row++) {
- for (var col = 0; col < 3; col++) {
+ for (var row = 0; row < boardSize; row++) {
+ for (var col = 0; col < boardSize; col++) {
if (cells[row][col].state === 'empty') {
return false;
}
}
@@ -832,10 +1001,10 @@
if (gameState !== 'playing') return;
var cells = gameBoard.cells;
var emptyCells = [];
// Find all empty cells
- for (var row = 0; row < 3; row++) {
- for (var col = 0; col < 3; col++) {
+ 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]);
}
}
@@ -943,9 +1112,13 @@
// Reset game state but keep scores
currentPlayer = 'X';
gameState = 'playing';
// Create new game board
- gameBoard = new GameBoard();
+ if (gameMode === 'fourByFour') {
+ gameBoard = new GameBoard4x4();
+ } else {
+ gameBoard = new GameBoard();
+ }
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2 - 100;
game.addChild(gameBoard);
// Update UI
@@ -1086,8 +1259,9 @@
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 (backButton) backButton.setText('BACK');
// Update settings texts
if (settingsTitle) settingsTitle.setText('SETTINGS');
if (languageLabel) languageLabel.setText('LANGUAGE:');
@@ -1124,8 +1298,9 @@
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 (backButton) backButton.setText('VOLVER');
// Update settings texts
if (settingsTitle) settingsTitle.setText('AJUSTES');
if (languageLabel) languageLabel.setText('IDIOMA:');
@@ -1153,10 +1328,10 @@
function saveGame() {
if (!gameBoard) return;
// Flatten board state to single array for storage compatibility
var flatBoardState = [];
- for (var row = 0; row < 3; row++) {
- for (var col = 0; col < 3; col++) {
+ 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
@@ -1165,8 +1340,9 @@
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,
@@ -1203,17 +1379,23 @@
playerOScore = storage.savedGamePlayerOScore;
drawScore = storage.savedGameDrawScore;
gameMode = storage.savedGameMode;
var flatBoardState = storage.savedGameBoardState;
- // Create new game board
- gameBoard = new GameBoard();
+ // 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 < 3; row++) {
- for (var col = 0; col < 3; col++) {
+ 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);
}
@@ -1263,8 +1445,9 @@
}
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) {