User prompt
Please fix the bug: 'TypeError: game.clearAllHighlights is not a function' in or related to this line: 'game.clearAllHighlights();' Line Number: 2061
Code edit (1 edits merged)
Please save this source code
User prompt
fix the error
Code edit (5 edits merged)
Please save this source code
User prompt
Please fix the bug: 'unit is not defined' in or related to this line: 'tween(button, {' Line Number: 753 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'unit is not defined' in or related to this line: 'tween(button, {' Line Number: 753
Code edit (3 edits merged)
Please save this source code
User prompt
now when the player clicks the player vs player button, it should start the match but the ai shouldnt controll any characters, its two players playing at turn
Code edit (5 edits merged)
Please save this source code
User prompt
now create two more ai play pattern, the current one can be for easy mode, create harder ones for medium and hard
User prompt
When the player clicks the player vs ai button it should pop up a small window asking for which difficulty, easy medium or hard, if the player clicks outside that window it closes
Code edit (1 edits merged)
Please save this source code
User prompt
Keep the game logo during the select mode screen
User prompt
make the player vs player button wider in x, and the back button slimmer in x
User prompt
Add an game mode screen, when the player presses the play game button it goes to the game mode screen where it has two buttons, one saying player vs AI and the other Player vs Player, for now neither button does anything next
Code edit (2 edits merged)
Please save this source code
User prompt
solve the issue of characters occupying same position, no character can occupy an currently occupied position, enemies characters are doing that
User prompt
king, knights and mage can jump over characters, move to front of them if is within movement range and its not an occupied position
User prompt
enemy characters are overlapping ocuppied positions, solve that
User prompt
Please fix the bug: 'ReferenceError: Knight is not defined' in or related to this line: 'unit = new Knight().init(type, team, row, col);' Line Number: 735
Code edit (1 edits merged)
Please save this source code
User prompt
Fix 4: Fix endTurn function to prevent skipping AI turn by checking state transitions properly
User prompt
Fix 3: Improve AI wizard strategy to fix adjacent cell access for area attacks
User prompt
Fix 2: Properly initialize AI in game code with game reference to fix AI turns being skipped
User prompt
Fix 1: Improve AIPlayer class by properly passing game reference to avoid method access issues
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AIPlayer = Container.expand(function () { var self = Container.call(this); self.game = null; self.makeMove = function () { if (game.gameState !== 'aiTurn') return false; var difficultyLevel = 1.0; var units = game.getTeamUnits('red'); if (difficultyLevel < 0.3 && Math.random() < 0.7) { var movableUnits = units.filter(function (u) { return u.getPossibleMoves(game.grid).length > 0; }); if (movableUnits.length > 0) { var randomUnit = movableUnits[Math.floor(Math.random() * movableUnits.length)]; var possibleMoves = randomUnit.getPossibleMoves(game.grid); var randomMove = possibleMoves[Math.floor(Math.random() * possibleMoves.length)]; game.selectUnit(randomUnit); game.moveSelectedUnit(randomMove.row, randomMove.col); return true; } } for (var i = 0; i < units.length; i++) { var unit = units[i]; var attacks = unit.getPossibleAttacks(game.grid); if (attacks.length > 0) { var kingAttack = attacks.find(function (a) { return game.grid[a.row][a.col].occupiedBy && game.grid[a.row][a.col].occupiedBy.isKing; }); if (kingAttack) { game.selectUnit(unit); game.attackCell(game.grid[kingAttack.row][kingAttack.col]); return true; } var criticalAttack = attacks.find(function (a) { var target = game.grid[a.row][a.col].occupiedBy; return target && game.isCriticalDamage(unit.type, target.type); }); if (criticalAttack) { game.selectUnit(unit); game.attackCell(game.grid[criticalAttack.row][criticalAttack.col]); return true; } if (unit.type === 'warrior' && attacks.length > 0) { game.selectUnit(unit); game.attackCell(game.grid[attacks[0].row][attacks[0].col]); return true; } if (unit.type === 'wizard' && attacks.length > 0) { var bestAttack = attacks[0], bestCount = 0; for (var j = 0; j < attacks.length; j++) { var attack = attacks[j]; var adjacentCells = game.getAdjacentCells(attack.row, attack.col); var enemyCount = 0; for (var k = 0; k < adjacentCells.length; k++) { var cell = adjacentCells[k]; if (cell.occupied && cell.occupiedBy && cell.occupiedBy.team === 'blue') enemyCount++; } if (enemyCount > bestCount) { bestCount = enemyCount; bestAttack = attack; } } game.selectUnit(unit); game.attackCell(game.grid[bestAttack.row][bestAttack.col]); return true; } game.selectUnit(unit); game.attackCell(game.grid[attacks[0].row][attacks[0].col]); return true; } } var playerKing = game.getKing('blue'); if (playerKing) { var closestUnit = null, closestDistance = Infinity; for (var i = 0; i < units.length; i++) { var unit = units[i]; var moves = unit.getPossibleMoves(game.grid); if (moves.length > 0) { var dist = Math.abs(unit.row - playerKing.row) + Math.abs(unit.col - playerKing.col); if (unit.type === 'knight') dist -= 2;else if (unit.type === 'archer') { if (dist > 4) dist -= 1;else if (dist < 3) dist += 2; } else if (unit.type === 'warrior') dist -= 3;else if (unit.type === 'wizard') { if (dist > 3 && dist < 6) dist -= 2; } if (dist < closestDistance) { closestUnit = unit; closestDistance = dist; } } } if (closestUnit) { var bestMove = null, bestMoveScore = -Infinity; var moves = closestUnit.getPossibleMoves(game.grid); for (var i = 0; i < moves.length; i++) { var move = moves[i]; var moveScore = closestDistance - (Math.abs(move.row - playerKing.row) + Math.abs(move.col - playerKing.col)); var difficultyLevel = 1.0; if (closestUnit.type === 'knight') { var potentialAttackRange = Math.abs(move.row - playerKing.row) + Math.abs(move.col - playerKing.col); if (potentialAttackRange <= closestUnit.attackRange + 1) moveScore += 3 * difficultyLevel; } else if (closestUnit.type === 'archer') { var distAfterMove = Math.abs(move.row - playerKing.row) + Math.abs(move.col - playerKing.col); if (distAfterMove >= 3 && distAfterMove <= 5) moveScore += 4 * difficultyLevel; } else if (closestUnit.type === 'wizard') { var adjacentCells = self.game.getAdjacentCells(move.row, move.col); var playerUnitsNearby = 0; for (var j = 0; j < adjacentCells.length; j++) { var cell = adjacentCells[j]; if (cell.occupied && cell.occupiedBy.team === 'blue') playerUnitsNearby++; } moveScore += playerUnitsNearby * 2; } if (moveScore > bestMoveScore) { bestMove = move; bestMoveScore = moveScore; } } if (bestMove) { game.selectUnit(closestUnit); game.moveSelectedUnit(bestMove.row, bestMove.col); return true; } } } var unitsInDanger = units.filter(function (unit) { var playerUnits = game.getTeamUnits('blue'); for (var i = 0; i < playerUnits.length; i++) { var playerUnit = playerUnits[i]; var rowDiff = Math.abs(playerUnit.row - unit.row); var colDiff = Math.abs(playerUnit.col - unit.col); var inDanger = false; if (playerUnit.type === 'warrior' && rowDiff + colDiff <= playerUnit.moveRange + playerUnit.attackRange) inDanger = true;else if (playerUnit.type === 'archer' && colDiff === 0 && rowDiff <= playerUnit.attackRange + 1) inDanger = true;else if (playerUnit.type === 'knight' && (rowDiff === 0 || colDiff === 0) && rowDiff + colDiff <= playerUnit.attackRange + 1) inDanger = true;else if (playerUnit.type === 'wizard' && rowDiff + colDiff <= playerUnit.moveRange + 2) inDanger = true; if (inDanger && unit.isKing) return true;else if (inDanger && unit.health < 50) return true; } return false; }); if (unitsInDanger.length > 0) { var unitToSave = unitsInDanger.find(function (u) { return u.isKing; }) || unitsInDanger[0]; var moves = unitToSave.getPossibleMoves(game.grid); if (moves.length > 0) { var playerUnits = game.getTeamUnits('blue'); var bestMove = null, bestSafetyScore = -Infinity; for (var i = 0; i < moves.length; i++) { var move = moves[i], safetyScore = 0; for (var j = 0; j < playerUnits.length; j++) { var playerUnit = playerUnits[j]; var dist = Math.abs(move.row - playerUnit.row) + Math.abs(move.col - playerUnit.col); safetyScore += dist; } if (safetyScore > bestSafetyScore) { bestSafetyScore = safetyScore; bestMove = move; } } if (bestMove) { game.selectUnit(unitToSave); game.moveSelectedUnit(bestMove.row, bestMove.col); return true; } } } var movableUnits = units.filter(function (u) { return u.getPossibleMoves(game.grid).length > 0; }); if (movableUnits.length > 0) { var randomUnit = movableUnits[Math.floor(Math.random() * movableUnits.length)]; var possibleMoves = randomUnit.getPossibleMoves(game.grid); var randomMove = possibleMoves[Math.floor(Math.random() * possibleMoves.length)]; game.selectUnit(randomUnit); game.moveSelectedUnit(randomMove.row, randomMove.col); return true; } return false; }; return self; }); var CriticalDamageEffect = Container.expand(function () { var self = Container.call(this); self.init = function (damage, x, y) { var critText = new Text2('CRITICAL HIT!', { size: 80, fill: '#FF0000' }); critText.anchor.set(0.5, 0.5); self.addChild(critText); var damageText = new Text2('-' + damage, { size: 100, fill: '#FFFF00' }); damageText.anchor.set(0.5, 0.5); damageText.y = 80; self.addChild(damageText); self.x = x; self.y = y; self.scale.set(0.1); self.alpha = 0; tween(self, { scaleX: 1.2, scaleY: 1.2, alpha: 1 }, { duration: 300, easing: tween.elasticOut }); LK.setTimeout(function () { tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0, y: self.y - 100 }, { duration: 700, easing: tween.easeOutQuad, onFinish: function onFinish() { self.parent.removeChild(self); } }); }, 1200); return self; }; return self; }); var GridCell = Container.expand(function () { var self = Container.call(this); self.row = 0; self.col = 0; self.occupied = false; self.occupiedBy = null; self.highlighted = false; self.highlightType = null; var cellGraphic = self.attachAsset('grid_cell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); self.highlight = function (type) { if (self.highlightAsset) self.removeChild(self.highlightAsset); self.highlighted = true; self.highlightType = type; var asset = null; if (type === 'move') asset = 'highlight_move';else if (type === 'attack') asset = 'highlight_attack';else if (type === 'selected') asset = 'highlight_selected'; if (asset) { self.highlightAsset = self.attachAsset(asset, { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); } }; self.clearHighlight = function () { if (self.highlightAsset) { self.removeChild(self.highlightAsset); self.highlightAsset = null; } self.highlighted = false; self.highlightType = null; }; self.down = function () { if (game.gameState === 'playerTurn') game.handleCellClick(self); }; return self; }); var SettingsScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SETTINGS', { size: 150, fill: '#FFFFFF' }); titleText.anchor.set(0.5, 0); titleText.y = -2732 / 3; self.addChild(titleText); var createToggle = function createToggle(text, yPos, initialState, xPos) { var container = new Container(); container.y = yPos; container.x = xPos; var label = new Text2(text, { size: 80, fill: '#FFFFFF' }); label.anchor.set(0, 0.5); container.addChild(label); var toggleBg = LK.getAsset('grid_cell_dark', { anchorX: 0.5, anchorY: 0.5, width: 200, height: 100, alpha: 0.8 }); toggleBg.x = 900; container.addChild(toggleBg); var indicator = LK.getAsset('grid_cell', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); indicator.x = initialState ? 950 : 850; indicator.y = 0; container.addChild(indicator); container.state = initialState; toggleBg.down = function () { container.state = !container.state; tween(indicator, { x: container.state ? 950 : 850 }, { duration: 200, easing: tween.easeOutQuad }); }; return container; }; var musicToggle = createToggle('Music', 550, true, -500); self.addChild(musicToggle); var backButton = new Container(); var backBg = LK.getAsset('grid_cell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8, width: 500, height: 120 }); var backText = new Text2('BACK', { size: 80, fill: '#000000' }); backText.anchor.set(0.5, 0.5); backButton.addChild(backBg); backButton.addChild(backText); backButton.x = 0; backButton.y = musicToggle.y + 200; self.addChild(backButton); backButton.down = function () { tween(backBg, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOutQuad }); }; function animateBackButton() { tween(backBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(backBg, { scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeInOut, onFinish: animateBackButton }); } }); } animateBackButton(); backButton.up = function () { tween(backBg, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { game.showTitleScreen(); } }); }; return self; }); var TitleScreen = Container.expand(function () { var self = Container.call(this); var logo = LK.getAsset('game_logo', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -200 }); self.addChild(logo); var titleText = new Text2('', { size: 200, fill: '#FFFFFF' }); self.addChild(titleText); function createButton(text, yOffset) { var button = new Container(); var bg = LK.getAsset('grid_cell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8, width: 800, height: 150 }); button.addChild(bg); var buttonText = new Text2(text, { size: 100, fill: '#000000' }); buttonText.anchor.set(0.5, 0.5); button.addChild(buttonText); button.y = yOffset; button.down = function () { tween(bg, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOutQuad }); }; button.up = function () { tween(bg, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut }); }; return button; } var playButton = createButton('PLAY GAME', 100); var tutorialButton = createButton('TUTORIAL', 300); var settingsButton = createButton('SETTINGS', 500); self.addChild(playButton); self.addChild(tutorialButton); self.addChild(settingsButton); function animateButton(button, delay) { LK.setTimeout(function () { tween(button, { scaleX: 1.1, scaleY: 1.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(button, { scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { animateButton(button, 0); } }); } }); }, delay); } animateButton(playButton, 0); animateButton(tutorialButton, 300); animateButton(settingsButton, 600); playButton.down = function () { tween(playButton.children[0], { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOutQuad }); }; playButton.up = function () { tween(playButton.children[0], { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { game.startGame(); } }); }; tutorialButton.down = function () { tween(tutorialButton.children[0], { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOutQuad }); }; tutorialButton.up = function () { tween(tutorialButton.children[0], { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { game.showTutorial(); } }); }; settingsButton.down = function () { tween(settingsButton.children[0], { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOutQuad }); }; settingsButton.up = function () { tween(settingsButton.children[0], { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { game.showSettings(); } }); }; return self; }); // Base Unit class var Unit = Container.expand(function () { var self = Container.call(this); self.init = function (type, team, row, col) { self.type = type; self.team = team; self.row = row; self.col = col; self.health = 100; self.attackStrength = 30; self.moveRange = 2; self.attackRange = 1; self.selected = false; self.isKing = false; self.isAreaAttacker = false; self.hasInstantKill = false; var assetId = type + '_' + team; self.unitGraphic = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, alpha: 1.0 }); return self; }; self.moveTo = function (row, col) { self.row = row; self.col = col; }; self.select = function () { self.selected = true; }; self.deselect = function () { self.selected = false; }; self.getPossibleMoves = function (grid) { var moves = []; var directions = [{ r: -1, c: 0 }, { r: 1, c: 0 }, { r: 0, c: -1 }, { r: 0, c: 1 }]; for (var i = 0; i < directions.length; i++) { for (var range = 1; range <= self.moveRange; range++) { var newRow = self.row + directions[i].r * range; var newCol = self.col + directions[i].c * range; if (newRow >= 0 && newRow < grid.length && newCol >= 0 && newCol < grid[0].length) { if (!grid[newRow][newCol].occupied) { moves.push({ row: newRow, col: newCol }); } else { break; // Stop checking in this direction if occupied } } } } return moves; }; self.getPossibleAttacks = function (grid) { var attacks = []; for (var r = Math.max(0, self.row - self.attackRange); r <= Math.min(grid.length - 1, self.row + self.attackRange); r++) { for (var c = Math.max(0, self.col - self.attackRange); c <= Math.min(grid[0].length - 1, self.col + self.attackRange); c++) { var rowDiff = Math.abs(r - self.row); var colDiff = Math.abs(c - self.col); if (rowDiff + colDiff <= self.attackRange && !(r === self.row && c === self.col)) { if (grid[r][c].occupied && grid[r][c].occupiedBy.team !== self.team) { attacks.push({ row: r, col: c }); } } } } return attacks; }; return self; }); // Wizard class var Wizard = Unit.expand(function () { var self = Unit.call(this); var baseInit = self.init; self.init = function (type, team, row, col) { baseInit.call(self, type, team, row, col); self.moveRange = 2; self.attackRange = 2; self.attackStrength = 20; self.isAreaAttacker = true; return self; }; var baseMoves = self.getPossibleMoves; self.getPossibleMoves = function (grid) { var moves = baseMoves.call(self, grid); var directions = [{ r: -1, c: 0 }, { r: 1, c: 0 }, { r: 0, c: -1 }, { r: 0, c: 1 }]; // Check for jump moves for (var i = 0; i < directions.length; i++) { for (var range = 1; range <= self.moveRange; range++) { var jumpRow = self.row + directions[i].r * range; var jumpCol = self.col + directions[i].c * range; // If there's an occupied cell in our path if (jumpRow >= 0 && jumpRow < grid.length && jumpCol >= 0 && jumpCol < grid[0].length && grid[jumpRow][jumpCol].occupied) { // Check if we can jump over to the next cell var landRow = jumpRow + directions[i].r; var landCol = jumpCol + directions[i].c; // If the landing spot is valid and unoccupied and within movement range, add it as a move if (landRow >= 0 && landRow < grid.length && landCol >= 0 && landCol < grid[0].length && !grid[landRow][landCol].occupied && Math.abs(landRow - self.row) + Math.abs(landCol - self.col) <= self.moveRange + 1) { moves.push({ row: landRow, col: landCol }); } break; // Stop checking in this direction after finding an occupied cell } } } return moves; }; return self; }); // Warrior class var Warrior = Unit.expand(function () { var self = Unit.call(this); var baseInit = self.init; self.init = function (type, team, row, col) { baseInit.call(self, type, team, row, col); self.moveRange = 1; self.attackRange = 1; self.attackStrength = 40; self.hasInstantKill = true; return self; }; return self; }); // Knight class var Knight = Unit.expand(function () { var self = Unit.call(this); var baseInit = self.init; self.init = function (type, team, row, col) { baseInit.call(self, type, team, row, col); self.moveRange = 3; self.attackRange = 1; self.attackStrength = 35; return self; }; var baseMoves = self.getPossibleMoves; self.getPossibleMoves = function (grid) { var moves = baseMoves.call(self, grid); var directions = [{ r: -1, c: 0 }, { r: 1, c: 0 }, { r: 0, c: -1 }, { r: 0, c: 1 }]; // Check for jump moves for (var i = 0; i < directions.length; i++) { for (var range = 1; range <= self.moveRange; range++) { var jumpRow = self.row + directions[i].r * range; var jumpCol = self.col + directions[i].c * range; // If there's an occupied cell in our path if (jumpRow >= 0 && jumpRow < grid.length && jumpCol >= 0 && jumpCol < grid[0].length && grid[jumpRow][jumpCol].occupied) { // Check if we can jump over to the next cell var landRow = jumpRow + directions[i].r; var landCol = jumpCol + directions[i].c; // If the landing spot is valid and unoccupied, add it as a move if (landRow >= 0 && landRow < grid.length && landCol >= 0 && landCol < grid[0].length && !grid[landRow][landCol].occupied) { moves.push({ row: landRow, col: landCol }); } break; // Stop checking in this direction after finding an occupied cell } } } return moves; }; var basePossibleAttacks = self.getPossibleAttacks; self.getPossibleAttacks = function (grid) { // Knights can only attack orthogonally (up, down, left, right) var attacks = []; var directions = [{ r: -1, c: 0 }, { r: 1, c: 0 }, { r: 0, c: -1 }, { r: 0, c: 1 }]; for (var i = 0; i < directions.length; i++) { var r = self.row + directions[i].r; var c = self.col + directions[i].c; if (r >= 0 && r < grid.length && c >= 0 && c < grid[0].length) { if (grid[r][c].occupied && grid[r][c].occupiedBy.team !== self.team) { attacks.push({ row: r, col: c }); } } } return attacks; }; return self; }); // King class var King = Unit.expand(function () { var self = Unit.call(this); var baseInit = self.init; self.init = function (type, team, row, col) { baseInit.call(self, type, team, row, col); self.moveRange = 1; self.attackRange = 1; self.attackStrength = 30; self.health = 150; self.isKing = true; return self; }; var baseMoves = self.getPossibleMoves; self.getPossibleMoves = function (grid) { var moves = baseMoves.call(self, grid); var directions = [{ r: -1, c: 0 }, { r: 1, c: 0 }, { r: 0, c: -1 }, { r: 0, c: 1 }]; // Check for jump moves for (var i = 0; i < directions.length; i++) { var jumpRow = self.row + directions[i].r; var jumpCol = self.col + directions[i].c; // If there's an occupied cell in our path if (jumpRow >= 0 && jumpRow < grid.length && jumpCol >= 0 && jumpCol < grid[0].length && grid[jumpRow][jumpCol].occupied) { // Check if we can jump over to the next cell var landRow = jumpRow + directions[i].r; var landCol = jumpCol + directions[i].c; // If the landing spot is valid and unoccupied, add it as a move if (landRow >= 0 && landRow < grid.length && landCol >= 0 && landCol < grid[0].length && !grid[landRow][landCol].occupied) { moves.push({ row: landRow, col: landCol }); } } } return moves; }; return self; }); // Archer class var Archer = Unit.expand(function () { var self = Unit.call(this); var baseInit = self.init; self.init = function (type, team, row, col) { baseInit.call(self, type, team, row, col); self.moveRange = 2; self.attackRange = 3; self.attackStrength = 25; return self; }; var basePossibleAttacks = self.getPossibleAttacks; self.getPossibleAttacks = function (grid) { // Archers can attack in straight lines only var attacks = []; var directions = [{ r: -1, c: 0 }, { r: 1, c: 0 }, { r: 0, c: -1 }, { r: 0, c: 1 }]; for (var i = 0; i < directions.length; i++) { for (var range = 1; range <= self.attackRange; range++) { var r = self.row + directions[i].r * range; var c = self.col + directions[i].c * range; if (r >= 0 && r < grid.length && c >= 0 && c < grid[0].length) { if (grid[r][c].occupied) { if (grid[r][c].occupiedBy.team !== self.team) { attacks.push({ row: r, col: c }); } break; // Stop checking in this direction if any unit found } } } } return attacks; }; return self; }); // Vector2 class var Vector2 = Container.expand(function () { var self = Container.call(this); self.init = function (x, y) { self.x = x || 0; self.y = y || 0; return self; }; self.add = function (v) { return new Vector2().init(self.x + v.x, self.y + v.y); }; self.subtract = function (v) { return new Vector2().init(self.x - v.x, self.y - v.y); }; self.multiply = function (scalar) { return new Vector2().init(self.x * scalar, self.y * scalar); }; self.clone = function () { return new Vector2().init(self.x, self.y); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ var tutorialOffset = new Vector2().init(0, 0); var dragNode = null; var GRID_ROWS = 10; var GRID_COLS = 5; var CELL_SIZE = 250; var GRID_PADDING_X = (2048 - GRID_COLS * CELL_SIZE) / 2; var GRID_PADDING_Y = (2732 - GRID_ROWS * CELL_SIZE) / 2; var grid = []; var units = []; var selectedUnit = null; var gameState = 'mainMenu'; var ai = new AIPlayer(); ai.game = game; // Set game reference for AI var statusText = new Text2('Player Turn', { size: 120, fill: 0xFFFFFF }); var titleScreen = new TitleScreen(); var tutorialScreen = new Container(); // Placeholder, implement as needed var settingsScreen = new SettingsScreen(); game.settingsScreen = settingsScreen; game.move = function (x, y, obj) { if (dragNode) { dragNode.x = x; dragNode.y = y; } }; function handleMove(x, y, obj) { if (dragNode) { dragNode.x = x; dragNode.y = y; } } game.down = function (x, y, obj) { if (typeof character !== 'undefined') { dragNode = character; } else { dragNode = null; } if (obj && obj.down) obj.__isTouchDown = true; if (handleMove) handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; titleScreen.x = 1024; titleScreen.y = 1366; tutorialScreen.x = 0; tutorialScreen.y = 0; settingsScreen.x = 1024; settingsScreen.y = 1366; function initializeGame() { for (var row = 0; row < GRID_ROWS; row++) { grid[row] = []; for (var col = 0; col < GRID_COLS; col++) { grid[row][col] = {}; } } var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChild(background); game.showTitleScreen(); } function createUnits() { createUnit('knight', 'blue', 8, 0); createUnit('warrior', 'blue', 8, 1); createUnit('warrior', 'blue', 8, 2); createUnit('warrior', 'blue', 8, 3); createUnit('knight', 'blue', 8, 4); createUnit('archer', 'blue', 9, 0); createUnit('wizard', 'blue', 9, 1); createUnit('king', 'blue', 9, 2); createUnit('wizard', 'blue', 9, 3); createUnit('archer', 'blue', 9, 4); createUnit('knight', 'red', 1, 0); createUnit('warrior', 'red', 1, 1); createUnit('warrior', 'red', 1, 2); createUnit('warrior', 'red', 1, 3); createUnit('knight', 'red', 1, 4); createUnit('archer', 'red', 0, 0); createUnit('wizard', 'red', 0, 1); createUnit('king', 'red', 0, 2); createUnit('wizard', 'red', 0, 3); createUnit('archer', 'red', 0, 4); } function createUnit(type, team, row, col) { // Check if position is already occupied if (grid[row] && grid[row][col] && grid[row][col].occupied) { console.log("Position " + row + "," + col + " already occupied, finding alternate position"); // Find nearest unoccupied position var foundPosition = false; for (var r = 0; r < GRID_ROWS; r++) { for (var c = 0; c < GRID_COLS; c++) { // Only place units on their respective sides of the board if (!grid[r][c].occupied && (team === 'blue' && r >= GRID_ROWS / 2 || team === 'red' && r < GRID_ROWS / 2)) { row = r; col = c; foundPosition = true; break; } } if (foundPosition) break; } // If still no position found, try anywhere on the board if (!foundPosition) { for (var r = 0; r < GRID_ROWS; r++) { for (var c = 0; c < GRID_COLS; c++) { if (!grid[r][c].occupied) { row = r; col = c; foundPosition = true; break; } } if (foundPosition) break; } } if (!foundPosition) { console.log("Cannot place unit: all positions occupied"); return null; // Return null if no position can be found } } var unit; switch (type) { case 'king': unit = new King().init(type, team, row, col); break; case 'knight': unit = new Knight().init(type, team, row, col); break; case 'archer': unit = new Archer().init(type, team, row, col); break; case 'wizard': unit = new Wizard().init(type, team, row, col); break; case 'warrior': unit = new Warrior().init(type, team, row, col); break; } unit.x = grid[row][col].x; unit.y = grid[row][col].y; unit.unitGraphic.alpha = 0.8; unit.healthText = new Text2(unit.health.toString(), { size: 50, fill: 0xFFD700 }); unit.healthText.anchor.set(0.5, 0.5); unit.healthText.y = unit.unitGraphic.height / 2 + 10; unit.addChild(unit.healthText); unit.unitGraphic.scale.set(0.1); unit.unitGraphic.alpha = 0.3; game.addChild(unit); units.push(unit); grid[row][col].occupied = true; grid[row][col].occupiedBy = unit; if (team === 'blue') { unit.down = function (x, y, obj) { if (game.gameState === 'playerTurn') selectUnit(unit); }; } tween(unit.unitGraphic, { scaleX: 1.0, scaleY: 1.0, alpha: 0.8 }, { duration: 600, easing: tween.elasticOut, delay: 100 * (row + col) % 5 }); return unit; } game.handleCellClick = function (cell) { if (!selectedUnit || game.gameState !== 'playerTurn') return; if (cell.highlighted && cell.highlightType === 'move') moveSelectedUnit(cell.row, cell.col);else if (cell.highlighted && cell.highlightType === 'attack') attackCell(cell); }; function selectUnit(unit) { if (unit.team !== 'blue' && gameState === 'playerTurn') return; if (unit.team !== 'red' && gameState === 'aiTurn') return; if (selectedUnit) { selectedUnit.deselect(); clearAllHighlights(); } selectedUnit = unit; selectedUnit.select(); tween(selectedUnit.unitGraphic, { scaleX: 1.2, scaleY: 1.2, alpha: 1.0 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { tween(selectedUnit.unitGraphic, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200, easing: tween.elasticOut }); } }); grid[unit.row][unit.col].highlight('selected'); var possibleMoves = unit.getPossibleMoves(grid); for (var i = 0; i < possibleMoves.length; i++) { (function (index) { LK.setTimeout(function () { if (selectedUnit === unit) { var move = possibleMoves[index]; grid[move.row][move.col].highlight('move'); } }, index * 30); })(i); } var possibleAttacks = unit.getPossibleAttacks(grid); for (var i = 0; i < possibleAttacks.length; i++) { (function (index) { LK.setTimeout(function () { if (selectedUnit === unit) { var attack = possibleAttacks[index]; grid[attack.row][attack.col].highlight('attack'); } }, (possibleMoves.length + index) * 30); })(i); } } function moveSelectedUnit(targetRow, targetCol) { if (!selectedUnit) return; grid[selectedUnit.row][selectedUnit.col].occupied = false; grid[selectedUnit.row][selectedUnit.col].occupiedBy = null; selectedUnit.moveTo(targetRow, targetCol); var targetX = grid[targetRow][targetCol].x; var targetY = grid[targetRow][targetCol].y; tween(selectedUnit, { x: targetX, y: targetY }, { duration: 300, easing: tween.easeOutQuad, onFinish: function onFinish() { grid[targetRow][targetCol].occupied = true; grid[targetRow][targetCol].occupiedBy = selectedUnit; selectedUnit.deselect(); clearAllHighlights(); selectedUnit = null; checkForAutoAttack(); endTurn(); } }); } function attackCell(cell) { if (!selectedUnit || !cell.occupied || cell.occupiedBy.team === selectedUnit.team) return; var targetUnit = cell.occupiedBy; var originalX = selectedUnit.x, originalY = selectedUnit.y; var directionX = targetUnit.x - selectedUnit.x, directionY = targetUnit.y - selectedUnit.y; var distance = Math.sqrt(directionX * directionX + directionY * directionY); var normalizedX = directionX / distance * 30, normalizedY = directionY / distance * 30; tween(selectedUnit, { x: selectedUnit.x + normalizedX * 1.5, y: selectedUnit.y + normalizedY * 1.5 }, { duration: 200, easing: tween.easeOutQuad, onFinish: function onFinish() { LK.effects.flashObject(targetUnit, 0xff0000, 400); tween(selectedUnit, { x: originalX, y: originalY }, { duration: 200, easing: tween.easeOutQuad, onFinish: function onFinish() { var isCritical = isCriticalDamage(selectedUnit.type, targetUnit.type); var damageMultiplier = isCritical ? getCriticalDamageMultiplier() : 1.0; var actualDamage = Math.floor(selectedUnit.attackStrength * damageMultiplier); if (selectedUnit.hasInstantKill && !targetUnit.isKing) { removeUnit(targetUnit); } else if (targetUnit.isKing) { if (selectedUnit.type === 'warrior') { removeUnit(targetUnit); } else { var isCritical = isCriticalDamage(selectedUnit.type, targetUnit.type); var damageMultiplier = isCritical ? getCriticalDamageMultiplier() : 1.0; var actualDamage = Math.floor(selectedUnit.attackStrength * damageMultiplier); targetUnit.health -= actualDamage; targetUnit.healthText.setText(targetUnit.health.toString()); if (targetUnit.health <= 0) removeUnit(targetUnit); } } else if (selectedUnit.isAreaAttacker) { var adjacentCells = getAdjacentCells(cell.row, cell.col); targetUnit.health -= actualDamage; targetUnit.healthText.setText(targetUnit.health.toString()); if (isCritical) { var critEffect = new CriticalDamageEffect().init(actualDamage, targetUnit.x, targetUnit.y - 50); game.addChild(critEffect); tween(targetUnit, { scaleX: 0.8, scaleY: 0.8, rotation: 0.2 }, { duration: 100, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(targetUnit, { scaleX: 1.0, scaleY: 1.0, rotation: 0 }, { duration: 200, easing: tween.elasticOut }); } }); } if (targetUnit.health <= 0) removeUnit(targetUnit); for (var i = 0; i < adjacentCells.length; i++) { var adjCell = adjacentCells[i]; if (adjCell.occupied && adjCell.occupiedBy.team !== selectedUnit.team) { var adjTargetUnit = adjCell.occupiedBy; var adjDamage = Math.floor(actualDamage / 2); adjTargetUnit.health -= adjDamage; adjTargetUnit.healthText.setText(adjTargetUnit.health.toString()); if (adjTargetUnit.health <= 0) removeUnit(adjTargetUnit); } } } else { targetUnit.health -= actualDamage; targetUnit.healthText.setText(targetUnit.health.toString()); if (isCritical) { var critEffect = new CriticalDamageEffect().init(actualDamage, targetUnit.x, targetUnit.y - 50); game.addChild(critEffect); tween(targetUnit, { scaleX: 0.7, scaleY: 0.7, rotation: 0.3 }, { duration: 100, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(targetUnit, { scaleX: 1.0, scaleY: 1.0, rotation: 0 }, { duration: 300, easing: tween.elasticOut }); } }); } if (targetUnit.health <= 0) removeUnit(targetUnit); } selectedUnit.deselect(); clearAllHighlights(); selectedUnit = null; endTurn(); } }); } }); } function checkForAutoAttack() {} function getAdjacentCells(row, col) { var adjacent = []; var directions = [{ r: -1, c: 0 }, { r: 1, c: 0 }, { r: 0, c: -1 }, { r: 0, c: 1 }, { r: -1, c: -1 }, { r: -1, c: 1 }, { r: 1, c: -1 }, { r: 1, c: 1 }]; for (var i = 0; i < directions.length; i++) { var newRow = row + directions[i].r, newCol = col + directions[i].c; if (newRow >= 0 && newRow < GRID_ROWS && newCol >= 0 && newCol < GRID_COLS) { adjacent.push(grid[newRow][newCol]); } } return adjacent; } function removeUnit(unit) { grid[unit.row][unit.col].occupied = false; grid[unit.row][unit.col].occupiedBy = null; var index = units.indexOf(unit); if (index > -1) units.splice(index, 1); game.removeChild(unit); if (unit.isKing) { if (unit.team === 'blue') endGame('red');else endGame('blue'); } } function clearAllHighlights() { for (var row = 0; row < GRID_ROWS; row++) { for (var col = 0; col < GRID_COLS; col++) { grid[row][col].clearHighlight(); } } } function endTurn() { var currentState = gameState; if (currentState === 'playerTurn') { gameState = 'aiTurn'; statusText.setText('AI Turn'); LK.setTimeout(function () { if (gameState === 'aiTurn') { var moveSuccessful = ai.makeMove(); if (!moveSuccessful) { gameState = 'playerTurn'; statusText.setText('Player Turn'); } } }, 500); } else if (currentState === 'aiTurn') { gameState = 'playerTurn'; statusText.setText('Player Turn'); } } function endGame(winner) { gameState = 'gameOver'; if (winner === 'blue') { statusText.setText('You Win!'); LK.showYouWin(); } else { statusText.setText('Game Over'); LK.showGameOver(); } } function isCriticalDamage(attackerType, targetType) { if (attackerType === 'knight' && targetType === 'archer') return true; if (attackerType === 'archer' && targetType === 'wizard') return true; if (attackerType === 'wizard' && targetType === 'knight') return true; return false; } game.isCriticalDamage = isCriticalDamage; function getCriticalDamageMultiplier() { return 2.0; } game.getTeamUnits = function (team) { return units.filter(function (unit) { return unit.team === team; }); }; game.getKing = function (team) { for (var i = 0; i < units.length; i++) { if (units[i].isKing && units[i].team === team) return units[i]; } return null; }; game.selectUnit = selectUnit; game.moveSelectedUnit = moveSelectedUnit; game.attackCell = attackCell; game.grid = grid; game.gameState = gameState; game.getAdjacentCells = getAdjacentCells; Object.defineProperty(game, 'gameState', { get: function get() { return gameState; }, set: function set(value) { gameState = value; } }); game.updateTutorialPosition = function (x, y) { tutorialOffset.x = x || tutorialOffset.x; tutorialOffset.y = y || tutorialOffset.y; if (tutorialScreen && tutorialScreen.parent) { tutorialScreen.scrollContainer.x = 1024 + tutorialOffset.x; tutorialScreen.scrollContainer.y = -tutorialScreen.scrollY + tutorialOffset.y; var backButton = tutorialScreen.children[tutorialScreen.children.length - 2]; backButton.x = 200 + tutorialOffset.x; backButton.y = 2500 + tutorialOffset.y; var scrollIndicator = tutorialScreen.children[tutorialScreen.children.length - 1]; scrollIndicator.x = 1950 + tutorialOffset.x; scrollIndicator.y = 1366 + tutorialOffset.y; } }; game.showTitleScreen = function () { game.removeAllGameElements(); game.addChild(titleScreen); game.removeChild(tutorialScreen); game.removeChild(settingsScreen); statusText.visible = false; gameState = 'mainMenu'; var logo = titleScreen.children[0]; var titleText = titleScreen.children[1]; tween(logo, { y: -500 }, { duration: 800, easing: tween.elasticOut }); tween(titleText, { y: 800 }, { duration: 800, easing: tween.elasticOut }); }; game.showTutorial = function () { game.removeChild(titleScreen); game.addChild(tutorialScreen); game.removeChild(settingsScreen); gameState = 'tutorial'; }; game.showSettings = function () { game.removeChild(titleScreen); game.removeChild(tutorialScreen); game.addChild(settingsScreen); gameState = 'settings'; }; game.startGame = function () { game.removeChild(titleScreen); game.removeChild(tutorialScreen); game.removeChild(settingsScreen); for (var row = 0; row < GRID_ROWS; row++) { grid[row] = []; for (var col = 0; col < GRID_COLS; col++) { var cell = new GridCell(); cell.row = row; cell.col = col; cell.x = GRID_PADDING_X + col * CELL_SIZE + CELL_SIZE / 2; cell.y = GRID_PADDING_Y + row * CELL_SIZE + CELL_SIZE / 2; if ((row + col) % 2 === 0) { cell.removeChild(cell.children[0]); cell.attachAsset('grid_cell_dark', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); } grid[row][col] = cell; game.addChild(cell); } } createUnits(); statusText.visible = true; statusText.setText('Player Turn'); gameState = 'playerTurn'; }; game.removeAllGameElements = function () { for (var row = 0; row < grid.length; row++) { for (var col = 0; col < grid[row].length; col++) { if (grid[row][col]) game.removeChild(grid[row][col]); } } for (var i = 0; i < units.length; i++) { game.removeChild(units[i]); } grid = []; units = []; selectedUnit = null; }; initializeGame();
===================================================================
--- original.js
+++ change.js
@@ -56,13 +56,13 @@
var bestAttack = attacks[0],
bestCount = 0;
for (var j = 0; j < attacks.length; j++) {
var attack = attacks[j];
- var adjacentCells = self.game.getAdjacentCells(attack.row, attack.col);
+ var adjacentCells = game.getAdjacentCells(attack.row, attack.col);
var enemyCount = 0;
for (var k = 0; k < adjacentCells.length; k++) {
var cell = adjacentCells[k];
- if (cell.occupied && cell.occupiedBy.team === 'blue') enemyCount++;
+ if (cell.occupied && cell.occupiedBy && cell.occupiedBy.team === 'blue') enemyCount++;
}
if (enemyCount > bestCount) {
bestCount = enemyCount;
bestAttack = attack;
@@ -915,9 +915,9 @@
var units = [];
var selectedUnit = null;
var gameState = 'mainMenu';
var ai = new AIPlayer();
-ai.game = game;
+ai.game = game; // Set game reference for AI
var statusText = new Text2('Player Turn', {
size: 120,
fill: 0xFFFFFF
});
@@ -999,18 +999,39 @@
// Check if position is already occupied
if (grid[row] && grid[row][col] && grid[row][col].occupied) {
console.log("Position " + row + "," + col + " already occupied, finding alternate position");
// Find nearest unoccupied position
+ var foundPosition = false;
for (var r = 0; r < GRID_ROWS; r++) {
for (var c = 0; c < GRID_COLS; c++) {
+ // Only place units on their respective sides of the board
if (!grid[r][c].occupied && (team === 'blue' && r >= GRID_ROWS / 2 || team === 'red' && r < GRID_ROWS / 2)) {
row = r;
col = c;
+ foundPosition = true;
break;
}
}
- if (grid[row][col] && !grid[row][col].occupied) break;
+ if (foundPosition) break;
}
+ // If still no position found, try anywhere on the board
+ if (!foundPosition) {
+ for (var r = 0; r < GRID_ROWS; r++) {
+ for (var c = 0; c < GRID_COLS; c++) {
+ if (!grid[r][c].occupied) {
+ row = r;
+ col = c;
+ foundPosition = true;
+ break;
+ }
+ }
+ if (foundPosition) break;
+ }
+ }
+ if (!foundPosition) {
+ console.log("Cannot place unit: all positions occupied");
+ return null; // Return null if no position can be found
+ }
}
var unit;
switch (type) {
case 'king':
@@ -1313,9 +1334,10 @@
gameState = 'aiTurn';
statusText.setText('AI Turn');
LK.setTimeout(function () {
if (gameState === 'aiTurn') {
- if (ai.makeMove()) {} else {
+ var moveSuccessful = ai.makeMove();
+ if (!moveSuccessful) {
gameState = 'playerTurn';
statusText.setText('Player Turn');
}
}
top view
make it have red-ish clothes
Make it seen from behind top view
make it seen from behind top view
make it just a grass field, and make it from 3 times far away
Create a logo for this game based on this description: Title: "Tactical Kings: Battle of Champions" A fantasy-themed, chess-inspired strategy game where players control unique units with predefined attack zones. Victory comes from eliminating the opponent’s King by strategically positioning characters on a 5x10 grid.. In-Game asset. 2d. High contrast. No shadows
make it wear more blue ish colors
make it from top view
paint the word silver
top view, seeing the back of the character
top down view, do not cut any elements off screen
Make it seen from his back, do not cut any elements off screen