User prompt
after the ship entirely sunk, i want to make it seem as sunk not black.
User prompt
bring a little bit down
User prompt
bring rotate button to middle and make it bigger
User prompt
make sure that my assets do not spoiling when i rotate the ships.
User prompt
make button where i can rotate the ship on the top
User prompt
hey I have uploaded the sprites of the each ship to the assets folder. Like for the 1 parted ship there is only one ship sprite, for the 2 parted ship there are two seperate sprite for each part of it and when merged it looks like a full ship. What I want you to do is to apply those photos according to the ships form and ordered.
User prompt
bring to previous version
User prompt
when i place ships make them as a whole not like slice slice
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'scaleX' in null' in or related to this line: 'tween(self.shipPartGraphic, {' Line Number: 154
User prompt
look! when i place my sheep i wanna place them at once i mean completely but when i blow up or enemy blows up mine each part should individually be blown up when i click first gap missed then second missed then third oh i found then when i found the other part the parts of ship should be seen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
refine it
User prompt
i cannot see my ships
User prompt
no i dont mean it bring previous version back
User prompt
make them seen when i place and click also ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
i want to make 4 different ships. Then i will slice them into parts. For example i clicked and found a one part then second part when i click parts of ship should be seen then when i click other part again it should be visible. When i click last part, the whole ship should be seen as a sunk ship. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
in order to make ship appearance i will make ship png and slice it then when i click on ship one part of it will be blown and then i will click rest of it. After fully found the whole ship will be seen as blown up as a whole. So now i need to add all parts one by one
User prompt
make some blow up effect whenever i blow or enemy blows my ship ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make cross lines visible and also when i attack enemy's ships i cannot blow them up. Additionally make the water area larger.
Code edit (1 edits merged)
Please save this source code
User prompt
Naval Strike: Battleship Duel
Initial prompt
A game like sea batlle. Where we place our ships and guess to blow them.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var GridTile = Container.expand(function (x, y, isPlayerGrid) {
var self = Container.call(this);
self.gridX = x;
self.gridY = y;
self.isPlayerGrid = isPlayerGrid;
self.hasShip = false;
self.isHit = false;
self.shipId = null;
self.shipPartIndex = null;
self.shipPartGraphic = null;
var background = self.attachAsset('waterTile', {
anchorX: 0.5,
anchorY: 0.5
});
self.marker = null;
self.setShip = function (shipId, partIndex, isHorizontal) {
self.hasShip = true;
self.shipId = shipId;
self.shipPartIndex = partIndex;
// Only show ship parts on player grid during placement
if (self.isPlayerGrid) {
var assetName = shipId + 'Part' + partIndex + 'Normal';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Apply rotation if ship is vertical (not horizontal)
if (!isHorizontal) {
self.shipPartGraphic.rotation = Math.PI / 2; // 90 degrees
}
}
// For enemy grid, don't show ship parts initially - they'll be revealed when hit
};
self.markHit = function () {
if (!self.isHit) {
self.isHit = true;
if (self.hasShip) {
// For ship hits, only show hit marker - keep ship parts invisible for guessing
self.marker = self.attachAsset('hitMarker', {
anchorX: 0.5,
anchorY: 0.5
});
// Explosion effect for ship hit
self.marker.scaleX = 0.1;
self.marker.scaleY = 0.1;
self.marker.alpha = 0.8;
tween(self.marker, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.marker, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
// Flash the background tile
var background = self.children[0]; // Get the water tile background
tween(background, {
tint: 0xff4444
}, {
duration: 150,
onFinish: function onFinish() {
tween(background, {
tint: 0xffffff
}, {
duration: 150
});
}
});
} else {
self.marker = self.attachAsset('missMarker', {
anchorX: 0.5,
anchorY: 0.5
});
// Small splash effect for miss
self.marker.scaleX = 0.3;
self.marker.scaleY = 0.3;
tween(self.marker, {
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.bounceOut
});
}
}
};
self.blowUpShipPart = function (keepColorless) {
if (self.shipPartGraphic) {
// Store the current rotation before removing
var currentRotation = self.shipPartGraphic.rotation;
self.removeChild(self.shipPartGraphic);
var assetName = self.shipId + 'Part' + self.shipPartIndex + 'Blown';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Restore the rotation
self.shipPartGraphic.rotation = currentRotation;
// Explosion effect for individual part
self.shipPartGraphic.scaleX = 0.2;
self.shipPartGraphic.scaleY = 0.2;
self.shipPartGraphic.alpha = 0.5;
self.shipPartGraphic.tint = 0xff4444;
// Only animate if shipPartGraphic exists
if (self.shipPartGraphic) {
tween(self.shipPartGraphic, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.shipPartGraphic) {
var finalTint = keepColorless ? 0x666666 : 0xffffff; // Keep colorless or make it white for color
tween(self.shipPartGraphic, {
scaleX: 1,
scaleY: 1,
tint: finalTint
}, {
duration: 400,
easing: tween.easeInOut
});
}
}
});
}
}
};
self.makePartColorful = function () {
if (self.shipPartGraphic) {
// Remove the current blown part and replace with colorful version
var currentRotation = self.shipPartGraphic.rotation;
self.removeChild(self.shipPartGraphic);
var assetName = self.shipId + 'Part' + self.shipPartIndex + 'Blown';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Restore the rotation
self.shipPartGraphic.rotation = currentRotation;
// Make it colorful with a nice tween effect
self.shipPartGraphic.tint = 0xffffff; // Start white to show the asset's natural colors
tween(self.shipPartGraphic, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.shipPartGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
};
self.markMiss = function () {
if (!self.isHit) {
self.isHit = true;
self.marker = self.attachAsset('missMarker', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
self.markSunk = function () {
if (self.marker) {
self.removeChild(self.marker);
}
// Now show the actual ship asset when fully sunk
if (!self.isPlayerGrid && self.hasShip) {
var assetName = self.shipId + 'Part' + self.shipPartIndex + 'Normal';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Apply rotation if ship is vertical (not horizontal)
// Find ship to determine orientation
for (var i = 0; i < aiShips.length; i++) {
if (aiShips[i].id === self.shipId) {
self.shipPartGraphic.rotation = aiShips[i].isHorizontal ? 0 : Math.PI / 2;
break;
}
}
}
// Then make the individual part colorful for visual effect
self.makePartColorful();
// Background explosion flash
var background = self.children[0]; // Get the water tile background
tween(background, {
tint: 0xff6600
}, {
duration: 200,
onFinish: function onFinish() {
tween(background, {
tint: 0x2c5aa0
}, {
duration: 400,
onFinish: function onFinish() {
tween(background, {
tint: 0xffffff
}, {
duration: 200
});
}
});
}
});
};
self.down = function (x, y, obj) {
if (gamePhase === 'placement') {
// Check if this tile has a ship and we want to drag it back
if (self.hasShip && self.isPlayerGrid) {
// Find the ship and corresponding preview ship
for (var i = 0; i < playerShips.length; i++) {
if (playerShips[i].isPlaced) {
var positions = playerShips[i].getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === self.gridX && positions[j].y === self.gridY) {
// Found the ship, remove it from grid and make preview ship draggable again
var currentPositions = playerShips[i].getPositions();
for (var k = 0; k < currentPositions.length; k++) {
var pos = currentPositions[k];
var tile = playerGrid[pos.x][pos.y];
tile.hasShip = false;
tile.shipId = null;
tile.shipPartIndex = null;
if (tile.shipPartGraphic) {
tile.removeChild(tile.shipPartGraphic);
tile.shipPartGraphic = null;
}
}
// Reset ship state
playerShips[i].isPlaced = false;
playerShips[i].positions = [];
// Make corresponding preview ship visible and draggable again
var previewShip = previewShips[i];
previewShip.placed = false;
previewShip.visible = true;
previewShip.x = previewShip.originalX;
previewShip.y = previewShip.originalY;
// Hide start button since not all ships are placed now
startButton.visible = false;
instructionText.setText('Tap ship to rotate, then drag to place');
return;
}
}
}
}
}
handleShipPlacement(self.gridX, self.gridY);
} else if (gamePhase === 'battle' && !self.isPlayerGrid && !self.isHit) {
handleAttack(self.gridX, self.gridY);
}
};
return self;
});
var RotateButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('rotateButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2('ROTATE', {
size: 24,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
if (gamePhase === 'placement') {
// Rotate all unplaced ships
for (var i = 0; i < previewShips.length; i++) {
if (!previewShips[i].placed) {
previewShips[i].isHorizontal = !previewShips[i].isHorizontal;
previewShips[i].updateOrientation();
}
}
// Update rotation text to show current state
var rotationText = previewShips.length > 0 && !previewShips[0].placed ? previewShips[0].isHorizontal ? 'Horizontal' : 'Vertical' : 'Horizontal';
rotateText.setText('Current: ' + rotationText + ' (Tap to change)');
// Check if all ships are placed to show start button
var allPlaced = true;
for (var j = 0; j < previewShips.length; j++) {
if (!previewShips[j].placed) {
allPlaced = false;
break;
}
}
if (allPlaced) {
startButton.visible = true;
}
// Visual feedback
tween(buttonBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(buttonBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
return self;
});
var Ship = Container.expand(function (id, size, name) {
var self = Container.call(this);
self.id = id;
self.size = size;
self.name = name;
self.positions = [];
self.hits = 0;
self.isHorizontal = true;
self.isPlaced = false;
self.isSunk = false;
self.getPositions = function () {
return self.positions;
};
self.hit = function () {
self.hits++;
if (self.hits >= self.size) {
self.isSunk = true;
return true;
}
return false;
};
self.placeAt = function (startX, startY, horizontal) {
self.positions = [];
self.isHorizontal = horizontal;
self.isPlaced = true;
for (var i = 0; i < self.size; i++) {
if (horizontal) {
self.positions.push({
x: startX + i,
y: startY
});
} else {
self.positions.push({
x: startX,
y: startY + i
});
}
}
};
return self;
});
var StartButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('rotateButton', {
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.width = 200;
buttonBg.height = 80;
var buttonText = new Text2('START GAME', {
size: 32,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.visible = false;
self.down = function (x, y, obj) {
if (gamePhase === 'placement') {
// Check if all ships are placed
var allPlaced = true;
for (var i = 0; i < previewShips.length; i++) {
if (!previewShips[i].placed) {
allPlaced = false;
break;
}
}
if (allPlaced) {
placeAIShips();
startBattlePhase();
self.visible = false;
}
// Visual feedback
tween(buttonBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(buttonBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
// Destroyer parts (2 parts)
// Cruiser parts (3 parts)
// Battleship parts (4 parts)
// Carrier parts (5 parts)
// Destroyer (2 parts)
// Cruiser (3 parts)
// Battleship (4 parts)
// Carrier (5 parts)
var GRID_SIZE = 10;
var TILE_SIZE = 90;
var GRID_SPACING = 120;
var gamePhase = 'placement'; // 'placement', 'battle', 'gameOver'
var currentPlayer = 'player'; // 'player', 'ai'
var playerGrid = [];
var aiGrid = [];
var playerShips = [];
var aiShips = [];
var currentShipIndex = 0;
// Individual ships now track their own orientation via isHorizontal property
// Ship definitions
var shipDefinitions = [{
id: 'destroyer',
size: 2,
name: 'Destroyer'
}, {
id: 'destroyer',
size: 2,
name: 'Destroyer'
}, {
id: 'destroyer',
size: 2,
name: 'Destroyer'
}, {
id: 'cruiser',
size: 3,
name: 'Cruiser'
}, {
id: 'cruiser',
size: 3,
name: 'Cruiser'
}, {
id: 'battleship',
size: 4,
name: 'Battleship'
}, {
id: 'carrier',
size: 5,
name: 'Carrier'
}];
// UI Elements
var phaseText = new Text2('Drag ships to place them', {
size: 60,
fill: 0xFFFFFF
});
phaseText.anchor.set(0.5, 0);
LK.gui.top.addChild(phaseText);
var instructionText = new Text2('Drag and drop ships from preview area', {
size: 40,
fill: 0xCCCCCC
});
instructionText.anchor.set(0.5, 0);
instructionText.y = 80;
LK.gui.top.addChild(instructionText);
var rotateText = new Text2('Tap ship to rotate before dragging', {
size: 30,
fill: 0xFFFF00
});
rotateText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(rotateText);
// Preview area for ships
var previewArea = [];
var draggedShip = null;
var dragOffset = {
x: 0,
y: 0
};
var previewShips = [];
// Create ship display area below rotate button
function createShipArea() {
var shipAreaStartY = 500; // Below rotate button with more space
var shipSpacing = 200; // Increased spacing to prevent overlap
var columnSpacing = 300; // Spacing between columns
// Position ships in 3 columns with extra ship to the right
var leftColumnX = 2048 / 2 - columnSpacing; // Left column
var centerColumnX = 2048 / 2; // Center column
var rightColumnX = 2048 / 2 + columnSpacing; // Right column
var extraShipX = rightColumnX + columnSpacing; // Extra position further to the right
for (var i = 0; i < shipDefinitions.length; i++) {
var def = shipDefinitions[i];
var shipContainer = new Container();
shipContainer.shipIndex = i;
// Position ships: 2-2-2-1 distribution for 7 ships
if (i < 2) {
shipContainer.x = leftColumnX;
shipContainer.y = shipAreaStartY + i * shipSpacing;
} else if (i < 4) {
shipContainer.x = centerColumnX;
shipContainer.y = shipAreaStartY + (i - 2) * shipSpacing;
} else if (i < 6) {
shipContainer.x = rightColumnX;
shipContainer.y = shipAreaStartY + (i - 4) * shipSpacing;
} else {
// Extra ship positioned to the right
shipContainer.x = extraShipX;
shipContainer.y = shipAreaStartY;
}
shipContainer.originalX = shipContainer.x;
shipContainer.originalY = shipContainer.y;
shipContainer.isHorizontal = true; // Start horizontal
shipContainer.placed = false;
shipContainer.parts = [];
// Create ship parts
for (var j = 0; j < def.size; j++) {
var partAsset = shipContainer.attachAsset(def.id + 'Part' + j + 'Normal', {
anchorX: 0.5,
anchorY: 0.5
});
partAsset.x = (j - def.size / 2 + 0.5) * 88;
partAsset.y = 0;
shipContainer.parts.push(partAsset);
}
// Add ship name label
var nameText = new Text2(def.name + ' (' + def.size + ')', {
size: 28,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.y = -80;
shipContainer.addChild(nameText);
// Add down handler for dragging start
shipContainer.down = function (x, y, obj) {
if (!this.placed && gamePhase === 'placement') {
// Start dragging without rotating
draggedShip = this;
dragOffset.x = x;
dragOffset.y = y;
}
};
shipContainer.updateOrientation = function () {
for (var k = 0; k < this.parts.length; k++) {
if (this.isHorizontal) {
this.parts[k].x = (k - shipDefinitions[this.shipIndex].size / 2 + 0.5) * 88;
this.parts[k].y = 0;
this.parts[k].rotation = 0;
} else {
this.parts[k].x = 0;
this.parts[k].y = (k - shipDefinitions[this.shipIndex].size / 2 + 0.5) * 88;
this.parts[k].rotation = Math.PI / 2;
}
}
};
previewShips.push(shipContainer);
game.addChild(shipContainer);
}
}
// Create rotate button
var rotateButton = new RotateButton();
rotateButton.x = 2048 / 2; // Center horizontally
rotateButton.y = 280; // Position a little bit down from top center
rotateButton.scaleX = 2.0; // Make it bigger
rotateButton.scaleY = 2.0; // Make it bigger
game.addChild(rotateButton);
// Create start button
var startButton = new StartButton();
startButton.x = 2048 / 2; // Center horizontally
startButton.y = 430; // Below rotate button, moved down a bit
startButton.scaleX = 1.5;
startButton.scaleY = 1.5;
game.addChild(startButton);
// Initialize grids
function initializeGrids() {
var playerGridStartX = 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var aiGridStartX = 3 * 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var gridStartY = 2732 / 2 - GRID_SIZE * TILE_SIZE / 2;
// Create player grid
for (var x = 0; x < GRID_SIZE; x++) {
playerGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
var tile = new GridTile(x, y, true);
tile.x = playerGridStartX + x * TILE_SIZE;
tile.y = gridStartY + y * TILE_SIZE;
playerGrid[x][y] = tile;
game.addChild(tile);
}
}
// Create AI grid
for (var x = 0; x < GRID_SIZE; x++) {
aiGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
var tile = new GridTile(x, y, false);
tile.x = aiGridStartX + x * TILE_SIZE;
tile.y = gridStartY + y * TILE_SIZE;
aiGrid[x][y] = tile;
game.addChild(tile);
}
}
}
// Create ships
function createShips() {
for (var i = 0; i < shipDefinitions.length; i++) {
var def = shipDefinitions[i];
var ship = new Ship(def.id, def.size, def.name);
playerShips.push(ship);
var aiShip = new Ship(def.id, def.size, def.name);
aiShips.push(aiShip);
}
}
function canPlaceShip(grid, x, y, size, horizontal) {
for (var i = 0; i < size; i++) {
var checkX = horizontal ? x + i : x;
var checkY = horizontal ? y : y + i;
if (checkX >= GRID_SIZE || checkY >= GRID_SIZE) {
return false;
}
if (grid[checkX][checkY].hasShip) {
return false;
}
}
return true;
}
function placeShip(grid, ship, x, y, horizontal) {
ship.placeAt(x, y, horizontal);
var positions = ship.getPositions();
for (var i = 0; i < positions.length; i++) {
var pos = positions[i];
grid[pos.x][pos.y].setShip(ship.id, i, horizontal);
}
}
function handleShipPlacement(x, y) {
// Check if we're clicking on an already placed ship to rotate it directly
var clickedTile = playerGrid[x][y];
if (clickedTile.hasShip) {
// Find which ship this tile belongs to
var shipToRotate = null;
var shipIndex = -1;
for (var i = 0; i < playerShips.length; i++) {
if (playerShips[i].isPlaced) {
var positions = playerShips[i].getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === x && positions[j].y === y) {
shipToRotate = playerShips[i];
shipIndex = i;
break;
}
}
if (shipToRotate) break;
}
}
if (shipToRotate) {
// Rotate this specific ship directly
var currentPositions = shipToRotate.getPositions();
var startX = currentPositions[0].x;
var startY = currentPositions[0].y;
var newHorizontal = !shipToRotate.isHorizontal;
// Temporarily remove ship from current positions to check placement
for (var k = 0; k < currentPositions.length; k++) {
var pos = currentPositions[k];
var tile = playerGrid[pos.x][pos.y];
tile.hasShip = false;
tile.shipId = null;
tile.shipPartIndex = null;
if (tile.shipPartGraphic) {
tile.removeChild(tile.shipPartGraphic);
tile.shipPartGraphic = null;
}
}
// Check if rotation is possible
if (canPlaceShip(playerGrid, startX, startY, shipToRotate.size, newHorizontal)) {
// Place ship with new orientation
placeShip(playerGrid, shipToRotate, startX, startY, newHorizontal);
rotateText.setText('Ship rotated! Current: ' + (newHorizontal ? 'Horizontal' : 'Vertical'));
} else {
// Rotation not possible, restore original placement
placeShip(playerGrid, shipToRotate, startX, startY, shipToRotate.isHorizontal);
rotateText.setText('Cannot rotate - not enough space!');
}
return;
}
} else {
rotateText.setText('Current: Horizontal (Tap ROTATE button to change)');
}
}
function placeAIShips() {
for (var i = 0; i < aiShips.length; i++) {
var ship = aiShips[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(aiGrid, x, y, ship.size, horizontal)) {
placeShip(aiGrid, ship, x, y, horizontal);
placed = true;
}
attempts++;
}
}
}
function startBattlePhase() {
gamePhase = 'battle';
phaseText.setText('Battle Phase');
instructionText.setText('Attack enemy grid!');
rotateText.setText('');
}
function handleAttack(x, y) {
if (gamePhase !== 'battle' || currentPlayer !== 'player') return;
var tile = aiGrid[x][y];
var isHit = tile.hasShip;
tile.markHit();
if (isHit) {
LK.getSound('hit').play();
// Check if ship is sunk
var hitShip = null;
for (var i = 0; i < aiShips.length; i++) {
var ship = aiShips[i];
var positions = ship.getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === x && positions[j].y === y) {
hitShip = ship;
break;
}
}
if (hitShip) break;
}
// Individual part blow up is now handled in markHit method
if (hitShip && hitShip.hit()) {
LK.getSound('sink').play();
// Mark all ship positions as sunk
var positions = hitShip.getPositions();
for (var k = 0; k < positions.length; k++) {
var pos = positions[k];
aiGrid[pos.x][pos.y].markSunk();
}
if (checkVictory(aiShips)) {
gamePhase = 'gameOver';
phaseText.setText('You Win!');
instructionText.setText('All enemy ships destroyed!');
LK.showYouWin();
return;
}
}
// Player hit - give another chance
instructionText.setText('Hit! Attack again!');
} else {
tile.markMiss();
LK.getSound('miss').play();
// Player missed - switch to AI turn
currentPlayer = 'ai';
instructionText.setText('AI is attacking...');
LK.setTimeout(function () {
aiAttack();
}, 1000);
}
}
function aiAttack() {
var validTargets = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (!playerGrid[x][y].isHit) {
validTargets.push({
x: x,
y: y
});
}
}
}
if (validTargets.length === 0) return;
var target = validTargets[Math.floor(Math.random() * validTargets.length)];
var tile = playerGrid[target.x][target.y];
var isHit = tile.hasShip;
tile.markHit();
if (isHit) {
LK.getSound('hit').play();
// Check if ship is sunk
var hitShip = null;
for (var i = 0; i < playerShips.length; i++) {
var ship = playerShips[i];
var positions = ship.getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === target.x && positions[j].y === target.y) {
hitShip = ship;
break;
}
}
if (hitShip) break;
}
// Individual part blow up is now handled in markHit method
if (hitShip && hitShip.hit()) {
LK.getSound('sink').play();
// Mark all ship positions as sunk
var positions = hitShip.getPositions();
for (var k = 0; k < positions.length; k++) {
var pos = positions[k];
playerGrid[pos.x][pos.y].markSunk();
}
if (checkVictory(playerShips)) {
gamePhase = 'gameOver';
phaseText.setText('You Lose!');
instructionText.setText('All your ships destroyed!');
LK.showGameOver();
return;
}
}
// AI hit - continue AI turn
instructionText.setText('AI hit! AI attacking again...');
LK.setTimeout(function () {
aiAttack();
}, 1000);
} else {
tile.markMiss();
LK.getSound('miss').play();
// AI missed - switch to player turn
currentPlayer = 'player';
instructionText.setText('Your turn - Attack enemy grid!');
}
}
function checkVictory(ships) {
for (var i = 0; i < ships.length; i++) {
if (!ships[i].isSunk) {
return false;
}
}
return true;
}
// Add global move handler for ship dragging
game.move = function (x, y, obj) {
if (draggedShip) {
draggedShip.x = x - dragOffset.x;
draggedShip.y = y - dragOffset.y;
// Clear previous highlights
for (var gx = 0; gx < GRID_SIZE; gx++) {
for (var gy = 0; gy < GRID_SIZE; gy++) {
var tile = playerGrid[gx][gy];
if (tile.highlight) {
tile.removeChild(tile.highlight);
tile.highlight = null;
}
}
}
// Show placement preview
var gridStartX = 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var gridStartY = 2732 / 2 - GRID_SIZE * TILE_SIZE / 2;
var gridX = Math.floor((draggedShip.x - gridStartX) / TILE_SIZE);
var gridY = Math.floor((draggedShip.y - gridStartY) / TILE_SIZE);
if (gridX >= 0 && gridY >= 0 && gridX < GRID_SIZE && gridY < GRID_SIZE) {
var shipSize = shipDefinitions[draggedShip.shipIndex].size;
var canPlace = canPlaceShip(playerGrid, gridX, gridY, shipSize, draggedShip.isHorizontal);
if (canPlace) {
for (var i = 0; i < shipSize; i++) {
var checkX = draggedShip.isHorizontal ? gridX + i : gridX;
var checkY = draggedShip.isHorizontal ? gridY : gridY + i;
if (checkX < GRID_SIZE && checkY < GRID_SIZE) {
var tile = playerGrid[checkX][checkY];
tile.highlight = tile.attachAsset('placementHighlight', {
anchorX: 0.5,
anchorY: 0.5
});
tile.highlight.alpha = 0.5;
}
}
}
}
}
};
// Add global up handler for ship placement
game.up = function (x, y, obj) {
if (draggedShip) {
// Clear highlights first
for (var gx = 0; gx < GRID_SIZE; gx++) {
for (var gy = 0; gy < GRID_SIZE; gy++) {
var tile = playerGrid[gx][gy];
if (tile.highlight) {
tile.removeChild(tile.highlight);
tile.highlight = null;
}
}
}
// Try to place ship
var gridStartX = 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var gridStartY = 2732 / 2 - GRID_SIZE * TILE_SIZE / 2;
var gridX = Math.floor((draggedShip.x - gridStartX) / TILE_SIZE);
var gridY = Math.floor((draggedShip.y - gridStartY) / TILE_SIZE);
var shipSize = shipDefinitions[draggedShip.shipIndex].size;
if (gridX >= 0 && gridY >= 0 && canPlaceShip(playerGrid, gridX, gridY, shipSize, draggedShip.isHorizontal)) {
// Place the ship
var ship = playerShips[draggedShip.shipIndex];
placeShip(playerGrid, ship, gridX, gridY, draggedShip.isHorizontal);
draggedShip.placed = true;
draggedShip.visible = false;
// Check if all ships are placed
var allPlaced = true;
for (var i = 0; i < previewShips.length; i++) {
if (!previewShips[i].placed) {
allPlaced = false;
break;
}
}
if (allPlaced) {
startButton.visible = true;
instructionText.setText('All ships placed! Click START GAME to begin battle');
}
} else {
// Return ship to original position
tween(draggedShip, {
x: draggedShip.originalX,
y: draggedShip.originalY
}, {
duration: 300,
easing: tween.easeOut
});
}
draggedShip = null;
}
};
// Initialize game
initializeGrids();
createShips();
createShipArea();
// Update initial instruction
instructionText.setText('Tap ship to rotate, then drag to place');
rotateText.setText('Current: Horizontal (Tap ROTATE button to change)');
; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var GridTile = Container.expand(function (x, y, isPlayerGrid) {
var self = Container.call(this);
self.gridX = x;
self.gridY = y;
self.isPlayerGrid = isPlayerGrid;
self.hasShip = false;
self.isHit = false;
self.shipId = null;
self.shipPartIndex = null;
self.shipPartGraphic = null;
var background = self.attachAsset('waterTile', {
anchorX: 0.5,
anchorY: 0.5
});
self.marker = null;
self.setShip = function (shipId, partIndex, isHorizontal) {
self.hasShip = true;
self.shipId = shipId;
self.shipPartIndex = partIndex;
// Only show ship parts on player grid during placement
if (self.isPlayerGrid) {
var assetName = shipId + 'Part' + partIndex + 'Normal';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Apply rotation if ship is vertical (not horizontal)
if (!isHorizontal) {
self.shipPartGraphic.rotation = Math.PI / 2; // 90 degrees
}
}
// For enemy grid, don't show ship parts initially - they'll be revealed when hit
};
self.markHit = function () {
if (!self.isHit) {
self.isHit = true;
if (self.hasShip) {
// For ship hits, only show hit marker - keep ship parts invisible for guessing
self.marker = self.attachAsset('hitMarker', {
anchorX: 0.5,
anchorY: 0.5
});
// Explosion effect for ship hit
self.marker.scaleX = 0.1;
self.marker.scaleY = 0.1;
self.marker.alpha = 0.8;
tween(self.marker, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.marker, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
// Flash the background tile
var background = self.children[0]; // Get the water tile background
tween(background, {
tint: 0xff4444
}, {
duration: 150,
onFinish: function onFinish() {
tween(background, {
tint: 0xffffff
}, {
duration: 150
});
}
});
} else {
self.marker = self.attachAsset('missMarker', {
anchorX: 0.5,
anchorY: 0.5
});
// Small splash effect for miss
self.marker.scaleX = 0.3;
self.marker.scaleY = 0.3;
tween(self.marker, {
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.bounceOut
});
}
}
};
self.blowUpShipPart = function (keepColorless) {
if (self.shipPartGraphic) {
// Store the current rotation before removing
var currentRotation = self.shipPartGraphic.rotation;
self.removeChild(self.shipPartGraphic);
var assetName = self.shipId + 'Part' + self.shipPartIndex + 'Blown';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Restore the rotation
self.shipPartGraphic.rotation = currentRotation;
// Explosion effect for individual part
self.shipPartGraphic.scaleX = 0.2;
self.shipPartGraphic.scaleY = 0.2;
self.shipPartGraphic.alpha = 0.5;
self.shipPartGraphic.tint = 0xff4444;
// Only animate if shipPartGraphic exists
if (self.shipPartGraphic) {
tween(self.shipPartGraphic, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.shipPartGraphic) {
var finalTint = keepColorless ? 0x666666 : 0xffffff; // Keep colorless or make it white for color
tween(self.shipPartGraphic, {
scaleX: 1,
scaleY: 1,
tint: finalTint
}, {
duration: 400,
easing: tween.easeInOut
});
}
}
});
}
}
};
self.makePartColorful = function () {
if (self.shipPartGraphic) {
// Remove the current blown part and replace with colorful version
var currentRotation = self.shipPartGraphic.rotation;
self.removeChild(self.shipPartGraphic);
var assetName = self.shipId + 'Part' + self.shipPartIndex + 'Blown';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Restore the rotation
self.shipPartGraphic.rotation = currentRotation;
// Make it colorful with a nice tween effect
self.shipPartGraphic.tint = 0xffffff; // Start white to show the asset's natural colors
tween(self.shipPartGraphic, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.shipPartGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
};
self.markMiss = function () {
if (!self.isHit) {
self.isHit = true;
self.marker = self.attachAsset('missMarker', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
self.markSunk = function () {
if (self.marker) {
self.removeChild(self.marker);
}
// Now show the actual ship asset when fully sunk
if (!self.isPlayerGrid && self.hasShip) {
var assetName = self.shipId + 'Part' + self.shipPartIndex + 'Normal';
self.shipPartGraphic = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Apply rotation if ship is vertical (not horizontal)
// Find ship to determine orientation
for (var i = 0; i < aiShips.length; i++) {
if (aiShips[i].id === self.shipId) {
self.shipPartGraphic.rotation = aiShips[i].isHorizontal ? 0 : Math.PI / 2;
break;
}
}
}
// Then make the individual part colorful for visual effect
self.makePartColorful();
// Background explosion flash
var background = self.children[0]; // Get the water tile background
tween(background, {
tint: 0xff6600
}, {
duration: 200,
onFinish: function onFinish() {
tween(background, {
tint: 0x2c5aa0
}, {
duration: 400,
onFinish: function onFinish() {
tween(background, {
tint: 0xffffff
}, {
duration: 200
});
}
});
}
});
};
self.down = function (x, y, obj) {
if (gamePhase === 'placement') {
// Check if this tile has a ship and we want to drag it back
if (self.hasShip && self.isPlayerGrid) {
// Find the ship and corresponding preview ship
for (var i = 0; i < playerShips.length; i++) {
if (playerShips[i].isPlaced) {
var positions = playerShips[i].getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === self.gridX && positions[j].y === self.gridY) {
// Found the ship, remove it from grid and make preview ship draggable again
var currentPositions = playerShips[i].getPositions();
for (var k = 0; k < currentPositions.length; k++) {
var pos = currentPositions[k];
var tile = playerGrid[pos.x][pos.y];
tile.hasShip = false;
tile.shipId = null;
tile.shipPartIndex = null;
if (tile.shipPartGraphic) {
tile.removeChild(tile.shipPartGraphic);
tile.shipPartGraphic = null;
}
}
// Reset ship state
playerShips[i].isPlaced = false;
playerShips[i].positions = [];
// Make corresponding preview ship visible and draggable again
var previewShip = previewShips[i];
previewShip.placed = false;
previewShip.visible = true;
previewShip.x = previewShip.originalX;
previewShip.y = previewShip.originalY;
// Hide start button since not all ships are placed now
startButton.visible = false;
instructionText.setText('Tap ship to rotate, then drag to place');
return;
}
}
}
}
}
handleShipPlacement(self.gridX, self.gridY);
} else if (gamePhase === 'battle' && !self.isPlayerGrid && !self.isHit) {
handleAttack(self.gridX, self.gridY);
}
};
return self;
});
var RotateButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('rotateButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2('ROTATE', {
size: 24,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
if (gamePhase === 'placement') {
// Rotate all unplaced ships
for (var i = 0; i < previewShips.length; i++) {
if (!previewShips[i].placed) {
previewShips[i].isHorizontal = !previewShips[i].isHorizontal;
previewShips[i].updateOrientation();
}
}
// Update rotation text to show current state
var rotationText = previewShips.length > 0 && !previewShips[0].placed ? previewShips[0].isHorizontal ? 'Horizontal' : 'Vertical' : 'Horizontal';
rotateText.setText('Current: ' + rotationText + ' (Tap to change)');
// Check if all ships are placed to show start button
var allPlaced = true;
for (var j = 0; j < previewShips.length; j++) {
if (!previewShips[j].placed) {
allPlaced = false;
break;
}
}
if (allPlaced) {
startButton.visible = true;
}
// Visual feedback
tween(buttonBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(buttonBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
return self;
});
var Ship = Container.expand(function (id, size, name) {
var self = Container.call(this);
self.id = id;
self.size = size;
self.name = name;
self.positions = [];
self.hits = 0;
self.isHorizontal = true;
self.isPlaced = false;
self.isSunk = false;
self.getPositions = function () {
return self.positions;
};
self.hit = function () {
self.hits++;
if (self.hits >= self.size) {
self.isSunk = true;
return true;
}
return false;
};
self.placeAt = function (startX, startY, horizontal) {
self.positions = [];
self.isHorizontal = horizontal;
self.isPlaced = true;
for (var i = 0; i < self.size; i++) {
if (horizontal) {
self.positions.push({
x: startX + i,
y: startY
});
} else {
self.positions.push({
x: startX,
y: startY + i
});
}
}
};
return self;
});
var StartButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('rotateButton', {
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.width = 200;
buttonBg.height = 80;
var buttonText = new Text2('START GAME', {
size: 32,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.visible = false;
self.down = function (x, y, obj) {
if (gamePhase === 'placement') {
// Check if all ships are placed
var allPlaced = true;
for (var i = 0; i < previewShips.length; i++) {
if (!previewShips[i].placed) {
allPlaced = false;
break;
}
}
if (allPlaced) {
placeAIShips();
startBattlePhase();
self.visible = false;
}
// Visual feedback
tween(buttonBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(buttonBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
// Destroyer parts (2 parts)
// Cruiser parts (3 parts)
// Battleship parts (4 parts)
// Carrier parts (5 parts)
// Destroyer (2 parts)
// Cruiser (3 parts)
// Battleship (4 parts)
// Carrier (5 parts)
var GRID_SIZE = 10;
var TILE_SIZE = 90;
var GRID_SPACING = 120;
var gamePhase = 'placement'; // 'placement', 'battle', 'gameOver'
var currentPlayer = 'player'; // 'player', 'ai'
var playerGrid = [];
var aiGrid = [];
var playerShips = [];
var aiShips = [];
var currentShipIndex = 0;
// Individual ships now track their own orientation via isHorizontal property
// Ship definitions
var shipDefinitions = [{
id: 'destroyer',
size: 2,
name: 'Destroyer'
}, {
id: 'destroyer',
size: 2,
name: 'Destroyer'
}, {
id: 'destroyer',
size: 2,
name: 'Destroyer'
}, {
id: 'cruiser',
size: 3,
name: 'Cruiser'
}, {
id: 'cruiser',
size: 3,
name: 'Cruiser'
}, {
id: 'battleship',
size: 4,
name: 'Battleship'
}, {
id: 'carrier',
size: 5,
name: 'Carrier'
}];
// UI Elements
var phaseText = new Text2('Drag ships to place them', {
size: 60,
fill: 0xFFFFFF
});
phaseText.anchor.set(0.5, 0);
LK.gui.top.addChild(phaseText);
var instructionText = new Text2('Drag and drop ships from preview area', {
size: 40,
fill: 0xCCCCCC
});
instructionText.anchor.set(0.5, 0);
instructionText.y = 80;
LK.gui.top.addChild(instructionText);
var rotateText = new Text2('Tap ship to rotate before dragging', {
size: 30,
fill: 0xFFFF00
});
rotateText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(rotateText);
// Preview area for ships
var previewArea = [];
var draggedShip = null;
var dragOffset = {
x: 0,
y: 0
};
var previewShips = [];
// Create ship display area below rotate button
function createShipArea() {
var shipAreaStartY = 500; // Below rotate button with more space
var shipSpacing = 200; // Increased spacing to prevent overlap
var columnSpacing = 300; // Spacing between columns
// Position ships in 3 columns with extra ship to the right
var leftColumnX = 2048 / 2 - columnSpacing; // Left column
var centerColumnX = 2048 / 2; // Center column
var rightColumnX = 2048 / 2 + columnSpacing; // Right column
var extraShipX = rightColumnX + columnSpacing; // Extra position further to the right
for (var i = 0; i < shipDefinitions.length; i++) {
var def = shipDefinitions[i];
var shipContainer = new Container();
shipContainer.shipIndex = i;
// Position ships: 2-2-2-1 distribution for 7 ships
if (i < 2) {
shipContainer.x = leftColumnX;
shipContainer.y = shipAreaStartY + i * shipSpacing;
} else if (i < 4) {
shipContainer.x = centerColumnX;
shipContainer.y = shipAreaStartY + (i - 2) * shipSpacing;
} else if (i < 6) {
shipContainer.x = rightColumnX;
shipContainer.y = shipAreaStartY + (i - 4) * shipSpacing;
} else {
// Extra ship positioned to the right
shipContainer.x = extraShipX;
shipContainer.y = shipAreaStartY;
}
shipContainer.originalX = shipContainer.x;
shipContainer.originalY = shipContainer.y;
shipContainer.isHorizontal = true; // Start horizontal
shipContainer.placed = false;
shipContainer.parts = [];
// Create ship parts
for (var j = 0; j < def.size; j++) {
var partAsset = shipContainer.attachAsset(def.id + 'Part' + j + 'Normal', {
anchorX: 0.5,
anchorY: 0.5
});
partAsset.x = (j - def.size / 2 + 0.5) * 88;
partAsset.y = 0;
shipContainer.parts.push(partAsset);
}
// Add ship name label
var nameText = new Text2(def.name + ' (' + def.size + ')', {
size: 28,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.y = -80;
shipContainer.addChild(nameText);
// Add down handler for dragging start
shipContainer.down = function (x, y, obj) {
if (!this.placed && gamePhase === 'placement') {
// Start dragging without rotating
draggedShip = this;
dragOffset.x = x;
dragOffset.y = y;
}
};
shipContainer.updateOrientation = function () {
for (var k = 0; k < this.parts.length; k++) {
if (this.isHorizontal) {
this.parts[k].x = (k - shipDefinitions[this.shipIndex].size / 2 + 0.5) * 88;
this.parts[k].y = 0;
this.parts[k].rotation = 0;
} else {
this.parts[k].x = 0;
this.parts[k].y = (k - shipDefinitions[this.shipIndex].size / 2 + 0.5) * 88;
this.parts[k].rotation = Math.PI / 2;
}
}
};
previewShips.push(shipContainer);
game.addChild(shipContainer);
}
}
// Create rotate button
var rotateButton = new RotateButton();
rotateButton.x = 2048 / 2; // Center horizontally
rotateButton.y = 280; // Position a little bit down from top center
rotateButton.scaleX = 2.0; // Make it bigger
rotateButton.scaleY = 2.0; // Make it bigger
game.addChild(rotateButton);
// Create start button
var startButton = new StartButton();
startButton.x = 2048 / 2; // Center horizontally
startButton.y = 430; // Below rotate button, moved down a bit
startButton.scaleX = 1.5;
startButton.scaleY = 1.5;
game.addChild(startButton);
// Initialize grids
function initializeGrids() {
var playerGridStartX = 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var aiGridStartX = 3 * 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var gridStartY = 2732 / 2 - GRID_SIZE * TILE_SIZE / 2;
// Create player grid
for (var x = 0; x < GRID_SIZE; x++) {
playerGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
var tile = new GridTile(x, y, true);
tile.x = playerGridStartX + x * TILE_SIZE;
tile.y = gridStartY + y * TILE_SIZE;
playerGrid[x][y] = tile;
game.addChild(tile);
}
}
// Create AI grid
for (var x = 0; x < GRID_SIZE; x++) {
aiGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
var tile = new GridTile(x, y, false);
tile.x = aiGridStartX + x * TILE_SIZE;
tile.y = gridStartY + y * TILE_SIZE;
aiGrid[x][y] = tile;
game.addChild(tile);
}
}
}
// Create ships
function createShips() {
for (var i = 0; i < shipDefinitions.length; i++) {
var def = shipDefinitions[i];
var ship = new Ship(def.id, def.size, def.name);
playerShips.push(ship);
var aiShip = new Ship(def.id, def.size, def.name);
aiShips.push(aiShip);
}
}
function canPlaceShip(grid, x, y, size, horizontal) {
for (var i = 0; i < size; i++) {
var checkX = horizontal ? x + i : x;
var checkY = horizontal ? y : y + i;
if (checkX >= GRID_SIZE || checkY >= GRID_SIZE) {
return false;
}
if (grid[checkX][checkY].hasShip) {
return false;
}
}
return true;
}
function placeShip(grid, ship, x, y, horizontal) {
ship.placeAt(x, y, horizontal);
var positions = ship.getPositions();
for (var i = 0; i < positions.length; i++) {
var pos = positions[i];
grid[pos.x][pos.y].setShip(ship.id, i, horizontal);
}
}
function handleShipPlacement(x, y) {
// Check if we're clicking on an already placed ship to rotate it directly
var clickedTile = playerGrid[x][y];
if (clickedTile.hasShip) {
// Find which ship this tile belongs to
var shipToRotate = null;
var shipIndex = -1;
for (var i = 0; i < playerShips.length; i++) {
if (playerShips[i].isPlaced) {
var positions = playerShips[i].getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === x && positions[j].y === y) {
shipToRotate = playerShips[i];
shipIndex = i;
break;
}
}
if (shipToRotate) break;
}
}
if (shipToRotate) {
// Rotate this specific ship directly
var currentPositions = shipToRotate.getPositions();
var startX = currentPositions[0].x;
var startY = currentPositions[0].y;
var newHorizontal = !shipToRotate.isHorizontal;
// Temporarily remove ship from current positions to check placement
for (var k = 0; k < currentPositions.length; k++) {
var pos = currentPositions[k];
var tile = playerGrid[pos.x][pos.y];
tile.hasShip = false;
tile.shipId = null;
tile.shipPartIndex = null;
if (tile.shipPartGraphic) {
tile.removeChild(tile.shipPartGraphic);
tile.shipPartGraphic = null;
}
}
// Check if rotation is possible
if (canPlaceShip(playerGrid, startX, startY, shipToRotate.size, newHorizontal)) {
// Place ship with new orientation
placeShip(playerGrid, shipToRotate, startX, startY, newHorizontal);
rotateText.setText('Ship rotated! Current: ' + (newHorizontal ? 'Horizontal' : 'Vertical'));
} else {
// Rotation not possible, restore original placement
placeShip(playerGrid, shipToRotate, startX, startY, shipToRotate.isHorizontal);
rotateText.setText('Cannot rotate - not enough space!');
}
return;
}
} else {
rotateText.setText('Current: Horizontal (Tap ROTATE button to change)');
}
}
function placeAIShips() {
for (var i = 0; i < aiShips.length; i++) {
var ship = aiShips[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(aiGrid, x, y, ship.size, horizontal)) {
placeShip(aiGrid, ship, x, y, horizontal);
placed = true;
}
attempts++;
}
}
}
function startBattlePhase() {
gamePhase = 'battle';
phaseText.setText('Battle Phase');
instructionText.setText('Attack enemy grid!');
rotateText.setText('');
}
function handleAttack(x, y) {
if (gamePhase !== 'battle' || currentPlayer !== 'player') return;
var tile = aiGrid[x][y];
var isHit = tile.hasShip;
tile.markHit();
if (isHit) {
LK.getSound('hit').play();
// Check if ship is sunk
var hitShip = null;
for (var i = 0; i < aiShips.length; i++) {
var ship = aiShips[i];
var positions = ship.getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === x && positions[j].y === y) {
hitShip = ship;
break;
}
}
if (hitShip) break;
}
// Individual part blow up is now handled in markHit method
if (hitShip && hitShip.hit()) {
LK.getSound('sink').play();
// Mark all ship positions as sunk
var positions = hitShip.getPositions();
for (var k = 0; k < positions.length; k++) {
var pos = positions[k];
aiGrid[pos.x][pos.y].markSunk();
}
if (checkVictory(aiShips)) {
gamePhase = 'gameOver';
phaseText.setText('You Win!');
instructionText.setText('All enemy ships destroyed!');
LK.showYouWin();
return;
}
}
// Player hit - give another chance
instructionText.setText('Hit! Attack again!');
} else {
tile.markMiss();
LK.getSound('miss').play();
// Player missed - switch to AI turn
currentPlayer = 'ai';
instructionText.setText('AI is attacking...');
LK.setTimeout(function () {
aiAttack();
}, 1000);
}
}
function aiAttack() {
var validTargets = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (!playerGrid[x][y].isHit) {
validTargets.push({
x: x,
y: y
});
}
}
}
if (validTargets.length === 0) return;
var target = validTargets[Math.floor(Math.random() * validTargets.length)];
var tile = playerGrid[target.x][target.y];
var isHit = tile.hasShip;
tile.markHit();
if (isHit) {
LK.getSound('hit').play();
// Check if ship is sunk
var hitShip = null;
for (var i = 0; i < playerShips.length; i++) {
var ship = playerShips[i];
var positions = ship.getPositions();
for (var j = 0; j < positions.length; j++) {
if (positions[j].x === target.x && positions[j].y === target.y) {
hitShip = ship;
break;
}
}
if (hitShip) break;
}
// Individual part blow up is now handled in markHit method
if (hitShip && hitShip.hit()) {
LK.getSound('sink').play();
// Mark all ship positions as sunk
var positions = hitShip.getPositions();
for (var k = 0; k < positions.length; k++) {
var pos = positions[k];
playerGrid[pos.x][pos.y].markSunk();
}
if (checkVictory(playerShips)) {
gamePhase = 'gameOver';
phaseText.setText('You Lose!');
instructionText.setText('All your ships destroyed!');
LK.showGameOver();
return;
}
}
// AI hit - continue AI turn
instructionText.setText('AI hit! AI attacking again...');
LK.setTimeout(function () {
aiAttack();
}, 1000);
} else {
tile.markMiss();
LK.getSound('miss').play();
// AI missed - switch to player turn
currentPlayer = 'player';
instructionText.setText('Your turn - Attack enemy grid!');
}
}
function checkVictory(ships) {
for (var i = 0; i < ships.length; i++) {
if (!ships[i].isSunk) {
return false;
}
}
return true;
}
// Add global move handler for ship dragging
game.move = function (x, y, obj) {
if (draggedShip) {
draggedShip.x = x - dragOffset.x;
draggedShip.y = y - dragOffset.y;
// Clear previous highlights
for (var gx = 0; gx < GRID_SIZE; gx++) {
for (var gy = 0; gy < GRID_SIZE; gy++) {
var tile = playerGrid[gx][gy];
if (tile.highlight) {
tile.removeChild(tile.highlight);
tile.highlight = null;
}
}
}
// Show placement preview
var gridStartX = 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var gridStartY = 2732 / 2 - GRID_SIZE * TILE_SIZE / 2;
var gridX = Math.floor((draggedShip.x - gridStartX) / TILE_SIZE);
var gridY = Math.floor((draggedShip.y - gridStartY) / TILE_SIZE);
if (gridX >= 0 && gridY >= 0 && gridX < GRID_SIZE && gridY < GRID_SIZE) {
var shipSize = shipDefinitions[draggedShip.shipIndex].size;
var canPlace = canPlaceShip(playerGrid, gridX, gridY, shipSize, draggedShip.isHorizontal);
if (canPlace) {
for (var i = 0; i < shipSize; i++) {
var checkX = draggedShip.isHorizontal ? gridX + i : gridX;
var checkY = draggedShip.isHorizontal ? gridY : gridY + i;
if (checkX < GRID_SIZE && checkY < GRID_SIZE) {
var tile = playerGrid[checkX][checkY];
tile.highlight = tile.attachAsset('placementHighlight', {
anchorX: 0.5,
anchorY: 0.5
});
tile.highlight.alpha = 0.5;
}
}
}
}
}
};
// Add global up handler for ship placement
game.up = function (x, y, obj) {
if (draggedShip) {
// Clear highlights first
for (var gx = 0; gx < GRID_SIZE; gx++) {
for (var gy = 0; gy < GRID_SIZE; gy++) {
var tile = playerGrid[gx][gy];
if (tile.highlight) {
tile.removeChild(tile.highlight);
tile.highlight = null;
}
}
}
// Try to place ship
var gridStartX = 2048 / 4 - GRID_SIZE * TILE_SIZE / 2;
var gridStartY = 2732 / 2 - GRID_SIZE * TILE_SIZE / 2;
var gridX = Math.floor((draggedShip.x - gridStartX) / TILE_SIZE);
var gridY = Math.floor((draggedShip.y - gridStartY) / TILE_SIZE);
var shipSize = shipDefinitions[draggedShip.shipIndex].size;
if (gridX >= 0 && gridY >= 0 && canPlaceShip(playerGrid, gridX, gridY, shipSize, draggedShip.isHorizontal)) {
// Place the ship
var ship = playerShips[draggedShip.shipIndex];
placeShip(playerGrid, ship, gridX, gridY, draggedShip.isHorizontal);
draggedShip.placed = true;
draggedShip.visible = false;
// Check if all ships are placed
var allPlaced = true;
for (var i = 0; i < previewShips.length; i++) {
if (!previewShips[i].placed) {
allPlaced = false;
break;
}
}
if (allPlaced) {
startButton.visible = true;
instructionText.setText('All ships placed! Click START GAME to begin battle');
}
} else {
// Return ship to original position
tween(draggedShip, {
x: draggedShip.originalX,
y: draggedShip.originalY
}, {
duration: 300,
easing: tween.easeOut
});
}
draggedShip = null;
}
};
// Initialize game
initializeGrids();
createShips();
createShipArea();
// Update initial instruction
instructionText.setText('Tap ship to rotate, then drag to place');
rotateText.setText('Current: Horizontal (Tap ROTATE button to change)');
;