/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var DraggableShip = Container.expand(function (length, id) { var self = Container.call(this); self.length = length; self.shipId = id; self.isHorizontal = true; self.isBeingDragged = false; self.isPlaced = false; self.shipCells = []; self.smallScale = 0.5; // Scale for when ship is waiting // Create visual representation of ship for (var i = 0; i < length; i++) { var shipCell = self.attachAsset('ship', { anchorX: 0, anchorY: 0 }); shipCell.x = i * CELL_SIZE; shipCell.y = 0; self.shipCells.push(shipCell); } // Start ships at smaller scale self.scaleX = self.smallScale; self.scaleY = self.smallScale; self.rotate = function () { self.isHorizontal = !self.isHorizontal; for (var i = 0; i < self.shipCells.length; i++) { if (self.isHorizontal) { self.shipCells[i].x = i * CELL_SIZE; self.shipCells[i].y = 0; } else { self.shipCells[i].x = 0; self.shipCells[i].y = i * CELL_SIZE; } } }; self.canBePlacedAt = function (gridX, gridY) { for (var i = 0; i < self.length; i++) { var x = self.isHorizontal ? gridX + i : gridX; var y = self.isHorizontal ? gridY : gridY + i; if (x >= GRID_SIZE || y >= GRID_SIZE || x < 0 || y < 0) { return false; } if (playerGrid[x][y].hasShip) { return false; } } return true; }; self.placeOnGrid = function (gridX, gridY) { if (self.canBePlacedAt(gridX, gridY)) { for (var i = 0; i < self.length; i++) { var x = self.isHorizontal ? gridX + i : gridX; var y = self.isHorizontal ? gridY : gridY + i; playerGrid[x][y].setShip(self.shipId); } self.isPlaced = true; self.visible = false; return true; } return false; }; self.down = function (x, y, obj) { if (gameState === 'placement') { if (self.isPlaced) { // Remove ship from grid for repositioning removeShipFromGrid(self); self.isPlaced = false; self.visible = true; placedShipsCount--; statusText.setText('Place remaining ships (' + (shipLengths.length - placedShipsCount) + ' left)'); startButton.visible = false; // Position ship at current grid location for smooth dragging var currentGridX = -1, currentGridY = -1; for (var gx = 0; gx < GRID_SIZE; gx++) { for (var gy = 0; gy < GRID_SIZE; gy++) { if (playerGrid[gx][gy].shipId === self.shipId) { currentGridX = gx; currentGridY = gy; break; } } if (currentGridX !== -1) { break; } } if (currentGridX !== -1) { self.x = CENTER_GRID_X + currentGridX * CELL_SIZE; self.y = CENTER_GRID_Y + currentGridY * CELL_SIZE; } } self.isBeingDragged = true; draggedShip = self; // Scale up to full size when dragging tween(self, { alpha: 0.7, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }; self.up = function (x, y, obj) { if (self.isBeingDragged && !self.isPlaced) { self.isBeingDragged = false; // Try to place ship on grid with better snapping var gridX = Math.round((self.x - CENTER_GRID_X) / CELL_SIZE); var gridY = Math.round((self.y - CENTER_GRID_Y) / CELL_SIZE); // Snap to grid position var snapX = CENTER_GRID_X + gridX * CELL_SIZE; var snapY = CENTER_GRID_Y + gridY * CELL_SIZE; if (self.placeOnGrid(gridX, gridY)) { // Snap ship to exact grid position before hiding self.x = snapX; self.y = snapY; tween(self, { alpha: 1 }, { duration: 200 }); placedShipsCount++; if (placedShipsCount >= shipLengths.length) { statusText.setText('All ships placed! Press START GAME to begin'); startButton.visible = true; } else { statusText.setText('Place remaining ships (' + (shipLengths.length - placedShipsCount) + ' left)'); } } else { // Return to original position and scale down if can't place tween(self, { x: self.originalX, y: self.originalY, alpha: 1, scaleX: self.smallScale, scaleY: self.smallScale }, { duration: 300, easing: tween.easeOut }); } } }; return self; }); var GridCell = Container.expand(function (x, y, isPlayer) { var self = Container.call(this); self.gridX = x; self.gridY = y; self.isPlayer = isPlayer; self.hasShip = false; self.isHit = false; self.isMiss = false; self.shipId = -1; var cellGraphics = self.attachAsset('gridBox', { anchorX: 0, anchorY: 0 }); self.setShip = function (shipId) { self.hasShip = true; self.shipId = shipId; if (self.isPlayer) { cellGraphics.tint = 0x808080; if (self.shipAsset) { self.shipAsset.alpha = 1; } } }; self.markHit = function () { self.isHit = true; cellGraphics.tint = 0xFF0000; // Add hit marker asset on top of the cell if (!self.hitMarkerAsset) { self.hitMarkerAsset = self.attachAsset('hitMarker', { anchorX: 0, anchorY: 0 }); // Position hit marker at the correct cell position self.hitMarkerAsset.x = 0; self.hitMarkerAsset.y = 0; } }; self.markMiss = function (isEnemyMiss) { self.isMiss = true; if (isEnemyMiss) { // Enemy missed on player's grid - use orange color cellGraphics.tint = 0xffaa00; } else { // Player missed on enemy's grid - use green color cellGraphics.tint = 0x00FF00; } }; self.markSunk = function () { cellGraphics.tint = 0x2F2F2F; }; self.down = function (x, y, obj) { if (gameState === 'playing' && !self.isPlayer && !self.isHit && !self.isMiss) { playerShoot(self.gridX, self.gridY); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var Ship = function Ship(length, id) { this.length = length; this.id = id; this.hits = 0; this.cells = []; this.isSunk = false; this.addCell = function (x, y) { this.cells.push({ x: x, y: y }); }; this.hit = function () { this.hits++; if (this.hits >= this.length) { this.isSunk = true; return true; } return false; }; }; var GRID_SIZE = 10; var CELL_SIZE = 150; // Increased to 150 for larger game area var GRID_OFFSET_X = 200; var PLAYER_GRID_Y = 1200; var AI_GRID_Y = 200; // Calculate center positions for single grid display var CENTER_GRID_X = (2048 - GRID_SIZE * CELL_SIZE) / 2; var CENTER_GRID_Y = (2732 - GRID_SIZE * CELL_SIZE) / 2 - 100; // Move grid up slightly var GRID_SPACING = 1000; // Distance between the two grids var playerGrid = []; var aiGrid = []; var playerShips = []; var aiShips = []; var gameState = 'intro'; // 'intro', 'placement', 'playing', 'gameOver' var introScreen = null; var currentPlayer = 'player'; // 'player', 'ai' var selectedX = -1; var selectedY = -1; var currentShipToPlace = 0; var isHorizontalPlacement = true; var shipLengths = [5, 4, 3, 3, 2]; var draggableShips = []; var placedShipsCount = 0; var draggedShip = null; // AI hunting variables var aiLastHitX = -1; var aiLastHitY = -1; var aiHuntingDirections = []; var aiCurrentDirection = 0; var aiHuntingMode = false; // Timer and effect variables var turnTimer = null; var turnTimeRemaining = 30; var isWaitingAfterShot = false; // Timer text var timerText = new Text2('30', { size: 50, fill: 0xFF0000 }); timerText.anchor.set(1, 0); LK.gui.topRight.addChild(timerText); var playerGridContainer = new Container(); var aiGridContainer = new Container(); // UI Elements var statusText = new Text2('Drag ships to your grid', { size: 60, fill: 0x000000 }); statusText.anchor.set(0.5, 0); LK.gui.top.addChild(statusText); var instructionText = new Text2('Double tap ships to rotate, drag to reposition', { size: 40, fill: 0x00FF00 }); instructionText.anchor.set(0.5, 0); instructionText.y = 200; instructionText.x = 0; // Starting position for scrolling // Add to game instead of GUI so it appears in front of monitor game.addChild(instructionText); // Position in front of monitor instructionText.x = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 1000; instructionText.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 1700; // Create scrolling text effect function scrollInstructionText() { if (gameState === 'placement') { var startX = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 1200; var endX = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 800; var scrollDuration = 8000; // Duration to reach end position instructionText.x = startX; instructionText.alpha = 1; tween.stop(instructionText); // Stop all existing tweens on instruction text // First tween: scroll to end position tween(instructionText, { x: endX }, { duration: scrollDuration, easing: tween.linear, onFinish: function onFinish() { if (gameState === 'placement') { // Second tween: fade out at end position tween(instructionText, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { if (gameState === 'placement') { // Restart immediately from beginning for seamless loop scrollInstructionText(); } } }); } } }); } } // Start scrolling after placement begins LK.setTimeout(function () { if (gameState === 'placement') { scrollInstructionText(); } }, 500); var startButton = new Text2('START GAME', { size: 60, fill: 0x00FF00 }); startButton.anchor.set(0.5, 0.5); startButton.x = 2048 / 2; startButton.y = 300; // Move button lower startButton.visible = false; game.addChild(startButton); startButton.down = function (x, y, obj) { if (gameState === 'placement' && placedShipsCount >= shipLengths.length) { gameState = 'playing'; statusText.setText('Fire at enemy positions!'); instructionText.setText('Tap enemy grid to shoot'); startButton.visible = false; placeAIShips(); // Switch to AI grid for player's turn to attack with transition effect tween(playerGridContainer, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { playerGridContainer.visible = false; aiGridContainer.visible = true; myCommanderContainer.visible = false; enemyCommanderContainer.visible = true; aiGridContainer.alpha = 0; tween(aiGridContainer, { alpha: 1 }, { duration: 300 }); } }); } }; // Initialize grids function initializeGrids() { // Add containers to game - computer monitor first for background layering game.addChild(playerGridContainer); game.addChild(aiGridContainer); // Move computer monitor to back if it exists if (computerMonitorContainer) { game.removeChild(computerMonitorContainer); game.addChildAt(computerMonitorContainer, 0); } // Player grid (center during placement, left side during game) for (var x = 0; x < GRID_SIZE; x++) { playerGrid[x] = []; for (var y = 0; y < GRID_SIZE; y++) { var cell = new GridCell(x, y, true); cell.x = CENTER_GRID_X + x * CELL_SIZE; cell.y = CENTER_GRID_Y + y * CELL_SIZE; playerGrid[x][y] = cell; playerGridContainer.addChild(cell); } } // AI grid (center during game) for (var x = 0; x < GRID_SIZE; x++) { aiGrid[x] = []; for (var y = 0; y < GRID_SIZE; y++) { var cell = new GridCell(x, y, false); cell.x = CENTER_GRID_X + x * CELL_SIZE; cell.y = CENTER_GRID_Y + y * CELL_SIZE; aiGrid[x][y] = cell; aiGridContainer.addChild(cell); } } // Initialize grid visibility - show player grid during placement phase playerGridContainer.visible = true; aiGridContainer.visible = false; // Add grid labels var playerLabel = new Text2('Your Fleet', { size: 40, fill: 0x000000 }); playerLabel.anchor.set(0.5, 0.5); playerLabel.x = CENTER_GRID_X + GRID_SIZE * CELL_SIZE / 2; playerLabel.y = CENTER_GRID_Y - 50; playerGridContainer.addChild(playerLabel); var aiLabel = new Text2('Enemy Waters', { size: 40, fill: 0x000000 }); aiLabel.anchor.set(0.5, 0.5); aiLabel.x = CENTER_GRID_X + GRID_SIZE * CELL_SIZE / 2; aiLabel.y = CENTER_GRID_Y - 50; aiGridContainer.addChild(aiLabel); // Add coordinate labels for player grid for (var i = 0; i < GRID_SIZE; i++) { // Letters A-J on the left side var letterLabel = new Text2(String.fromCharCode(65 + i), { size: 20, fill: 0x000000 }); letterLabel.anchor.set(0.5, 0.5); letterLabel.x = CENTER_GRID_X - 30; letterLabel.y = CENTER_GRID_Y + i * CELL_SIZE + CELL_SIZE / 2; playerGridContainer.addChild(letterLabel); // Numbers 1-10 on the top var numberLabel = new Text2((i + 1).toString(), { size: 20, fill: 0x000000 }); numberLabel.anchor.set(0.5, 0.5); numberLabel.x = CENTER_GRID_X + i * CELL_SIZE + CELL_SIZE / 2; numberLabel.y = CENTER_GRID_Y - 60; playerGridContainer.addChild(numberLabel); } // Add coordinate labels for AI grid for (var i = 0; i < GRID_SIZE; i++) { // Letters A-J on the left side var letterLabel = new Text2(String.fromCharCode(65 + i), { size: 20, fill: 0x000000 }); letterLabel.anchor.set(0.5, 0.5); letterLabel.x = CENTER_GRID_X - 30; letterLabel.y = CENTER_GRID_Y + i * CELL_SIZE + CELL_SIZE / 2; aiGridContainer.addChild(letterLabel); // Numbers 1-10 on the top var numberLabel = new Text2((i + 1).toString(), { size: 20, fill: 0x000000 }); numberLabel.anchor.set(0.5, 0.5); numberLabel.x = CENTER_GRID_X + i * CELL_SIZE + CELL_SIZE / 2; numberLabel.y = CENTER_GRID_Y - 60; aiGridContainer.addChild(numberLabel); } // Create ship visual assets for player grid for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { var shipAsset = LK.getAsset('ship', { anchorX: 0, anchorY: 0, alpha: 0 }); shipAsset.x = CENTER_GRID_X + x * CELL_SIZE; shipAsset.y = CENTER_GRID_Y + y * CELL_SIZE; playerGrid[x][y].shipAsset = shipAsset; playerGridContainer.addChild(shipAsset); } } // Create draggable ships in a grid layout at bottom of screen, avoiding commander area var shipStartY = 2400; // Move ships lower to avoid commander assets var shipStartX = 600; // Start further right to avoid commander assets var shipsPerRow = 3; // Maximum ships per row var shipSpacingX = 400; // Horizontal spacing between ships var shipSpacingY = 250; // Vertical spacing between rows for (var i = 0; i < shipLengths.length; i++) { var ship = new DraggableShip(shipLengths[i], i); // Calculate grid position for ship var row = Math.floor(i / shipsPerRow); var col = i % shipsPerRow; ship.x = shipStartX + col * shipSpacingX; ship.y = shipStartY + row * shipSpacingY; ship.originalX = ship.x; ship.originalY = ship.y; draggableShips.push(ship); game.addChild(ship); } } function selectCellForPlacement(x, y) { selectedX = x; selectedY = y; if (canPlaceShip(x, y, shipLengths[currentShipToPlace], isHorizontalPlacement, true)) { placeShip(x, y, shipLengths[currentShipToPlace], isHorizontalPlacement, true, currentShipToPlace); currentShipToPlace++; if (currentShipToPlace >= shipLengths.length) { gameState = 'playing'; statusText.setText('Fire at enemy positions!'); instructionText.setText('Tap enemy grid to shoot'); placeAIShips(); } else { statusText.setText('Place ship ' + (currentShipToPlace + 1) + ' (length: ' + shipLengths[currentShipToPlace] + ')'); } } } function canPlaceShip(startX, startY, length, horizontal, isPlayer) { var grid = isPlayer ? playerGrid : aiGrid; for (var i = 0; i < length; i++) { var x = horizontal ? startX + i : startX; var y = horizontal ? startY : startY + i; if (x >= GRID_SIZE || y >= GRID_SIZE || x < 0 || y < 0) { return false; } if (grid[x][y].hasShip) { return false; } } return true; } function placeShip(startX, startY, length, horizontal, isPlayer, shipId) { var grid = isPlayer ? playerGrid : aiGrid; var ships = isPlayer ? playerShips : aiShips; var ship = new Ship(length, shipId); for (var i = 0; i < length; i++) { var x = horizontal ? startX + i : startX; var y = horizontal ? startY : startY + i; grid[x][y].setShip(shipId); ship.addCell(x, y); } ships.push(ship); } function placeAIShips() { for (var i = 0; i < shipLengths.length; i++) { var placed = false; var attempts = 0; while (!placed && attempts < 100) { var x = Math.floor(Math.random() * GRID_SIZE); var y = Math.floor(Math.random() * GRID_SIZE); var horizontal = Math.random() < 0.5; if (canPlaceShip(x, y, shipLengths[i], horizontal, false)) { placeShip(x, y, shipLengths[i], horizontal, false, i); placed = true; } attempts++; } } } function playerShoot(x, y) { if (currentPlayer !== 'player' || isWaitingAfterShot) { return; } // Stop the timer if (turnTimer) { LK.clearInterval(turnTimer); turnTimer = null; } // Play shooting sound when firing LK.getSound('shoot').play(); var cell = aiGrid[x][y]; var hit = cell.hasShip; // Create visual effect var effectAsset = hit ? 'hit' : 'miss'; var effect = LK.getAsset(effectAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1 }); effect.x = CENTER_GRID_X + x * CELL_SIZE + CELL_SIZE / 2; effect.y = CENTER_GRID_Y + y * CELL_SIZE + CELL_SIZE / 2; aiGridContainer.addChild(effect); if (cell.hasShip) { cell.markHit(); hit = true; LK.getSound('explosion').play(); // Explosion effect for hit tween(effect, { scaleX: 2, scaleY: 2, alpha: 0.8 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(effect, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 500, onFinish: function onFinish() { effect.destroy(); } }); } }); var ship = null; // Find the ship by ID in the aiShips array for (var i = 0; i < aiShips.length; i++) { if (aiShips[i].id === cell.shipId) { ship = aiShips[i]; break; } } if (ship) { var sunk = ship.hit(); if (sunk) { LK.getSound('sunk').play(); markShipAsSunk(ship, false); // Enemy commander reacts to ship being sunk showCommanderSpeech(getRandomSpeech(myCommanderSpeech.sunk), false); if (checkVictory(false)) { gameState = 'gameOver'; statusText.setText('Victory! You sank all enemy ships!'); instructionText.setText(''); LK.showYouWin(); return; } } else { // Enemy commander reacts to being hit showCommanderSpeech(getRandomSpeech(myCommanderSpeech.hit), false); } } statusText.setText('Hit! Fire again!'); // No delay for hits - player can continue shooting immediately startPlayerTurn(); } else { cell.markMiss(false); LK.getSound('waterSplash').play(); // Enemy commander reacts to player missing showCommanderSpeech(getRandomSpeech(myCommanderSpeech.miss), false); // Water splash effect for miss effect.tint = 0x00FF00; // Green color to match miss cell tint tween(effect, { scaleX: 1.5, scaleY: 1.5, alpha: 0.6 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { tween(effect, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 600, onFinish: function onFinish() { effect.destroy(); } }); } }); currentPlayer = 'ai'; statusText.setText('Miss! Enemy is thinking...'); // 5-second delay before AI turn isWaitingAfterShot = true; LK.setTimeout(function () { isWaitingAfterShot = false; // Switch to player grid when AI attacks with transition effect tween(aiGridContainer, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { aiGridContainer.visible = false; playerGridContainer.visible = true; enemyCommanderContainer.visible = false; myCommanderContainer.visible = true; playerGridContainer.alpha = 0; tween(playerGridContainer, { alpha: 1 }, { duration: 300 }); } }); startAiTurn(); }, 5000); } } function aiTurn() { if (currentPlayer !== 'ai' || isWaitingAfterShot) { return; } // Stop the timer if (turnTimer) { LK.clearInterval(turnTimer); turnTimer = null; } var x, y; var attempts = 0; // Smart AI: If in hunting mode, try adjacent cells to last hit if (aiHuntingMode && aiLastHitX !== -1 && aiLastHitY !== -1) { var found = false; // Try directions: up, right, down, left var directions = [{ x: 0, y: -1 }, // up { x: 1, y: 0 }, // right { x: 0, y: 1 }, // down { x: -1, y: 0 } // left ]; // Try each direction from the last hit for (var d = 0; d < directions.length && !found; d++) { var testX = aiLastHitX + directions[d].x; var testY = aiLastHitY + directions[d].y; // Check if position is valid and not already attacked if (testX >= 0 && testX < GRID_SIZE && testY >= 0 && testY < GRID_SIZE && !playerGrid[testX][testY].isHit && !playerGrid[testX][testY].isMiss) { x = testX; y = testY; found = true; } } // If no adjacent cells available, exit hunting mode if (!found) { aiHuntingMode = false; aiLastHitX = -1; aiLastHitY = -1; } } // If not in hunting mode or no valid adjacent cells, shoot randomly if (!aiHuntingMode || aiLastHitX === -1 && aiLastHitY === -1) { do { x = Math.floor(Math.random() * GRID_SIZE); y = Math.floor(Math.random() * GRID_SIZE); attempts++; } while ((playerGrid[x][y].isHit || playerGrid[x][y].isMiss) && attempts < 100); } // Play shooting sound when AI fires LK.getSound('shoot').play(); var cell = playerGrid[x][y]; var hit = cell.hasShip; // Create visual effect var effectAsset = hit ? 'hit' : 'miss'; var effect = LK.getAsset(effectAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1 }); effect.x = CENTER_GRID_X + x * CELL_SIZE + CELL_SIZE / 2; effect.y = CENTER_GRID_Y + y * CELL_SIZE + CELL_SIZE / 2; playerGridContainer.addChild(effect); if (cell.hasShip) { cell.markHit(); LK.getSound('explosion').play(); // Explosion effect for hit tween(effect, { scaleX: 2, scaleY: 2, alpha: 0.8 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(effect, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 500, onFinish: function onFinish() { effect.destroy(); } }); } }); // Enter hunting mode and remember this hit position aiHuntingMode = true; aiLastHitX = x; aiLastHitY = y; var ship = null; // Find the ship by ID in the playerShips array for (var i = 0; i < playerShips.length; i++) { if (playerShips[i].id === cell.shipId) { ship = playerShips[i]; break; } } if (ship) { var sunk = ship.hit(); if (sunk) { LK.getSound('sunk').play(); markShipAsSunk(ship, true); // Exit hunting mode when ship is sunk aiHuntingMode = false; aiLastHitX = -1; aiLastHitY = -1; // Enemy commander celebrates sinking player's ship showCommanderSpeech(getRandomSpeech(enemyCommanderSpeech.sunk), true); if (checkVictory(true)) { gameState = 'gameOver'; statusText.setText('Defeat! Enemy sank all your ships!'); instructionText.setText(''); LK.showGameOver(); return; } } else { // Enemy commander celebrates hitting player's ship showCommanderSpeech(getRandomSpeech(enemyCommanderSpeech.hit), true); } } statusText.setText('Enemy hit your ship!'); // 5-second delay before AI continues isWaitingAfterShot = true; LK.setTimeout(function () { isWaitingAfterShot = false; startAiTurn(); }, 5000); } else { cell.markMiss(true); LK.getSound('waterSplash').play(); // Enemy commander reacts to missing showCommanderSpeech(getRandomSpeech(enemyCommanderSpeech.miss), true); // Water splash effect for miss effect.tint = 0xFFAA00; tween(effect, { scaleX: 1.5, scaleY: 1.5, alpha: 0.6 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { tween(effect, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 600, onFinish: function onFinish() { effect.destroy(); } }); } }); currentPlayer = 'player'; statusText.setText('Enemy missed! Your turn!'); // 5-second delay before player turn isWaitingAfterShot = true; LK.setTimeout(function () { isWaitingAfterShot = false; // Switch to AI grid for player's turn to attack with transition effect tween(playerGridContainer, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { playerGridContainer.visible = false; aiGridContainer.visible = true; myCommanderContainer.visible = false; enemyCommanderContainer.visible = true; aiGridContainer.alpha = 0; tween(aiGridContainer, { alpha: 1 }, { duration: 300 }); } }); startPlayerTurn(); }, 5000); } } function markShipAsSunk(ship, isPlayer) { var grid = isPlayer ? playerGrid : aiGrid; for (var i = 0; i < ship.cells.length; i++) { var cellPos = ship.cells[i]; grid[cellPos.x][cellPos.y].markSunk(); // Darken hit markers on sunk ship cells if (grid[cellPos.x][cellPos.y].hitMarkerAsset) { tween(grid[cellPos.x][cellPos.y].hitMarkerAsset, { alpha: 0.4 }, { duration: 500, easing: tween.easeOut }); } } } function removeShipFromGrid(ship) { for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { if (playerGrid[x][y].shipId === ship.shipId) { playerGrid[x][y].hasShip = false; playerGrid[x][y].shipId = -1; var cellGraphics = playerGrid[x][y].children[0]; cellGraphics.tint = 0xFFFFFF; if (playerGrid[x][y].shipAsset) { playerGrid[x][y].shipAsset.alpha = 0; } } } } } function checkVictory(playerLost) { var ships = playerLost ? playerShips : aiShips; for (var i = 0; i < ships.length; i++) { if (!ships[i].isSunk) { return false; } } return true; } // Double tap to rotate ship during placement var lastTapTime = 0; game.down = function (x, y, obj) { if (gameState === 'placement') { var currentTime = Date.now(); if (currentTime - lastTapTime < 300) { // Find ship being rotated - check both unplaced and placed ships for (var i = 0; i < draggableShips.length; i++) { var ship = draggableShips[i]; var shipBounds = { x: ship.x, y: ship.y, width: ship.isHorizontal ? ship.length * CELL_SIZE : CELL_SIZE, height: ship.isHorizontal ? CELL_SIZE : ship.length * CELL_SIZE }; if (x >= shipBounds.x && x <= shipBounds.x + shipBounds.width && y >= shipBounds.y && y <= shipBounds.y + shipBounds.height) { if (ship.isPlaced) { // Remove ship from grid removeShipFromGrid(ship); ship.isPlaced = false; ship.visible = true; placedShipsCount--; } ship.rotate(); break; } } instructionText.setText('Double tap ships to rotate, drag to reposition'); } lastTapTime = currentTime; } }; game.move = function (x, y, obj) { if (gameState === 'placement' && draggedShip) { draggedShip.x = x - draggedShip.width / 2; draggedShip.y = y - draggedShip.height / 2; } }; game.up = function (x, y, obj) { draggedShip = null; }; function startPlayerTurn() { currentPlayer = 'player'; turnTimeRemaining = 30; timerText.setText(turnTimeRemaining.toString()); timerText.visible = true; // Clear any existing timer first if (turnTimer) { LK.clearInterval(turnTimer); turnTimer = null; } turnTimer = LK.setInterval(function () { turnTimeRemaining--; timerText.setText(turnTimeRemaining.toString()); if (turnTimeRemaining <= 0) { LK.clearInterval(turnTimer); turnTimer = null; timerText.visible = false; // Auto-switch to AI turn on timeout currentPlayer = 'ai'; statusText.setText('Time up! Enemy is thinking...'); tween(aiGridContainer, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { aiGridContainer.visible = false; playerGridContainer.visible = true; enemyCommanderContainer.visible = false; myCommanderContainer.visible = true; playerGridContainer.alpha = 0; tween(playerGridContainer, { alpha: 1 }, { duration: 300 }); } }); startAiTurn(); } }, 1000); } function startAiTurn() { currentPlayer = 'ai'; turnTimeRemaining = 30; timerText.setText(turnTimeRemaining.toString()); timerText.visible = true; // Clear any existing timer first if (turnTimer) { LK.clearInterval(turnTimer); turnTimer = null; } // AI thinks for 2-5 seconds before shooting var thinkTime = 2000 + Math.random() * 3000; turnTimer = LK.setInterval(function () { turnTimeRemaining--; timerText.setText(turnTimeRemaining.toString()); if (turnTimeRemaining <= 0) { LK.clearInterval(turnTimer); turnTimer = null; timerText.visible = false; aiTurn(); } }, 1000); LK.setTimeout(function () { if (currentPlayer === 'ai' && !isWaitingAfterShot) { aiTurn(); } }, thinkTime); } // Create commander assets var myCommanderContainer = new Container(); var enemyCommanderContainer = new Container(); // Position commanders higher and at bottom left corner myCommanderContainer.x = 50; myCommanderContainer.y = 2732 - 500; // Moved even higher up enemyCommanderContainer.x = 50; enemyCommanderContainer.y = 2732 - 500; // Same position as my commander // Create shadow assets first (positioned slightly offset) var myCommanderShadow = myCommanderContainer.attachAsset('myCommander', { anchorX: 0, anchorY: 0, scaleX: 2.2, scaleY: 2.2, alpha: 0.3, tint: 0x000000 }); myCommanderShadow.x = 8; myCommanderShadow.y = 8; var enemyCommanderShadow = enemyCommanderContainer.attachAsset('enemyCommander', { anchorX: 0, anchorY: 0, scaleX: 2.2, scaleY: 2.2, alpha: 0.3, tint: 0x000000 }); enemyCommanderShadow.x = 8; enemyCommanderShadow.y = 8; // Create main commander assets (larger size) var myCommanderAsset = myCommanderContainer.attachAsset('myCommander', { anchorX: 0, anchorY: 0, scaleX: 2.2, scaleY: 2.2 }); var enemyCommanderAsset = enemyCommanderContainer.attachAsset('enemyCommander', { anchorX: 0, anchorY: 0, scaleX: 2.2, scaleY: 2.2 }); // Add commanders to game game.addChild(myCommanderContainer); game.addChild(enemyCommanderContainer); // Initialize commander visibility - show my commander during placement phase myCommanderContainer.visible = true; enemyCommanderContainer.visible = false; // Commander speech arrays var myCommanderSpeech = { hit: ["Lanet olsun! Nasıl buldun?", "Bu olamaz! Şanslı atış!", "Beni buldun ama savaş henüz bitmedi!", "İyi atış... ama son olmayacak!"], miss: ["Hah! Ordumu bulmak kolay değil!", "Boşa atış! Denemeye devam et!", "Su sıçradı ama gemim yerinde!", "Yaklaştın ama yeterli değil!"], sunk: ["Gemimi batırdın... ama intikam alacağım!", "Bu savaşı kaybettim ama onur hala benimle!", "İyi savaştın... şimdilik kazandın"] }; var enemyCommanderSpeech = { hit: ["Mükemmel atış! Hedefi bulduk!", "Doğrudan isabet! Böyle devam!", "Ateş! Düşman gemisini yakaladık!", "Harika! Bu şekilde kazanacağız!"], miss: ["Lanet! Hedefi kaçırdık!", "Su sıçradı! Tekrar dene!", "Bu sefer olmadı ama pes etmem!", "Yakında hedefimi bulacağım!"], sunk: ["Bir gemi daha battı! Zafer yakın!", "Düşman filosu eriyor!", "Mükemmel! Böyle devam edersek kazanırız!"] }; // Current speech display var currentSpeechText = null; var speechTimeout = null; function showCommanderSpeech(text, isEnemyCommander) { // Clear existing speech if (currentSpeechText) { currentSpeechText.destroy(); currentSpeechText = null; } if (speechTimeout) { LK.clearTimeout(speechTimeout); speechTimeout = null; } // Create speech bubble container currentSpeechText = new Container(); // Create speech bubble background var speechBubble = currentSpeechText.attachAsset('speechBubble', { anchorX: 0, anchorY: 0 }); // Create speech bubble text var speechText = new Text2(text, { size: 36, fill: 0x000000 }); speechText.anchor.set(0.5, 0.5); speechText.x = 350; // Move text further to the right in bubble speechText.y = 75; // Center in bubble currentSpeechText.addChild(speechText); // Add speech bubble directly to game instead of commander container game.addChild(currentSpeechText); // Position speech bubble next to appropriate commander if (isEnemyCommander && enemyCommanderContainer.visible) { currentSpeechText.x = enemyCommanderContainer.x + 440; // Position to the right of commander currentSpeechText.y = enemyCommanderContainer.y + 50; // Align with top of commander } else if (!isEnemyCommander && myCommanderContainer.visible) { currentSpeechText.x = myCommanderContainer.x + 440; // Position to the right of commander currentSpeechText.y = myCommanderContainer.y + 50; // Align with top of commander } else { // Fallback positioning if no commander is visible currentSpeechText.x = 50 + 440; currentSpeechText.y = 2732 - 500 + 50; } // Animate speech appearance currentSpeechText.alpha = 0; tween(currentSpeechText, { alpha: 1 }, { duration: 300 }); // Remove speech after 3 seconds speechTimeout = LK.setTimeout(function () { if (currentSpeechText) { tween(currentSpeechText, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { if (currentSpeechText) { currentSpeechText.destroy(); currentSpeechText = null; } } }); } }, 3000); } function getRandomSpeech(speechArray) { return speechArray[Math.floor(Math.random() * speechArray.length)]; } // Initialize the game function createIntroScreen() { introScreen = new Container(); game.addChild(introScreen); // Create television frame var tvFrame = LK.getAsset('tvFrame', { anchorX: 0.5, anchorY: 0.5 }); tvFrame.x = 2048 / 2; tvFrame.y = 2732 / 2; introScreen.addChild(tvFrame); // Create TV stand var tvStand = LK.getAsset('tvStand', { anchorX: 0.5, anchorY: 0 }); tvStand.x = 2048 / 2; tvStand.y = 2732 / 2 + 1450; introScreen.addChild(tvStand); // Create TV buttons and speakers var powerButton = LK.getAsset('tvButton', { anchorX: 0.5, anchorY: 0.5, tint: 0xFF4444 }); powerButton.x = 2048 / 2 + 1200; powerButton.y = 2732 / 2 - 1200; introScreen.addChild(powerButton); var volumeButton = LK.getAsset('tvButton', { anchorX: 0.5, anchorY: 0.5, tint: 0x4444FF }); volumeButton.x = 2048 / 2 + 1200; volumeButton.y = 2732 / 2 - 1100; introScreen.addChild(volumeButton); // Create speakers for (var s = 0; s < 6; s++) { var speaker = LK.getAsset('tvSpeaker', { anchorX: 0.5, anchorY: 0.5 }); speaker.x = 2048 / 2 + 1200; speaker.y = 2732 / 2 - 800 + s * 60; introScreen.addChild(speaker); } // Create TV screen (black background) var tvScreen = LK.getAsset('tvScreen', { anchorX: 0.5, anchorY: 0.5 }); tvScreen.x = 2048 / 2; tvScreen.y = 2732 / 2; introScreen.addChild(tvScreen); // Create container for all TV content var tvContent = new Container(); tvContent.x = 2048 / 2 - 950; // Center content in TV screen tvContent.y = 2732 / 2 - 1300; introScreen.addChild(tvContent); // Create gradient background layers for depth (now inside TV) var bgLayer1 = LK.getAsset('gridBox', { anchorX: 0, anchorY: 0, scaleX: 16, scaleY: 21, tint: 0x001A33, alpha: 1 }); tvContent.addChild(bgLayer1); var bgLayer2 = LK.getAsset('gridBox', { anchorX: 0, anchorY: 0, scaleX: 15, scaleY: 19, tint: 0x003D66, alpha: 0.7 }); bgLayer2.x = 80; bgLayer2.y = 80; tvContent.addChild(bgLayer2); // Create multiple water layers for depth var waterLayer1 = LK.getAsset('water', { anchorX: 0, anchorY: 0, scaleX: 24, scaleY: 28, alpha: 0.08, tint: 0x1B4F72 }); tvContent.addChild(waterLayer1); var waterLayer2 = LK.getAsset('water', { anchorX: 0, anchorY: 0, scaleX: 20, scaleY: 24, alpha: 0.15, tint: 0x2E86AB }); tvContent.addChild(waterLayer2); var waterLayer3 = LK.getAsset('water', { anchorX: 0, anchorY: 0, scaleX: 16, scaleY: 20, alpha: 0.12, tint: 0x5DADE2 }); tvContent.addChild(waterLayer3); // Create floating water particles var waterParticles = []; for (var p = 0; p < 8; p++) { var particle = LK.getAsset('water', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3 + Math.random() * 0.2, scaleY: 0.3 + Math.random() * 0.2, alpha: 0.2 + Math.random() * 0.1, tint: 0x87CEEB }); particle.x = Math.random() * 1900; particle.y = Math.random() * 2600; waterParticles.push(particle); tvContent.addChild(particle); } // Animate water layers with wave motion tween(waterLayer1, { alpha: 0.12, x: -50 }, { duration: 4000, easing: tween.easeInOut, onFinish: function onFinish() { tween(waterLayer1, { alpha: 0.08, x: 0 }, { duration: 4000, easing: tween.easeInOut, onFinish: function onFinish() { tween(waterLayer1, { alpha: 0.12, x: -50 }, { duration: 4000, easing: tween.easeInOut, onFinish: arguments.callee }); } }); } }); tween(waterLayer2, { alpha: 0.22, x: 30 }, { duration: 3500, easing: tween.easeInOut, onFinish: function onFinish() { tween(waterLayer2, { alpha: 0.15, x: -30 }, { duration: 3500, easing: tween.easeInOut, onFinish: function onFinish() { tween(waterLayer2, { alpha: 0.22, x: 30 }, { duration: 3500, easing: tween.easeInOut, onFinish: arguments.callee }); } }); } }); tween(waterLayer3, { alpha: 0.18, y: -20 }, { duration: 3000, easing: tween.easeInOut, onFinish: function onFinish() { tween(waterLayer3, { alpha: 0.12, y: 20 }, { duration: 3000, easing: tween.easeInOut, onFinish: function onFinish() { tween(waterLayer3, { alpha: 0.18, y: -20 }, { duration: 3000, easing: tween.easeInOut, onFinish: arguments.callee }); } }); } }); // Animate water particles for (var p = 0; p < waterParticles.length; p++) { (function (particle, index) { function floatParticle() { if (gameState === 'intro') { var targetX = Math.random() * 1900; var targetY = Math.random() * 2600; tween(particle, { x: targetX, y: targetY, alpha: 0.1 + Math.random() * 0.15, rotation: Math.random() * Math.PI * 2 }, { duration: 8000 + Math.random() * 4000, easing: tween.easeInOut, onFinish: floatParticle }); } } LK.setTimeout(floatParticle, index * 500); })(waterParticles[p], p); } // Main title with shadow effect var titleShadow = new Text2('NAVAL COMBAT', { size: 100, fill: 0x000000 }); titleShadow.anchor.set(0.5, 0.5); titleShadow.x = 950 + 6; titleShadow.y = 406; titleShadow.alpha = 0; tvContent.addChild(titleShadow); var titleText = new Text2('NAVAL COMBAT', { size: 100, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.x = 950; titleText.y = 400; titleText.alpha = 0; tvContent.addChild(titleText); // Subtitle with glow effect var subtitleGlow = new Text2('Strategic Fleet Warfare', { size: 42, fill: 0x87CEEB }); subtitleGlow.anchor.set(0.5, 0.5); subtitleGlow.x = 950; subtitleGlow.y = 550; subtitleGlow.alpha = 0; tvContent.addChild(subtitleGlow); var subtitleText = new Text2('Strategic Fleet Warfare', { size: 42, fill: 0xFFFFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 950; subtitleText.y = 550; subtitleText.alpha = 0; tvContent.addChild(subtitleText); // Create naval battle themed decorative elements var navyElements = []; var shipColors = [0x2C3E50, 0x34495E, 0x566573, 0x7F8C8D, 0x95A5A6]; var enemyColors = [0x8B0000, 0xA52A2A, 0xB22222, 0xDC143C, 0xFF4500]; // Create allied fleet formation (left side) for (var i = 0; i < 4; i++) { var allyShip = LK.getAsset('ship', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0 + i * 0.2, scaleY: 2.0 + i * 0.2, tint: shipColors[i] }); allyShip.x = 120 + i * 50; allyShip.y = 750 + i * 70; allyShip.alpha = 0; allyShip.rotation = Math.PI / 8 + i * 0.05; navyElements.push(allyShip); tvContent.addChild(allyShip); } // Create enemy fleet formation (right side) for (var i = 0; i < 4; i++) { var enemyShip = LK.getAsset('ship', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0 + i * 0.2, scaleY: 2.0 + i * 0.2, tint: enemyColors[i] }); enemyShip.x = 1780 - i * 50; enemyShip.y = 750 + i * 70; enemyShip.alpha = 0; enemyShip.rotation = -Math.PI / 8 - i * 0.05; navyElements.push(enemyShip); tvContent.addChild(enemyShip); } // Add naval commander silhouettes var allyCommander = LK.getAsset('myCommander', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, alpha: 0, tint: 0x2C3E50 }); allyCommander.x = 250; allyCommander.y = 1100; navyElements.push(allyCommander); tvContent.addChild(allyCommander); var enemyCommander = LK.getAsset('enemyCommander', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, alpha: 0, tint: 0x8B0000 }); enemyCommander.x = 1650; enemyCommander.y = 1100; navyElements.push(enemyCommander); tvContent.addChild(enemyCommander); // Add strategic grid overlay effects for (var i = 0; i < 6; i++) { var gridElement = LK.getAsset('gridBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, alpha: 0, tint: 0x3498DB }); gridElement.x = 950 + (i % 3 - 1) * 160; gridElement.y = 900 + Math.floor(i / 3) * 120; navyElements.push(gridElement); tvContent.addChild(gridElement); } // Replace shipFormation reference with navyElements var shipFormation = navyElements; // Enhanced start button with frame var buttonFrame = LK.getAsset('gridBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.2, scaleY: 1.0, tint: 0xFFD700, alpha: 0 }); buttonFrame.x = 950; buttonFrame.y = 1300; tvContent.addChild(buttonFrame); var startGameButton = new Text2('⚓ START BATTLE ⚓', { size: 58, fill: 0x000000 }); startGameButton.anchor.set(0.5, 0.5); startGameButton.x = 950; startGameButton.y = 1300; startGameButton.alpha = 0; tvContent.addChild(startGameButton); // Additional UI elements var versionText = new Text2('Admiral Edition v1.0', { size: 25, fill: 0x808080 }); versionText.anchor.set(0.5, 0.5); versionText.x = 950; versionText.y = 1450; versionText.alpha = 0; tvContent.addChild(versionText); var instructionsText = new Text2('Deploy your fleet strategically and command the seas!', { size: 36, fill: 0xE0E0E0 }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 950; instructionsText.y = 1600; instructionsText.alpha = 0; tvContent.addChild(instructionsText); // Professional animation sequence // Title entrance with dramatic effect tween(titleShadow, { alpha: 0.6, y: 500, scaleX: 1.05, scaleY: 1.05 }, { duration: 1200, easing: tween.elasticOut }); tween(titleText, { alpha: 1, y: 500, scaleX: 1.1, scaleY: 1.1 }, { duration: 1200, easing: tween.elasticOut }); // Subtitle with sequential glow effect LK.setTimeout(function () { tween(subtitleGlow, { alpha: 0.4, scaleX: 1.05, scaleY: 1.05 }, { duration: 800, easing: tween.easeOut }); tween(subtitleText, { alpha: 1 }, { duration: 800, easing: tween.easeOut }); }, 400); // Naval fleet formation entrance with battle positioning LK.setTimeout(function () { for (var i = 0; i < navyElements.length; i++) { (function (element, delay) { LK.setTimeout(function () { // Different entrance effects based on element type var isShip = element.x < 1024; // Left side elements are allied ships var entranceEffect = isShip ? { alpha: 0.85, scaleX: element.scaleX * 1.2, scaleY: element.scaleY * 1.2, x: element.x + (isShip ? 50 : -50) } : { alpha: 0.7, scaleX: element.scaleX * 1.1, scaleY: element.scaleY * 1.1 }; tween(element, entranceEffect, { duration: 1200, easing: tween.elasticOut }); // Add naval movement patterns function navalMovement() { if (gameState === 'intro') { var movementPattern = isShip ? { y: element.y + (10 + Math.random() * 8), rotation: element.rotation + (Math.random() - 0.5) * 0.1, alpha: element.alpha * (0.9 + Math.random() * 0.2) } : { scaleX: element.scaleX * (0.95 + Math.random() * 0.1), scaleY: element.scaleY * (0.95 + Math.random() * 0.1), alpha: element.alpha * (0.8 + Math.random() * 0.4) }; tween(element, movementPattern, { duration: 2500 + Math.random() * 1500, easing: tween.easeInOut, onFinish: function onFinish() { var returnPattern = isShip ? { y: element.y - (10 + Math.random() * 8), rotation: element.rotation - (Math.random() - 0.5) * 0.1, alpha: element.alpha / (0.9 + Math.random() * 0.2) } : { scaleX: element.scaleX / (0.95 + Math.random() * 0.1), scaleY: element.scaleY / (0.95 + Math.random() * 0.1), alpha: element.alpha / (0.8 + Math.random() * 0.4) }; tween(element, returnPattern, { duration: 2500 + Math.random() * 1500, easing: tween.easeInOut, onFinish: navalMovement }); } }); } } LK.setTimeout(navalMovement, 1500 + Math.random() * 1000); }, delay * 120); })(navyElements[i], i); } }, 800); // Button entrance with frame LK.setTimeout(function () { tween(buttonFrame, { alpha: 0.8, scaleX: 4.2, scaleY: 1.3 }, { duration: 600, easing: tween.easeOut }); tween(startGameButton, { alpha: 1, scaleX: 1.0, scaleY: 1.0 }, { duration: 600, easing: tween.bounceOut }); tween(versionText, { alpha: 1 }, { duration: 600, easing: tween.easeOut }); tween(instructionsText, { alpha: 1 }, { duration: 600, easing: tween.easeOut }); }, 1400); // Enhanced pulsing effect for start button LK.setTimeout(function () { function pulseButton() { if (gameState === 'intro') { tween(startGameButton, { scaleX: 1.15, scaleY: 1.15, tint: 0x00FF00 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(startGameButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 1000, easing: tween.easeInOut, onFinish: pulseButton }); } }); tween(buttonFrame, { alpha: 1, scaleX: 4.4, scaleY: 1.4 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(buttonFrame, { alpha: 0.8, scaleX: 4.2, scaleY: 1.3 }, { duration: 1000, easing: tween.easeInOut }); } }); } } pulseButton(); }, 2000); // Add TV static overlay effect var tvStatic = LK.getAsset('gridBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 16, scaleY: 21, alpha: 0.02, tint: 0xFFFFFF }); tvStatic.x = 950; tvStatic.y = 1300; tvContent.addChild(tvStatic); // Animate TV static function animateStatic() { if (gameState === 'intro') { tween(tvStatic, { alpha: 0.05 + Math.random() * 0.03, scaleX: 16 + Math.random() * 0.2, scaleY: 21 + Math.random() * 0.2 }, { duration: 100 + Math.random() * 200, onFinish: animateStatic }); } } animateStatic(); // Add TV screen glow effect var screenGlow = LK.getAsset('tvScreen', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.05, scaleY: 1.05, tint: 0x4A90E2, alpha: 0.1 }); screenGlow.x = 2048 / 2; screenGlow.y = 2732 / 2; introScreen.addChild(screenGlow); // Animate screen glow function animateGlow() { if (gameState === 'intro') { tween(screenGlow, { alpha: 0.15, scaleX: 1.08, scaleY: 1.08 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(screenGlow, { alpha: 0.1, scaleX: 1.05, scaleY: 1.05 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateGlow }); } }); } } animateGlow(); // Click handler for start button with effect startGameButton.down = function (x, y, obj) { if (gameState === 'intro') { // Button press effect tween(startGameButton, { scaleX: 0.95, scaleY: 0.95, alpha: 0.8 }, { duration: 100, onFinish: function onFinish() { tween(startGameButton, { scaleX: 1.0, scaleY: 1.0, alpha: 1 }, { duration: 100 }); } }); // Fade out intro screen with dramatic effect tween(introScreen, { alpha: 0, scaleX: 1.1, scaleY: 1.1 }, { duration: 800, easing: tween.easeIn, onFinish: function onFinish() { introScreen.destroy(); introScreen = null; gameState = 'placement'; statusText.setText('Drag ships to your grid'); // Start ocean waves music after intro ends LK.playMusic('oceanWaves'); // Show computer monitor and radar system computerMonitorContainer.visible = true; computerMonitorContainer.alpha = 0; tween(computerMonitorContainer, { alpha: 1 }, { duration: 1000, easing: tween.easeOut }); initializeGrids(); // Start scrolling text immediately when placement begins scrollInstructionText(); } }); } }; } createIntroScreen(); // Create computer monitor frame around game area var computerMonitorContainer = new Container(); computerMonitorContainer.visible = false; // Hide during intro game.addChild(computerMonitorContainer); // Position computer monitor behind the game grids var monitorFrame = computerMonitorContainer.attachAsset('computerMonitor', { anchorX: 0.5, anchorY: 0.5 }); monitorFrame.x = 2048 / 2; monitorFrame.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE / 2; // Computer screen background var computerScreen = computerMonitorContainer.attachAsset('computerScreen', { anchorX: 0.5, anchorY: 0.5 }); computerScreen.x = 2048 / 2; computerScreen.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE / 2; // Computer base var computerBase = computerMonitorContainer.attachAsset('computerBase', { anchorX: 0.5, anchorY: 0 }); computerBase.x = 2048 / 2; computerBase.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE + 50; // Create rotating radar system in background var radarContainer = new Container(); computerMonitorContainer.addChild(radarContainer); radarContainer.x = 2048 / 2; radarContainer.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE / 2; // Radar circles (concentric) for (var i = 1; i <= 3; i++) { var radarCircle = radarContainer.attachAsset('radarCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: i * 0.4, scaleY: i * 0.4, alpha: 0.15 }); radarCircle.x = 0; radarCircle.y = 0; } // Radar center dot var radarCenter = radarContainer.attachAsset('radarCenter', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); radarCenter.x = 0; radarCenter.y = 0; // Rotating radar sweep line var radarSweep = radarContainer.attachAsset('radarSweep', { anchorX: 0, anchorY: 0.5, alpha: 0.6 }); radarSweep.x = 0; radarSweep.y = 0; // Start radar sweep rotation function rotateRadarSweep() { if (gameState === 'playing' || gameState === 'placement') { tween(radarSweep, { rotation: radarSweep.rotation + Math.PI * 2 }, { duration: 3000, easing: tween.linear, onFinish: rotateRadarSweep }); } } // Start radar sweep after placement phase begins LK.setTimeout(function () { if (gameState === 'placement') { rotateRadarSweep(); } }, 1500); // Start radar sweep after initialization LK.setTimeout(function () { rotateRadarSweep(); }, 500); // Add computer-style grid overlay var computerGrid = new Container(); computerMonitorContainer.addChild(computerGrid); computerGrid.x = 2048 / 2 - 950; computerGrid.y = CENTER_GRID_Y - 750; // Create computer grid lines for (var i = 0; i <= 20; i++) { // Vertical lines var vLine = LK.getAsset('gridLine', { anchorX: 0.5, anchorY: 0, scaleX: 0.5, scaleY: 1.2, alpha: 0.08, tint: 0x00FF00 }); vLine.x = i * 95; vLine.y = 0; computerGrid.addChild(vLine); // Horizontal lines var hLine = LK.getAsset('gridLine', { anchorX: 0, anchorY: 0.5, scaleX: 1.0, scaleY: 0.5, alpha: 0.08, tint: 0x00FF00, rotation: Math.PI / 2 }); hLine.x = 0; hLine.y = i * 75; computerGrid.addChild(hLine); } // Add ocean movement to game containers function addOceanMovement() { if (gameState === 'playing' || gameState === 'placement') { // Gentle rocking motion for player grid tween(playerGridContainer, { x: playerGridContainer.x + (Math.random() - 0.5) * 8, y: playerGridContainer.y + (Math.random() - 0.5) * 6, rotation: playerGridContainer.rotation + (Math.random() - 0.5) * 0.008 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Return to original position with slight variation tween(playerGridContainer, { x: game.children.indexOf(playerGridContainer) >= 0 ? playerGridContainer.x - (Math.random() - 0.5) * 8 : playerGridContainer.x, y: game.children.indexOf(playerGridContainer) >= 0 ? playerGridContainer.y - (Math.random() - 0.5) * 6 : playerGridContainer.y, rotation: playerGridContainer.rotation - (Math.random() - 0.5) * 0.008 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: addOceanMovement }); } }); // Similar motion for AI grid tween(aiGridContainer, { x: aiGridContainer.x + (Math.random() - 0.5) * 8, y: aiGridContainer.y + (Math.random() - 0.5) * 6, rotation: aiGridContainer.rotation + (Math.random() - 0.5) * 0.008 }, { duration: 2200 + Math.random() * 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(aiGridContainer, { x: game.children.indexOf(aiGridContainer) >= 0 ? aiGridContainer.x - (Math.random() - 0.5) * 8 : aiGridContainer.x, y: game.children.indexOf(aiGridContainer) >= 0 ? aiGridContainer.y - (Math.random() - 0.5) * 6 : aiGridContainer.y, rotation: aiGridContainer.rotation - (Math.random() - 0.5) * 0.008 }, { duration: 2200 + Math.random() * 800, easing: tween.easeInOut }); } }); // Gentle movement for commander containers tween(myCommanderContainer, { x: myCommanderContainer.x + (Math.random() - 0.5) * 4, y: myCommanderContainer.y + (Math.random() - 0.5) * 3 }, { duration: 3000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(myCommanderContainer, { x: 50 + (Math.random() - 0.5) * 4, y: 2732 - 500 + (Math.random() - 0.5) * 3 }, { duration: 3000 + Math.random() * 1000, easing: tween.easeInOut }); } }); tween(enemyCommanderContainer, { x: enemyCommanderContainer.x + (Math.random() - 0.5) * 4, y: enemyCommanderContainer.y + (Math.random() - 0.5) * 3 }, { duration: 2800 + Math.random() * 1200, easing: tween.easeInOut, onFinish: function onFinish() { tween(enemyCommanderContainer, { x: 50 + (Math.random() - 0.5) * 4, y: 2732 - 500 + (Math.random() - 0.5) * 3 }, { duration: 2800 + Math.random() * 1200, easing: tween.easeInOut }); } }); } } // Add computer screen effects function addComputerEffects() { if (gameState === 'playing' || gameState === 'placement') { // Screen flicker effect if (computerScreen) { var flickerAlpha = 0.95 + Math.random() * 0.05; tween(computerScreen, { alpha: flickerAlpha, tint: 0x001100 + Math.floor(Math.random() * 0x001100) }, { duration: 100 + Math.random() * 200, onFinish: addComputerEffects }); } } } // Start computer effects LK.setTimeout(function () { addComputerEffects(); }, 2000); // Start ocean movement after a short delay LK.setTimeout(function () { addOceanMovement(); }, 1000); // Ocean waves music will start after intro ends // Game code saved successfully - Naval Combat Game Complete;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var DraggableShip = Container.expand(function (length, id) {
var self = Container.call(this);
self.length = length;
self.shipId = id;
self.isHorizontal = true;
self.isBeingDragged = false;
self.isPlaced = false;
self.shipCells = [];
self.smallScale = 0.5; // Scale for when ship is waiting
// Create visual representation of ship
for (var i = 0; i < length; i++) {
var shipCell = self.attachAsset('ship', {
anchorX: 0,
anchorY: 0
});
shipCell.x = i * CELL_SIZE;
shipCell.y = 0;
self.shipCells.push(shipCell);
}
// Start ships at smaller scale
self.scaleX = self.smallScale;
self.scaleY = self.smallScale;
self.rotate = function () {
self.isHorizontal = !self.isHorizontal;
for (var i = 0; i < self.shipCells.length; i++) {
if (self.isHorizontal) {
self.shipCells[i].x = i * CELL_SIZE;
self.shipCells[i].y = 0;
} else {
self.shipCells[i].x = 0;
self.shipCells[i].y = i * CELL_SIZE;
}
}
};
self.canBePlacedAt = function (gridX, gridY) {
for (var i = 0; i < self.length; i++) {
var x = self.isHorizontal ? gridX + i : gridX;
var y = self.isHorizontal ? gridY : gridY + i;
if (x >= GRID_SIZE || y >= GRID_SIZE || x < 0 || y < 0) {
return false;
}
if (playerGrid[x][y].hasShip) {
return false;
}
}
return true;
};
self.placeOnGrid = function (gridX, gridY) {
if (self.canBePlacedAt(gridX, gridY)) {
for (var i = 0; i < self.length; i++) {
var x = self.isHorizontal ? gridX + i : gridX;
var y = self.isHorizontal ? gridY : gridY + i;
playerGrid[x][y].setShip(self.shipId);
}
self.isPlaced = true;
self.visible = false;
return true;
}
return false;
};
self.down = function (x, y, obj) {
if (gameState === 'placement') {
if (self.isPlaced) {
// Remove ship from grid for repositioning
removeShipFromGrid(self);
self.isPlaced = false;
self.visible = true;
placedShipsCount--;
statusText.setText('Place remaining ships (' + (shipLengths.length - placedShipsCount) + ' left)');
startButton.visible = false;
// Position ship at current grid location for smooth dragging
var currentGridX = -1,
currentGridY = -1;
for (var gx = 0; gx < GRID_SIZE; gx++) {
for (var gy = 0; gy < GRID_SIZE; gy++) {
if (playerGrid[gx][gy].shipId === self.shipId) {
currentGridX = gx;
currentGridY = gy;
break;
}
}
if (currentGridX !== -1) {
break;
}
}
if (currentGridX !== -1) {
self.x = CENTER_GRID_X + currentGridX * CELL_SIZE;
self.y = CENTER_GRID_Y + currentGridY * CELL_SIZE;
}
}
self.isBeingDragged = true;
draggedShip = self;
// Scale up to full size when dragging
tween(self, {
alpha: 0.7,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
};
self.up = function (x, y, obj) {
if (self.isBeingDragged && !self.isPlaced) {
self.isBeingDragged = false;
// Try to place ship on grid with better snapping
var gridX = Math.round((self.x - CENTER_GRID_X) / CELL_SIZE);
var gridY = Math.round((self.y - CENTER_GRID_Y) / CELL_SIZE);
// Snap to grid position
var snapX = CENTER_GRID_X + gridX * CELL_SIZE;
var snapY = CENTER_GRID_Y + gridY * CELL_SIZE;
if (self.placeOnGrid(gridX, gridY)) {
// Snap ship to exact grid position before hiding
self.x = snapX;
self.y = snapY;
tween(self, {
alpha: 1
}, {
duration: 200
});
placedShipsCount++;
if (placedShipsCount >= shipLengths.length) {
statusText.setText('All ships placed! Press START GAME to begin');
startButton.visible = true;
} else {
statusText.setText('Place remaining ships (' + (shipLengths.length - placedShipsCount) + ' left)');
}
} else {
// Return to original position and scale down if can't place
tween(self, {
x: self.originalX,
y: self.originalY,
alpha: 1,
scaleX: self.smallScale,
scaleY: self.smallScale
}, {
duration: 300,
easing: tween.easeOut
});
}
}
};
return self;
});
var GridCell = Container.expand(function (x, y, isPlayer) {
var self = Container.call(this);
self.gridX = x;
self.gridY = y;
self.isPlayer = isPlayer;
self.hasShip = false;
self.isHit = false;
self.isMiss = false;
self.shipId = -1;
var cellGraphics = self.attachAsset('gridBox', {
anchorX: 0,
anchorY: 0
});
self.setShip = function (shipId) {
self.hasShip = true;
self.shipId = shipId;
if (self.isPlayer) {
cellGraphics.tint = 0x808080;
if (self.shipAsset) {
self.shipAsset.alpha = 1;
}
}
};
self.markHit = function () {
self.isHit = true;
cellGraphics.tint = 0xFF0000;
// Add hit marker asset on top of the cell
if (!self.hitMarkerAsset) {
self.hitMarkerAsset = self.attachAsset('hitMarker', {
anchorX: 0,
anchorY: 0
});
// Position hit marker at the correct cell position
self.hitMarkerAsset.x = 0;
self.hitMarkerAsset.y = 0;
}
};
self.markMiss = function (isEnemyMiss) {
self.isMiss = true;
if (isEnemyMiss) {
// Enemy missed on player's grid - use orange color
cellGraphics.tint = 0xffaa00;
} else {
// Player missed on enemy's grid - use green color
cellGraphics.tint = 0x00FF00;
}
};
self.markSunk = function () {
cellGraphics.tint = 0x2F2F2F;
};
self.down = function (x, y, obj) {
if (gameState === 'playing' && !self.isPlayer && !self.isHit && !self.isMiss) {
playerShoot(self.gridX, self.gridY);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var Ship = function Ship(length, id) {
this.length = length;
this.id = id;
this.hits = 0;
this.cells = [];
this.isSunk = false;
this.addCell = function (x, y) {
this.cells.push({
x: x,
y: y
});
};
this.hit = function () {
this.hits++;
if (this.hits >= this.length) {
this.isSunk = true;
return true;
}
return false;
};
};
var GRID_SIZE = 10;
var CELL_SIZE = 150; // Increased to 150 for larger game area
var GRID_OFFSET_X = 200;
var PLAYER_GRID_Y = 1200;
var AI_GRID_Y = 200;
// Calculate center positions for single grid display
var CENTER_GRID_X = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var CENTER_GRID_Y = (2732 - GRID_SIZE * CELL_SIZE) / 2 - 100; // Move grid up slightly
var GRID_SPACING = 1000; // Distance between the two grids
var playerGrid = [];
var aiGrid = [];
var playerShips = [];
var aiShips = [];
var gameState = 'intro'; // 'intro', 'placement', 'playing', 'gameOver'
var introScreen = null;
var currentPlayer = 'player'; // 'player', 'ai'
var selectedX = -1;
var selectedY = -1;
var currentShipToPlace = 0;
var isHorizontalPlacement = true;
var shipLengths = [5, 4, 3, 3, 2];
var draggableShips = [];
var placedShipsCount = 0;
var draggedShip = null;
// AI hunting variables
var aiLastHitX = -1;
var aiLastHitY = -1;
var aiHuntingDirections = [];
var aiCurrentDirection = 0;
var aiHuntingMode = false;
// Timer and effect variables
var turnTimer = null;
var turnTimeRemaining = 30;
var isWaitingAfterShot = false;
// Timer text
var timerText = new Text2('30', {
size: 50,
fill: 0xFF0000
});
timerText.anchor.set(1, 0);
LK.gui.topRight.addChild(timerText);
var playerGridContainer = new Container();
var aiGridContainer = new Container();
// UI Elements
var statusText = new Text2('Drag ships to your grid', {
size: 60,
fill: 0x000000
});
statusText.anchor.set(0.5, 0);
LK.gui.top.addChild(statusText);
var instructionText = new Text2('Double tap ships to rotate, drag to reposition', {
size: 40,
fill: 0x00FF00
});
instructionText.anchor.set(0.5, 0);
instructionText.y = 200;
instructionText.x = 0; // Starting position for scrolling
// Add to game instead of GUI so it appears in front of monitor
game.addChild(instructionText);
// Position in front of monitor
instructionText.x = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 1000;
instructionText.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 1700;
// Create scrolling text effect
function scrollInstructionText() {
if (gameState === 'placement') {
var startX = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 1200;
var endX = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE - 800;
var scrollDuration = 8000; // Duration to reach end position
instructionText.x = startX;
instructionText.alpha = 1;
tween.stop(instructionText); // Stop all existing tweens on instruction text
// First tween: scroll to end position
tween(instructionText, {
x: endX
}, {
duration: scrollDuration,
easing: tween.linear,
onFinish: function onFinish() {
if (gameState === 'placement') {
// Second tween: fade out at end position
tween(instructionText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (gameState === 'placement') {
// Restart immediately from beginning for seamless loop
scrollInstructionText();
}
}
});
}
}
});
}
}
// Start scrolling after placement begins
LK.setTimeout(function () {
if (gameState === 'placement') {
scrollInstructionText();
}
}, 500);
var startButton = new Text2('START GAME', {
size: 60,
fill: 0x00FF00
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 2048 / 2;
startButton.y = 300; // Move button lower
startButton.visible = false;
game.addChild(startButton);
startButton.down = function (x, y, obj) {
if (gameState === 'placement' && placedShipsCount >= shipLengths.length) {
gameState = 'playing';
statusText.setText('Fire at enemy positions!');
instructionText.setText('Tap enemy grid to shoot');
startButton.visible = false;
placeAIShips();
// Switch to AI grid for player's turn to attack with transition effect
tween(playerGridContainer, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
playerGridContainer.visible = false;
aiGridContainer.visible = true;
myCommanderContainer.visible = false;
enemyCommanderContainer.visible = true;
aiGridContainer.alpha = 0;
tween(aiGridContainer, {
alpha: 1
}, {
duration: 300
});
}
});
}
};
// Initialize grids
function initializeGrids() {
// Add containers to game - computer monitor first for background layering
game.addChild(playerGridContainer);
game.addChild(aiGridContainer);
// Move computer monitor to back if it exists
if (computerMonitorContainer) {
game.removeChild(computerMonitorContainer);
game.addChildAt(computerMonitorContainer, 0);
}
// Player grid (center during placement, left side during game)
for (var x = 0; x < GRID_SIZE; x++) {
playerGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
var cell = new GridCell(x, y, true);
cell.x = CENTER_GRID_X + x * CELL_SIZE;
cell.y = CENTER_GRID_Y + y * CELL_SIZE;
playerGrid[x][y] = cell;
playerGridContainer.addChild(cell);
}
}
// AI grid (center during game)
for (var x = 0; x < GRID_SIZE; x++) {
aiGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
var cell = new GridCell(x, y, false);
cell.x = CENTER_GRID_X + x * CELL_SIZE;
cell.y = CENTER_GRID_Y + y * CELL_SIZE;
aiGrid[x][y] = cell;
aiGridContainer.addChild(cell);
}
}
// Initialize grid visibility - show player grid during placement phase
playerGridContainer.visible = true;
aiGridContainer.visible = false;
// Add grid labels
var playerLabel = new Text2('Your Fleet', {
size: 40,
fill: 0x000000
});
playerLabel.anchor.set(0.5, 0.5);
playerLabel.x = CENTER_GRID_X + GRID_SIZE * CELL_SIZE / 2;
playerLabel.y = CENTER_GRID_Y - 50;
playerGridContainer.addChild(playerLabel);
var aiLabel = new Text2('Enemy Waters', {
size: 40,
fill: 0x000000
});
aiLabel.anchor.set(0.5, 0.5);
aiLabel.x = CENTER_GRID_X + GRID_SIZE * CELL_SIZE / 2;
aiLabel.y = CENTER_GRID_Y - 50;
aiGridContainer.addChild(aiLabel);
// Add coordinate labels for player grid
for (var i = 0; i < GRID_SIZE; i++) {
// Letters A-J on the left side
var letterLabel = new Text2(String.fromCharCode(65 + i), {
size: 20,
fill: 0x000000
});
letterLabel.anchor.set(0.5, 0.5);
letterLabel.x = CENTER_GRID_X - 30;
letterLabel.y = CENTER_GRID_Y + i * CELL_SIZE + CELL_SIZE / 2;
playerGridContainer.addChild(letterLabel);
// Numbers 1-10 on the top
var numberLabel = new Text2((i + 1).toString(), {
size: 20,
fill: 0x000000
});
numberLabel.anchor.set(0.5, 0.5);
numberLabel.x = CENTER_GRID_X + i * CELL_SIZE + CELL_SIZE / 2;
numberLabel.y = CENTER_GRID_Y - 60;
playerGridContainer.addChild(numberLabel);
}
// Add coordinate labels for AI grid
for (var i = 0; i < GRID_SIZE; i++) {
// Letters A-J on the left side
var letterLabel = new Text2(String.fromCharCode(65 + i), {
size: 20,
fill: 0x000000
});
letterLabel.anchor.set(0.5, 0.5);
letterLabel.x = CENTER_GRID_X - 30;
letterLabel.y = CENTER_GRID_Y + i * CELL_SIZE + CELL_SIZE / 2;
aiGridContainer.addChild(letterLabel);
// Numbers 1-10 on the top
var numberLabel = new Text2((i + 1).toString(), {
size: 20,
fill: 0x000000
});
numberLabel.anchor.set(0.5, 0.5);
numberLabel.x = CENTER_GRID_X + i * CELL_SIZE + CELL_SIZE / 2;
numberLabel.y = CENTER_GRID_Y - 60;
aiGridContainer.addChild(numberLabel);
}
// Create ship visual assets for player grid
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
var shipAsset = LK.getAsset('ship', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
shipAsset.x = CENTER_GRID_X + x * CELL_SIZE;
shipAsset.y = CENTER_GRID_Y + y * CELL_SIZE;
playerGrid[x][y].shipAsset = shipAsset;
playerGridContainer.addChild(shipAsset);
}
}
// Create draggable ships in a grid layout at bottom of screen, avoiding commander area
var shipStartY = 2400; // Move ships lower to avoid commander assets
var shipStartX = 600; // Start further right to avoid commander assets
var shipsPerRow = 3; // Maximum ships per row
var shipSpacingX = 400; // Horizontal spacing between ships
var shipSpacingY = 250; // Vertical spacing between rows
for (var i = 0; i < shipLengths.length; i++) {
var ship = new DraggableShip(shipLengths[i], i);
// Calculate grid position for ship
var row = Math.floor(i / shipsPerRow);
var col = i % shipsPerRow;
ship.x = shipStartX + col * shipSpacingX;
ship.y = shipStartY + row * shipSpacingY;
ship.originalX = ship.x;
ship.originalY = ship.y;
draggableShips.push(ship);
game.addChild(ship);
}
}
function selectCellForPlacement(x, y) {
selectedX = x;
selectedY = y;
if (canPlaceShip(x, y, shipLengths[currentShipToPlace], isHorizontalPlacement, true)) {
placeShip(x, y, shipLengths[currentShipToPlace], isHorizontalPlacement, true, currentShipToPlace);
currentShipToPlace++;
if (currentShipToPlace >= shipLengths.length) {
gameState = 'playing';
statusText.setText('Fire at enemy positions!');
instructionText.setText('Tap enemy grid to shoot');
placeAIShips();
} else {
statusText.setText('Place ship ' + (currentShipToPlace + 1) + ' (length: ' + shipLengths[currentShipToPlace] + ')');
}
}
}
function canPlaceShip(startX, startY, length, horizontal, isPlayer) {
var grid = isPlayer ? playerGrid : aiGrid;
for (var i = 0; i < length; i++) {
var x = horizontal ? startX + i : startX;
var y = horizontal ? startY : startY + i;
if (x >= GRID_SIZE || y >= GRID_SIZE || x < 0 || y < 0) {
return false;
}
if (grid[x][y].hasShip) {
return false;
}
}
return true;
}
function placeShip(startX, startY, length, horizontal, isPlayer, shipId) {
var grid = isPlayer ? playerGrid : aiGrid;
var ships = isPlayer ? playerShips : aiShips;
var ship = new Ship(length, shipId);
for (var i = 0; i < length; i++) {
var x = horizontal ? startX + i : startX;
var y = horizontal ? startY : startY + i;
grid[x][y].setShip(shipId);
ship.addCell(x, y);
}
ships.push(ship);
}
function placeAIShips() {
for (var i = 0; i < shipLengths.length; i++) {
var placed = false;
var attempts = 0;
while (!placed && attempts < 100) {
var x = Math.floor(Math.random() * GRID_SIZE);
var y = Math.floor(Math.random() * GRID_SIZE);
var horizontal = Math.random() < 0.5;
if (canPlaceShip(x, y, shipLengths[i], horizontal, false)) {
placeShip(x, y, shipLengths[i], horizontal, false, i);
placed = true;
}
attempts++;
}
}
}
function playerShoot(x, y) {
if (currentPlayer !== 'player' || isWaitingAfterShot) {
return;
}
// Stop the timer
if (turnTimer) {
LK.clearInterval(turnTimer);
turnTimer = null;
}
// Play shooting sound when firing
LK.getSound('shoot').play();
var cell = aiGrid[x][y];
var hit = cell.hasShip;
// Create visual effect
var effectAsset = hit ? 'hit' : 'miss';
var effect = LK.getAsset(effectAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
effect.x = CENTER_GRID_X + x * CELL_SIZE + CELL_SIZE / 2;
effect.y = CENTER_GRID_Y + y * CELL_SIZE + CELL_SIZE / 2;
aiGridContainer.addChild(effect);
if (cell.hasShip) {
cell.markHit();
hit = true;
LK.getSound('explosion').play();
// Explosion effect for hit
tween(effect, {
scaleX: 2,
scaleY: 2,
alpha: 0.8
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(effect, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
effect.destroy();
}
});
}
});
var ship = null;
// Find the ship by ID in the aiShips array
for (var i = 0; i < aiShips.length; i++) {
if (aiShips[i].id === cell.shipId) {
ship = aiShips[i];
break;
}
}
if (ship) {
var sunk = ship.hit();
if (sunk) {
LK.getSound('sunk').play();
markShipAsSunk(ship, false);
// Enemy commander reacts to ship being sunk
showCommanderSpeech(getRandomSpeech(myCommanderSpeech.sunk), false);
if (checkVictory(false)) {
gameState = 'gameOver';
statusText.setText('Victory! You sank all enemy ships!');
instructionText.setText('');
LK.showYouWin();
return;
}
} else {
// Enemy commander reacts to being hit
showCommanderSpeech(getRandomSpeech(myCommanderSpeech.hit), false);
}
}
statusText.setText('Hit! Fire again!');
// No delay for hits - player can continue shooting immediately
startPlayerTurn();
} else {
cell.markMiss(false);
LK.getSound('waterSplash').play();
// Enemy commander reacts to player missing
showCommanderSpeech(getRandomSpeech(myCommanderSpeech.miss), false);
// Water splash effect for miss
effect.tint = 0x00FF00; // Green color to match miss cell tint
tween(effect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.6
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(effect, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
effect.destroy();
}
});
}
});
currentPlayer = 'ai';
statusText.setText('Miss! Enemy is thinking...');
// 5-second delay before AI turn
isWaitingAfterShot = true;
LK.setTimeout(function () {
isWaitingAfterShot = false;
// Switch to player grid when AI attacks with transition effect
tween(aiGridContainer, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
aiGridContainer.visible = false;
playerGridContainer.visible = true;
enemyCommanderContainer.visible = false;
myCommanderContainer.visible = true;
playerGridContainer.alpha = 0;
tween(playerGridContainer, {
alpha: 1
}, {
duration: 300
});
}
});
startAiTurn();
}, 5000);
}
}
function aiTurn() {
if (currentPlayer !== 'ai' || isWaitingAfterShot) {
return;
}
// Stop the timer
if (turnTimer) {
LK.clearInterval(turnTimer);
turnTimer = null;
}
var x, y;
var attempts = 0;
// Smart AI: If in hunting mode, try adjacent cells to last hit
if (aiHuntingMode && aiLastHitX !== -1 && aiLastHitY !== -1) {
var found = false;
// Try directions: up, right, down, left
var directions = [{
x: 0,
y: -1
},
// up
{
x: 1,
y: 0
},
// right
{
x: 0,
y: 1
},
// down
{
x: -1,
y: 0
} // left
];
// Try each direction from the last hit
for (var d = 0; d < directions.length && !found; d++) {
var testX = aiLastHitX + directions[d].x;
var testY = aiLastHitY + directions[d].y;
// Check if position is valid and not already attacked
if (testX >= 0 && testX < GRID_SIZE && testY >= 0 && testY < GRID_SIZE && !playerGrid[testX][testY].isHit && !playerGrid[testX][testY].isMiss) {
x = testX;
y = testY;
found = true;
}
}
// If no adjacent cells available, exit hunting mode
if (!found) {
aiHuntingMode = false;
aiLastHitX = -1;
aiLastHitY = -1;
}
}
// If not in hunting mode or no valid adjacent cells, shoot randomly
if (!aiHuntingMode || aiLastHitX === -1 && aiLastHitY === -1) {
do {
x = Math.floor(Math.random() * GRID_SIZE);
y = Math.floor(Math.random() * GRID_SIZE);
attempts++;
} while ((playerGrid[x][y].isHit || playerGrid[x][y].isMiss) && attempts < 100);
}
// Play shooting sound when AI fires
LK.getSound('shoot').play();
var cell = playerGrid[x][y];
var hit = cell.hasShip;
// Create visual effect
var effectAsset = hit ? 'hit' : 'miss';
var effect = LK.getAsset(effectAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
effect.x = CENTER_GRID_X + x * CELL_SIZE + CELL_SIZE / 2;
effect.y = CENTER_GRID_Y + y * CELL_SIZE + CELL_SIZE / 2;
playerGridContainer.addChild(effect);
if (cell.hasShip) {
cell.markHit();
LK.getSound('explosion').play();
// Explosion effect for hit
tween(effect, {
scaleX: 2,
scaleY: 2,
alpha: 0.8
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(effect, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
effect.destroy();
}
});
}
});
// Enter hunting mode and remember this hit position
aiHuntingMode = true;
aiLastHitX = x;
aiLastHitY = y;
var ship = null;
// Find the ship by ID in the playerShips array
for (var i = 0; i < playerShips.length; i++) {
if (playerShips[i].id === cell.shipId) {
ship = playerShips[i];
break;
}
}
if (ship) {
var sunk = ship.hit();
if (sunk) {
LK.getSound('sunk').play();
markShipAsSunk(ship, true);
// Exit hunting mode when ship is sunk
aiHuntingMode = false;
aiLastHitX = -1;
aiLastHitY = -1;
// Enemy commander celebrates sinking player's ship
showCommanderSpeech(getRandomSpeech(enemyCommanderSpeech.sunk), true);
if (checkVictory(true)) {
gameState = 'gameOver';
statusText.setText('Defeat! Enemy sank all your ships!');
instructionText.setText('');
LK.showGameOver();
return;
}
} else {
// Enemy commander celebrates hitting player's ship
showCommanderSpeech(getRandomSpeech(enemyCommanderSpeech.hit), true);
}
}
statusText.setText('Enemy hit your ship!');
// 5-second delay before AI continues
isWaitingAfterShot = true;
LK.setTimeout(function () {
isWaitingAfterShot = false;
startAiTurn();
}, 5000);
} else {
cell.markMiss(true);
LK.getSound('waterSplash').play();
// Enemy commander reacts to missing
showCommanderSpeech(getRandomSpeech(enemyCommanderSpeech.miss), true);
// Water splash effect for miss
effect.tint = 0xFFAA00;
tween(effect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.6
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(effect, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
effect.destroy();
}
});
}
});
currentPlayer = 'player';
statusText.setText('Enemy missed! Your turn!');
// 5-second delay before player turn
isWaitingAfterShot = true;
LK.setTimeout(function () {
isWaitingAfterShot = false;
// Switch to AI grid for player's turn to attack with transition effect
tween(playerGridContainer, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
playerGridContainer.visible = false;
aiGridContainer.visible = true;
myCommanderContainer.visible = false;
enemyCommanderContainer.visible = true;
aiGridContainer.alpha = 0;
tween(aiGridContainer, {
alpha: 1
}, {
duration: 300
});
}
});
startPlayerTurn();
}, 5000);
}
}
function markShipAsSunk(ship, isPlayer) {
var grid = isPlayer ? playerGrid : aiGrid;
for (var i = 0; i < ship.cells.length; i++) {
var cellPos = ship.cells[i];
grid[cellPos.x][cellPos.y].markSunk();
// Darken hit markers on sunk ship cells
if (grid[cellPos.x][cellPos.y].hitMarkerAsset) {
tween(grid[cellPos.x][cellPos.y].hitMarkerAsset, {
alpha: 0.4
}, {
duration: 500,
easing: tween.easeOut
});
}
}
}
function removeShipFromGrid(ship) {
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (playerGrid[x][y].shipId === ship.shipId) {
playerGrid[x][y].hasShip = false;
playerGrid[x][y].shipId = -1;
var cellGraphics = playerGrid[x][y].children[0];
cellGraphics.tint = 0xFFFFFF;
if (playerGrid[x][y].shipAsset) {
playerGrid[x][y].shipAsset.alpha = 0;
}
}
}
}
}
function checkVictory(playerLost) {
var ships = playerLost ? playerShips : aiShips;
for (var i = 0; i < ships.length; i++) {
if (!ships[i].isSunk) {
return false;
}
}
return true;
}
// Double tap to rotate ship during placement
var lastTapTime = 0;
game.down = function (x, y, obj) {
if (gameState === 'placement') {
var currentTime = Date.now();
if (currentTime - lastTapTime < 300) {
// Find ship being rotated - check both unplaced and placed ships
for (var i = 0; i < draggableShips.length; i++) {
var ship = draggableShips[i];
var shipBounds = {
x: ship.x,
y: ship.y,
width: ship.isHorizontal ? ship.length * CELL_SIZE : CELL_SIZE,
height: ship.isHorizontal ? CELL_SIZE : ship.length * CELL_SIZE
};
if (x >= shipBounds.x && x <= shipBounds.x + shipBounds.width && y >= shipBounds.y && y <= shipBounds.y + shipBounds.height) {
if (ship.isPlaced) {
// Remove ship from grid
removeShipFromGrid(ship);
ship.isPlaced = false;
ship.visible = true;
placedShipsCount--;
}
ship.rotate();
break;
}
}
instructionText.setText('Double tap ships to rotate, drag to reposition');
}
lastTapTime = currentTime;
}
};
game.move = function (x, y, obj) {
if (gameState === 'placement' && draggedShip) {
draggedShip.x = x - draggedShip.width / 2;
draggedShip.y = y - draggedShip.height / 2;
}
};
game.up = function (x, y, obj) {
draggedShip = null;
};
function startPlayerTurn() {
currentPlayer = 'player';
turnTimeRemaining = 30;
timerText.setText(turnTimeRemaining.toString());
timerText.visible = true;
// Clear any existing timer first
if (turnTimer) {
LK.clearInterval(turnTimer);
turnTimer = null;
}
turnTimer = LK.setInterval(function () {
turnTimeRemaining--;
timerText.setText(turnTimeRemaining.toString());
if (turnTimeRemaining <= 0) {
LK.clearInterval(turnTimer);
turnTimer = null;
timerText.visible = false;
// Auto-switch to AI turn on timeout
currentPlayer = 'ai';
statusText.setText('Time up! Enemy is thinking...');
tween(aiGridContainer, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
aiGridContainer.visible = false;
playerGridContainer.visible = true;
enemyCommanderContainer.visible = false;
myCommanderContainer.visible = true;
playerGridContainer.alpha = 0;
tween(playerGridContainer, {
alpha: 1
}, {
duration: 300
});
}
});
startAiTurn();
}
}, 1000);
}
function startAiTurn() {
currentPlayer = 'ai';
turnTimeRemaining = 30;
timerText.setText(turnTimeRemaining.toString());
timerText.visible = true;
// Clear any existing timer first
if (turnTimer) {
LK.clearInterval(turnTimer);
turnTimer = null;
}
// AI thinks for 2-5 seconds before shooting
var thinkTime = 2000 + Math.random() * 3000;
turnTimer = LK.setInterval(function () {
turnTimeRemaining--;
timerText.setText(turnTimeRemaining.toString());
if (turnTimeRemaining <= 0) {
LK.clearInterval(turnTimer);
turnTimer = null;
timerText.visible = false;
aiTurn();
}
}, 1000);
LK.setTimeout(function () {
if (currentPlayer === 'ai' && !isWaitingAfterShot) {
aiTurn();
}
}, thinkTime);
}
// Create commander assets
var myCommanderContainer = new Container();
var enemyCommanderContainer = new Container();
// Position commanders higher and at bottom left corner
myCommanderContainer.x = 50;
myCommanderContainer.y = 2732 - 500; // Moved even higher up
enemyCommanderContainer.x = 50;
enemyCommanderContainer.y = 2732 - 500; // Same position as my commander
// Create shadow assets first (positioned slightly offset)
var myCommanderShadow = myCommanderContainer.attachAsset('myCommander', {
anchorX: 0,
anchorY: 0,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.3,
tint: 0x000000
});
myCommanderShadow.x = 8;
myCommanderShadow.y = 8;
var enemyCommanderShadow = enemyCommanderContainer.attachAsset('enemyCommander', {
anchorX: 0,
anchorY: 0,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.3,
tint: 0x000000
});
enemyCommanderShadow.x = 8;
enemyCommanderShadow.y = 8;
// Create main commander assets (larger size)
var myCommanderAsset = myCommanderContainer.attachAsset('myCommander', {
anchorX: 0,
anchorY: 0,
scaleX: 2.2,
scaleY: 2.2
});
var enemyCommanderAsset = enemyCommanderContainer.attachAsset('enemyCommander', {
anchorX: 0,
anchorY: 0,
scaleX: 2.2,
scaleY: 2.2
});
// Add commanders to game
game.addChild(myCommanderContainer);
game.addChild(enemyCommanderContainer);
// Initialize commander visibility - show my commander during placement phase
myCommanderContainer.visible = true;
enemyCommanderContainer.visible = false;
// Commander speech arrays
var myCommanderSpeech = {
hit: ["Lanet olsun! Nasıl buldun?", "Bu olamaz! Şanslı atış!", "Beni buldun ama savaş henüz bitmedi!", "İyi atış... ama son olmayacak!"],
miss: ["Hah! Ordumu bulmak kolay değil!", "Boşa atış! Denemeye devam et!", "Su sıçradı ama gemim yerinde!", "Yaklaştın ama yeterli değil!"],
sunk: ["Gemimi batırdın... ama intikam alacağım!", "Bu savaşı kaybettim ama onur hala benimle!", "İyi savaştın... şimdilik kazandın"]
};
var enemyCommanderSpeech = {
hit: ["Mükemmel atış! Hedefi bulduk!", "Doğrudan isabet! Böyle devam!", "Ateş! Düşman gemisini yakaladık!", "Harika! Bu şekilde kazanacağız!"],
miss: ["Lanet! Hedefi kaçırdık!", "Su sıçradı! Tekrar dene!", "Bu sefer olmadı ama pes etmem!", "Yakında hedefimi bulacağım!"],
sunk: ["Bir gemi daha battı! Zafer yakın!", "Düşman filosu eriyor!", "Mükemmel! Böyle devam edersek kazanırız!"]
};
// Current speech display
var currentSpeechText = null;
var speechTimeout = null;
function showCommanderSpeech(text, isEnemyCommander) {
// Clear existing speech
if (currentSpeechText) {
currentSpeechText.destroy();
currentSpeechText = null;
}
if (speechTimeout) {
LK.clearTimeout(speechTimeout);
speechTimeout = null;
}
// Create speech bubble container
currentSpeechText = new Container();
// Create speech bubble background
var speechBubble = currentSpeechText.attachAsset('speechBubble', {
anchorX: 0,
anchorY: 0
});
// Create speech bubble text
var speechText = new Text2(text, {
size: 36,
fill: 0x000000
});
speechText.anchor.set(0.5, 0.5);
speechText.x = 350; // Move text further to the right in bubble
speechText.y = 75; // Center in bubble
currentSpeechText.addChild(speechText);
// Add speech bubble directly to game instead of commander container
game.addChild(currentSpeechText);
// Position speech bubble next to appropriate commander
if (isEnemyCommander && enemyCommanderContainer.visible) {
currentSpeechText.x = enemyCommanderContainer.x + 440; // Position to the right of commander
currentSpeechText.y = enemyCommanderContainer.y + 50; // Align with top of commander
} else if (!isEnemyCommander && myCommanderContainer.visible) {
currentSpeechText.x = myCommanderContainer.x + 440; // Position to the right of commander
currentSpeechText.y = myCommanderContainer.y + 50; // Align with top of commander
} else {
// Fallback positioning if no commander is visible
currentSpeechText.x = 50 + 440;
currentSpeechText.y = 2732 - 500 + 50;
}
// Animate speech appearance
currentSpeechText.alpha = 0;
tween(currentSpeechText, {
alpha: 1
}, {
duration: 300
});
// Remove speech after 3 seconds
speechTimeout = LK.setTimeout(function () {
if (currentSpeechText) {
tween(currentSpeechText, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
if (currentSpeechText) {
currentSpeechText.destroy();
currentSpeechText = null;
}
}
});
}
}, 3000);
}
function getRandomSpeech(speechArray) {
return speechArray[Math.floor(Math.random() * speechArray.length)];
}
// Initialize the game
function createIntroScreen() {
introScreen = new Container();
game.addChild(introScreen);
// Create television frame
var tvFrame = LK.getAsset('tvFrame', {
anchorX: 0.5,
anchorY: 0.5
});
tvFrame.x = 2048 / 2;
tvFrame.y = 2732 / 2;
introScreen.addChild(tvFrame);
// Create TV stand
var tvStand = LK.getAsset('tvStand', {
anchorX: 0.5,
anchorY: 0
});
tvStand.x = 2048 / 2;
tvStand.y = 2732 / 2 + 1450;
introScreen.addChild(tvStand);
// Create TV buttons and speakers
var powerButton = LK.getAsset('tvButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
powerButton.x = 2048 / 2 + 1200;
powerButton.y = 2732 / 2 - 1200;
introScreen.addChild(powerButton);
var volumeButton = LK.getAsset('tvButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x4444FF
});
volumeButton.x = 2048 / 2 + 1200;
volumeButton.y = 2732 / 2 - 1100;
introScreen.addChild(volumeButton);
// Create speakers
for (var s = 0; s < 6; s++) {
var speaker = LK.getAsset('tvSpeaker', {
anchorX: 0.5,
anchorY: 0.5
});
speaker.x = 2048 / 2 + 1200;
speaker.y = 2732 / 2 - 800 + s * 60;
introScreen.addChild(speaker);
}
// Create TV screen (black background)
var tvScreen = LK.getAsset('tvScreen', {
anchorX: 0.5,
anchorY: 0.5
});
tvScreen.x = 2048 / 2;
tvScreen.y = 2732 / 2;
introScreen.addChild(tvScreen);
// Create container for all TV content
var tvContent = new Container();
tvContent.x = 2048 / 2 - 950; // Center content in TV screen
tvContent.y = 2732 / 2 - 1300;
introScreen.addChild(tvContent);
// Create gradient background layers for depth (now inside TV)
var bgLayer1 = LK.getAsset('gridBox', {
anchorX: 0,
anchorY: 0,
scaleX: 16,
scaleY: 21,
tint: 0x001A33,
alpha: 1
});
tvContent.addChild(bgLayer1);
var bgLayer2 = LK.getAsset('gridBox', {
anchorX: 0,
anchorY: 0,
scaleX: 15,
scaleY: 19,
tint: 0x003D66,
alpha: 0.7
});
bgLayer2.x = 80;
bgLayer2.y = 80;
tvContent.addChild(bgLayer2);
// Create multiple water layers for depth
var waterLayer1 = LK.getAsset('water', {
anchorX: 0,
anchorY: 0,
scaleX: 24,
scaleY: 28,
alpha: 0.08,
tint: 0x1B4F72
});
tvContent.addChild(waterLayer1);
var waterLayer2 = LK.getAsset('water', {
anchorX: 0,
anchorY: 0,
scaleX: 20,
scaleY: 24,
alpha: 0.15,
tint: 0x2E86AB
});
tvContent.addChild(waterLayer2);
var waterLayer3 = LK.getAsset('water', {
anchorX: 0,
anchorY: 0,
scaleX: 16,
scaleY: 20,
alpha: 0.12,
tint: 0x5DADE2
});
tvContent.addChild(waterLayer3);
// Create floating water particles
var waterParticles = [];
for (var p = 0; p < 8; p++) {
var particle = LK.getAsset('water', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + Math.random() * 0.2,
scaleY: 0.3 + Math.random() * 0.2,
alpha: 0.2 + Math.random() * 0.1,
tint: 0x87CEEB
});
particle.x = Math.random() * 1900;
particle.y = Math.random() * 2600;
waterParticles.push(particle);
tvContent.addChild(particle);
}
// Animate water layers with wave motion
tween(waterLayer1, {
alpha: 0.12,
x: -50
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waterLayer1, {
alpha: 0.08,
x: 0
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waterLayer1, {
alpha: 0.12,
x: -50
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: arguments.callee
});
}
});
}
});
tween(waterLayer2, {
alpha: 0.22,
x: 30
}, {
duration: 3500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waterLayer2, {
alpha: 0.15,
x: -30
}, {
duration: 3500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waterLayer2, {
alpha: 0.22,
x: 30
}, {
duration: 3500,
easing: tween.easeInOut,
onFinish: arguments.callee
});
}
});
}
});
tween(waterLayer3, {
alpha: 0.18,
y: -20
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waterLayer3, {
alpha: 0.12,
y: 20
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waterLayer3, {
alpha: 0.18,
y: -20
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: arguments.callee
});
}
});
}
});
// Animate water particles
for (var p = 0; p < waterParticles.length; p++) {
(function (particle, index) {
function floatParticle() {
if (gameState === 'intro') {
var targetX = Math.random() * 1900;
var targetY = Math.random() * 2600;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0.1 + Math.random() * 0.15,
rotation: Math.random() * Math.PI * 2
}, {
duration: 8000 + Math.random() * 4000,
easing: tween.easeInOut,
onFinish: floatParticle
});
}
}
LK.setTimeout(floatParticle, index * 500);
})(waterParticles[p], p);
}
// Main title with shadow effect
var titleShadow = new Text2('NAVAL COMBAT', {
size: 100,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 950 + 6;
titleShadow.y = 406;
titleShadow.alpha = 0;
tvContent.addChild(titleShadow);
var titleText = new Text2('NAVAL COMBAT', {
size: 100,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 950;
titleText.y = 400;
titleText.alpha = 0;
tvContent.addChild(titleText);
// Subtitle with glow effect
var subtitleGlow = new Text2('Strategic Fleet Warfare', {
size: 42,
fill: 0x87CEEB
});
subtitleGlow.anchor.set(0.5, 0.5);
subtitleGlow.x = 950;
subtitleGlow.y = 550;
subtitleGlow.alpha = 0;
tvContent.addChild(subtitleGlow);
var subtitleText = new Text2('Strategic Fleet Warfare', {
size: 42,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 950;
subtitleText.y = 550;
subtitleText.alpha = 0;
tvContent.addChild(subtitleText);
// Create naval battle themed decorative elements
var navyElements = [];
var shipColors = [0x2C3E50, 0x34495E, 0x566573, 0x7F8C8D, 0x95A5A6];
var enemyColors = [0x8B0000, 0xA52A2A, 0xB22222, 0xDC143C, 0xFF4500];
// Create allied fleet formation (left side)
for (var i = 0; i < 4; i++) {
var allyShip = LK.getAsset('ship', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0 + i * 0.2,
scaleY: 2.0 + i * 0.2,
tint: shipColors[i]
});
allyShip.x = 120 + i * 50;
allyShip.y = 750 + i * 70;
allyShip.alpha = 0;
allyShip.rotation = Math.PI / 8 + i * 0.05;
navyElements.push(allyShip);
tvContent.addChild(allyShip);
}
// Create enemy fleet formation (right side)
for (var i = 0; i < 4; i++) {
var enemyShip = LK.getAsset('ship', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0 + i * 0.2,
scaleY: 2.0 + i * 0.2,
tint: enemyColors[i]
});
enemyShip.x = 1780 - i * 50;
enemyShip.y = 750 + i * 70;
enemyShip.alpha = 0;
enemyShip.rotation = -Math.PI / 8 - i * 0.05;
navyElements.push(enemyShip);
tvContent.addChild(enemyShip);
}
// Add naval commander silhouettes
var allyCommander = LK.getAsset('myCommander', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0,
tint: 0x2C3E50
});
allyCommander.x = 250;
allyCommander.y = 1100;
navyElements.push(allyCommander);
tvContent.addChild(allyCommander);
var enemyCommander = LK.getAsset('enemyCommander', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0,
tint: 0x8B0000
});
enemyCommander.x = 1650;
enemyCommander.y = 1100;
navyElements.push(enemyCommander);
tvContent.addChild(enemyCommander);
// Add strategic grid overlay effects
for (var i = 0; i < 6; i++) {
var gridElement = LK.getAsset('gridBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
alpha: 0,
tint: 0x3498DB
});
gridElement.x = 950 + (i % 3 - 1) * 160;
gridElement.y = 900 + Math.floor(i / 3) * 120;
navyElements.push(gridElement);
tvContent.addChild(gridElement);
}
// Replace shipFormation reference with navyElements
var shipFormation = navyElements;
// Enhanced start button with frame
var buttonFrame = LK.getAsset('gridBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.2,
scaleY: 1.0,
tint: 0xFFD700,
alpha: 0
});
buttonFrame.x = 950;
buttonFrame.y = 1300;
tvContent.addChild(buttonFrame);
var startGameButton = new Text2('⚓ START BATTLE ⚓', {
size: 58,
fill: 0x000000
});
startGameButton.anchor.set(0.5, 0.5);
startGameButton.x = 950;
startGameButton.y = 1300;
startGameButton.alpha = 0;
tvContent.addChild(startGameButton);
// Additional UI elements
var versionText = new Text2('Admiral Edition v1.0', {
size: 25,
fill: 0x808080
});
versionText.anchor.set(0.5, 0.5);
versionText.x = 950;
versionText.y = 1450;
versionText.alpha = 0;
tvContent.addChild(versionText);
var instructionsText = new Text2('Deploy your fleet strategically and command the seas!', {
size: 36,
fill: 0xE0E0E0
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 950;
instructionsText.y = 1600;
instructionsText.alpha = 0;
tvContent.addChild(instructionsText);
// Professional animation sequence
// Title entrance with dramatic effect
tween(titleShadow, {
alpha: 0.6,
y: 500,
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 1200,
easing: tween.elasticOut
});
tween(titleText, {
alpha: 1,
y: 500,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1200,
easing: tween.elasticOut
});
// Subtitle with sequential glow effect
LK.setTimeout(function () {
tween(subtitleGlow, {
alpha: 0.4,
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 800,
easing: tween.easeOut
});
tween(subtitleText, {
alpha: 1
}, {
duration: 800,
easing: tween.easeOut
});
}, 400);
// Naval fleet formation entrance with battle positioning
LK.setTimeout(function () {
for (var i = 0; i < navyElements.length; i++) {
(function (element, delay) {
LK.setTimeout(function () {
// Different entrance effects based on element type
var isShip = element.x < 1024; // Left side elements are allied ships
var entranceEffect = isShip ? {
alpha: 0.85,
scaleX: element.scaleX * 1.2,
scaleY: element.scaleY * 1.2,
x: element.x + (isShip ? 50 : -50)
} : {
alpha: 0.7,
scaleX: element.scaleX * 1.1,
scaleY: element.scaleY * 1.1
};
tween(element, entranceEffect, {
duration: 1200,
easing: tween.elasticOut
});
// Add naval movement patterns
function navalMovement() {
if (gameState === 'intro') {
var movementPattern = isShip ? {
y: element.y + (10 + Math.random() * 8),
rotation: element.rotation + (Math.random() - 0.5) * 0.1,
alpha: element.alpha * (0.9 + Math.random() * 0.2)
} : {
scaleX: element.scaleX * (0.95 + Math.random() * 0.1),
scaleY: element.scaleY * (0.95 + Math.random() * 0.1),
alpha: element.alpha * (0.8 + Math.random() * 0.4)
};
tween(element, movementPattern, {
duration: 2500 + Math.random() * 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
var returnPattern = isShip ? {
y: element.y - (10 + Math.random() * 8),
rotation: element.rotation - (Math.random() - 0.5) * 0.1,
alpha: element.alpha / (0.9 + Math.random() * 0.2)
} : {
scaleX: element.scaleX / (0.95 + Math.random() * 0.1),
scaleY: element.scaleY / (0.95 + Math.random() * 0.1),
alpha: element.alpha / (0.8 + Math.random() * 0.4)
};
tween(element, returnPattern, {
duration: 2500 + Math.random() * 1500,
easing: tween.easeInOut,
onFinish: navalMovement
});
}
});
}
}
LK.setTimeout(navalMovement, 1500 + Math.random() * 1000);
}, delay * 120);
})(navyElements[i], i);
}
}, 800);
// Button entrance with frame
LK.setTimeout(function () {
tween(buttonFrame, {
alpha: 0.8,
scaleX: 4.2,
scaleY: 1.3
}, {
duration: 600,
easing: tween.easeOut
});
tween(startGameButton, {
alpha: 1,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 600,
easing: tween.bounceOut
});
tween(versionText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
tween(instructionsText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
}, 1400);
// Enhanced pulsing effect for start button
LK.setTimeout(function () {
function pulseButton() {
if (gameState === 'intro') {
tween(startGameButton, {
scaleX: 1.15,
scaleY: 1.15,
tint: 0x00FF00
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startGameButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: pulseButton
});
}
});
tween(buttonFrame, {
alpha: 1,
scaleX: 4.4,
scaleY: 1.4
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(buttonFrame, {
alpha: 0.8,
scaleX: 4.2,
scaleY: 1.3
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
}
}
pulseButton();
}, 2000);
// Add TV static overlay effect
var tvStatic = LK.getAsset('gridBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 16,
scaleY: 21,
alpha: 0.02,
tint: 0xFFFFFF
});
tvStatic.x = 950;
tvStatic.y = 1300;
tvContent.addChild(tvStatic);
// Animate TV static
function animateStatic() {
if (gameState === 'intro') {
tween(tvStatic, {
alpha: 0.05 + Math.random() * 0.03,
scaleX: 16 + Math.random() * 0.2,
scaleY: 21 + Math.random() * 0.2
}, {
duration: 100 + Math.random() * 200,
onFinish: animateStatic
});
}
}
animateStatic();
// Add TV screen glow effect
var screenGlow = LK.getAsset('tvScreen', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.05,
scaleY: 1.05,
tint: 0x4A90E2,
alpha: 0.1
});
screenGlow.x = 2048 / 2;
screenGlow.y = 2732 / 2;
introScreen.addChild(screenGlow);
// Animate screen glow
function animateGlow() {
if (gameState === 'intro') {
tween(screenGlow, {
alpha: 0.15,
scaleX: 1.08,
scaleY: 1.08
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(screenGlow, {
alpha: 0.1,
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateGlow
});
}
});
}
}
animateGlow();
// Click handler for start button with effect
startGameButton.down = function (x, y, obj) {
if (gameState === 'intro') {
// Button press effect
tween(startGameButton, {
scaleX: 0.95,
scaleY: 0.95,
alpha: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(startGameButton, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1
}, {
duration: 100
});
}
});
// Fade out intro screen with dramatic effect
tween(introScreen, {
alpha: 0,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
introScreen.destroy();
introScreen = null;
gameState = 'placement';
statusText.setText('Drag ships to your grid');
// Start ocean waves music after intro ends
LK.playMusic('oceanWaves');
// Show computer monitor and radar system
computerMonitorContainer.visible = true;
computerMonitorContainer.alpha = 0;
tween(computerMonitorContainer, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
initializeGrids();
// Start scrolling text immediately when placement begins
scrollInstructionText();
}
});
}
};
}
createIntroScreen();
// Create computer monitor frame around game area
var computerMonitorContainer = new Container();
computerMonitorContainer.visible = false; // Hide during intro
game.addChild(computerMonitorContainer);
// Position computer monitor behind the game grids
var monitorFrame = computerMonitorContainer.attachAsset('computerMonitor', {
anchorX: 0.5,
anchorY: 0.5
});
monitorFrame.x = 2048 / 2;
monitorFrame.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE / 2;
// Computer screen background
var computerScreen = computerMonitorContainer.attachAsset('computerScreen', {
anchorX: 0.5,
anchorY: 0.5
});
computerScreen.x = 2048 / 2;
computerScreen.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE / 2;
// Computer base
var computerBase = computerMonitorContainer.attachAsset('computerBase', {
anchorX: 0.5,
anchorY: 0
});
computerBase.x = 2048 / 2;
computerBase.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE + 50;
// Create rotating radar system in background
var radarContainer = new Container();
computerMonitorContainer.addChild(radarContainer);
radarContainer.x = 2048 / 2;
radarContainer.y = CENTER_GRID_Y + GRID_SIZE * CELL_SIZE / 2;
// Radar circles (concentric)
for (var i = 1; i <= 3; i++) {
var radarCircle = radarContainer.attachAsset('radarCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: i * 0.4,
scaleY: i * 0.4,
alpha: 0.15
});
radarCircle.x = 0;
radarCircle.y = 0;
}
// Radar center dot
var radarCenter = radarContainer.attachAsset('radarCenter', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
radarCenter.x = 0;
radarCenter.y = 0;
// Rotating radar sweep line
var radarSweep = radarContainer.attachAsset('radarSweep', {
anchorX: 0,
anchorY: 0.5,
alpha: 0.6
});
radarSweep.x = 0;
radarSweep.y = 0;
// Start radar sweep rotation
function rotateRadarSweep() {
if (gameState === 'playing' || gameState === 'placement') {
tween(radarSweep, {
rotation: radarSweep.rotation + Math.PI * 2
}, {
duration: 3000,
easing: tween.linear,
onFinish: rotateRadarSweep
});
}
}
// Start radar sweep after placement phase begins
LK.setTimeout(function () {
if (gameState === 'placement') {
rotateRadarSweep();
}
}, 1500);
// Start radar sweep after initialization
LK.setTimeout(function () {
rotateRadarSweep();
}, 500);
// Add computer-style grid overlay
var computerGrid = new Container();
computerMonitorContainer.addChild(computerGrid);
computerGrid.x = 2048 / 2 - 950;
computerGrid.y = CENTER_GRID_Y - 750;
// Create computer grid lines
for (var i = 0; i <= 20; i++) {
// Vertical lines
var vLine = LK.getAsset('gridLine', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.5,
scaleY: 1.2,
alpha: 0.08,
tint: 0x00FF00
});
vLine.x = i * 95;
vLine.y = 0;
computerGrid.addChild(vLine);
// Horizontal lines
var hLine = LK.getAsset('gridLine', {
anchorX: 0,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 0.5,
alpha: 0.08,
tint: 0x00FF00,
rotation: Math.PI / 2
});
hLine.x = 0;
hLine.y = i * 75;
computerGrid.addChild(hLine);
}
// Add ocean movement to game containers
function addOceanMovement() {
if (gameState === 'playing' || gameState === 'placement') {
// Gentle rocking motion for player grid
tween(playerGridContainer, {
x: playerGridContainer.x + (Math.random() - 0.5) * 8,
y: playerGridContainer.y + (Math.random() - 0.5) * 6,
rotation: playerGridContainer.rotation + (Math.random() - 0.5) * 0.008
}, {
duration: 2000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Return to original position with slight variation
tween(playerGridContainer, {
x: game.children.indexOf(playerGridContainer) >= 0 ? playerGridContainer.x - (Math.random() - 0.5) * 8 : playerGridContainer.x,
y: game.children.indexOf(playerGridContainer) >= 0 ? playerGridContainer.y - (Math.random() - 0.5) * 6 : playerGridContainer.y,
rotation: playerGridContainer.rotation - (Math.random() - 0.5) * 0.008
}, {
duration: 2000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: addOceanMovement
});
}
});
// Similar motion for AI grid
tween(aiGridContainer, {
x: aiGridContainer.x + (Math.random() - 0.5) * 8,
y: aiGridContainer.y + (Math.random() - 0.5) * 6,
rotation: aiGridContainer.rotation + (Math.random() - 0.5) * 0.008
}, {
duration: 2200 + Math.random() * 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(aiGridContainer, {
x: game.children.indexOf(aiGridContainer) >= 0 ? aiGridContainer.x - (Math.random() - 0.5) * 8 : aiGridContainer.x,
y: game.children.indexOf(aiGridContainer) >= 0 ? aiGridContainer.y - (Math.random() - 0.5) * 6 : aiGridContainer.y,
rotation: aiGridContainer.rotation - (Math.random() - 0.5) * 0.008
}, {
duration: 2200 + Math.random() * 800,
easing: tween.easeInOut
});
}
});
// Gentle movement for commander containers
tween(myCommanderContainer, {
x: myCommanderContainer.x + (Math.random() - 0.5) * 4,
y: myCommanderContainer.y + (Math.random() - 0.5) * 3
}, {
duration: 3000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(myCommanderContainer, {
x: 50 + (Math.random() - 0.5) * 4,
y: 2732 - 500 + (Math.random() - 0.5) * 3
}, {
duration: 3000 + Math.random() * 1000,
easing: tween.easeInOut
});
}
});
tween(enemyCommanderContainer, {
x: enemyCommanderContainer.x + (Math.random() - 0.5) * 4,
y: enemyCommanderContainer.y + (Math.random() - 0.5) * 3
}, {
duration: 2800 + Math.random() * 1200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(enemyCommanderContainer, {
x: 50 + (Math.random() - 0.5) * 4,
y: 2732 - 500 + (Math.random() - 0.5) * 3
}, {
duration: 2800 + Math.random() * 1200,
easing: tween.easeInOut
});
}
});
}
}
// Add computer screen effects
function addComputerEffects() {
if (gameState === 'playing' || gameState === 'placement') {
// Screen flicker effect
if (computerScreen) {
var flickerAlpha = 0.95 + Math.random() * 0.05;
tween(computerScreen, {
alpha: flickerAlpha,
tint: 0x001100 + Math.floor(Math.random() * 0x001100)
}, {
duration: 100 + Math.random() * 200,
onFinish: addComputerEffects
});
}
}
}
// Start computer effects
LK.setTimeout(function () {
addComputerEffects();
}, 2000);
// Start ocean movement after a short delay
LK.setTimeout(function () {
addOceanMovement();
}, 1000);
// Ocean waves music will start after intro ends
// Game code saved successfully - Naval Combat Game Complete;
water look on top. In-Game asset. 2d. High contrast. No shadows
white location icon look on top. In-Game asset. 2d. High contrast. No shadows
explosion look on top. In-Game asset. 2d. High contrast. No shadows
white smoke look on top. In-Game asset. 2d. High contrast. No shadows
red point icon. In-Game asset. 2d. High contrast. No shadows
2d sea mine In-Game asset. 2d. High contrast. No shadows
warship commander head looking right in game asset. In-Game asset. 2d. High contrast. No shadows
warship commander head looking left in game asset. In-Game asset. 2d. High contrast. No shadows
speech bubble 2d. In-Game asset. 2d. High contrast. No shadows
metal border. In-Game asset. 2d. High contrast. No shadows
metal line. In-Game asset. 2d. High contrast. No shadows