/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Attack class (for both player and enemy) var Attack = Container.expand(function () { var self = Container.call(this); // type: 'basic', 'special', 'enemy' self.type = 'basic'; self.owner = null; // player or enemy self.dmg = 10; self.speed = 30; self.radius = 30; self.dirY = -1; // -1 up, 1 down // Attach asset self.setType = function (type) { self.type = type; if (type === 'basic') { self.removeChildren(); self.attachAsset('attack_basic', { anchorX: 0.5, anchorY: 0.5 }); self.dmg = 10; self.speed = 36; self.radius = 30; self.dirY = -1; } else if (type === 'special') { self.removeChildren(); self.attachAsset('attack_special', { anchorX: 0.5, anchorY: 0.5 }); self.dmg = 22; self.speed = 24; self.radius = 40; self.dirY = -1; } else if (type === 'enemy') { self.removeChildren(); self.attachAsset('attack_enemy', { anchorX: 0.5, anchorY: 0.5 }); self.dmg = 12; self.speed = 28; self.radius = 30; self.dirY = 1; } }; // Update self.update = function () { self.y += self.speed * self.dirY; }; return self; }); // Enemy Pokemon class (AI) var EnemyPokemon = Container.expand(function () { var self = Container.call(this); // Attach enemy asset var sprite = self.attachAsset('poke_enemy', { anchorX: 0.5, anchorY: 0.5 }); // Stats self.maxHP = 100; self.hp = 100; self.speed = 12; // px per move self.radius = sprite.width / 2; // For attack cooldowns self.basicCooldown = 0; self.specialCooldown = 0; // For invulnerability after hit self.invuln = 0; // Move to (x, y) with bounds self.moveTo = function (x, y) { // Clamp to arena bounds (leave 20px margin) var minX = 20 + self.radius; var maxX = 2048 - 20 - self.radius; var minY = 20 + self.radius; var maxY = 1366 - 20 - self.radius; // top half self.x = Math.max(minX, Math.min(maxX, x)); self.y = Math.max(minY, Math.min(maxY, y)); }; // Take damage self.takeDamage = function (dmg) { if (self.invuln > 0) return; self.hp -= dmg; if (self.hp < 0) self.hp = 0; self.invuln = 30; // 0.5s invuln LK.effects.flashObject(self, 0xffffff, 200); }; return self; }); // Player Pokemon class var PlayerPokemon = Container.expand(function () { var self = Container.call(this); // Attach player asset var sprite = self.attachAsset('poke_player', { anchorX: 0.5, anchorY: 0.5 }); // Stats self.maxHP = 100; self.hp = 100; self.speed = 18; // px per move self.radius = sprite.width / 2; // For drag self.isDragging = false; // For attack cooldowns self.basicCooldown = 0; self.specialCooldown = 0; // For invulnerability after hit self.invuln = 0; // Move to (x, y) with bounds self.moveTo = function (x, y) { // Clamp to arena bounds (leave 20px margin) var minX = 20 + self.radius; var maxX = 2048 - 20 - self.radius; var minY = 1366 + 20 + self.radius; // bottom half var maxY = 2732 - 20 - self.radius; self.x = Math.max(minX, Math.min(maxX, x)); self.y = Math.max(minY, Math.min(maxY, y)); }; // Take damage self.takeDamage = function (dmg) { if (self.invuln > 0) return; self.hp -= dmg; if (self.hp < 0) self.hp = 0; self.invuln = 30; // 0.5s invuln LK.effects.flashObject(self, 0xffffff, 200); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf5f5f5 }); /**** * Game Code ****/ // Attack SFX // Pokemon shapes (simple for MVP) // Arena background var arenaBg = LK.getAsset('arena_bg', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(arenaBg); // Player and enemy var player = new PlayerPokemon(); var enemy = new EnemyPokemon(); game.addChild(player); game.addChild(enemy); // Initial positions player.moveTo(2048 / 2, 2732 - 300); enemy.moveTo(2048 / 2, 300); // HP bars var playerHpBg = LK.getAsset('hp_bar_bg', { anchorX: 0, anchorY: 0.5, x: 100, y: 2732 - 100 }); var playerHpFg = LK.getAsset('hp_bar_fg', { anchorX: 0, anchorY: 0.5, x: 100, y: 2732 - 100 }); game.addChild(playerHpBg); game.addChild(playerHpFg); var enemyHpBg = LK.getAsset('hp_bar_bg', { anchorX: 0, anchorY: 0.5, x: 100, y: 100 }); var enemyHpFg = LK.getAsset('hp_bar_fg_enemy', { anchorX: 0, anchorY: 0.5, x: 100, y: 100 }); game.addChild(enemyHpBg); game.addChild(enemyHpFg); // HP text var playerHpTxt = new Text2('100/100', { size: 60, fill: "#222" }); playerHpTxt.anchor.set(0, 0.5); playerHpTxt.x = 520; playerHpTxt.y = 2732 - 100; game.addChild(playerHpTxt); var enemyHpTxt = new Text2('100/100', { size: 60, fill: "#222" }); enemyHpTxt.anchor.set(0, 0.5); enemyHpTxt.x = 520; enemyHpTxt.y = 100; game.addChild(enemyHpTxt); // Attack buttons (GUI) var atkBtn = new Text2('Attack', { size: 90, fill: "#fff" }); atkBtn.anchor.set(0.5, 0.5); atkBtn.x = 2048 / 2 - 200; atkBtn.y = 2732 - 220; atkBtn.bg = LK.getAsset('hp_bar_bg', { anchorX: 0.5, anchorY: 0.5, x: atkBtn.x, y: atkBtn.y, width: 260, height: 120 }); game.addChild(atkBtn.bg); game.addChild(atkBtn); var spcBtn = new Text2('Special', { size: 90, fill: "#fff" }); spcBtn.anchor.set(0.5, 0.5); spcBtn.x = 2048 / 2 + 200; spcBtn.y = 2732 - 220; spcBtn.bg = LK.getAsset('hp_bar_bg', { anchorX: 0.5, anchorY: 0.5, x: spcBtn.x, y: spcBtn.y, width: 260, height: 120 }); game.addChild(spcBtn.bg); game.addChild(spcBtn); // Attack cooldown overlays var atkBtnCd = LK.getAsset('hp_bar_fg', { anchorX: 0.5, anchorY: 0.5, x: atkBtn.x, y: atkBtn.y, width: 260, height: 120 }); atkBtnCd.alpha = 0.5; game.addChild(atkBtnCd); atkBtnCd.visible = false; var spcBtnCd = LK.getAsset('hp_bar_fg_enemy', { anchorX: 0.5, anchorY: 0.5, x: spcBtn.x, y: spcBtn.y, width: 260, height: 120 }); spcBtnCd.alpha = 0.5; game.addChild(spcBtnCd); spcBtnCd.visible = false; // Attacks array var attacks = []; var enemyAttacks = []; // Dragging var dragNode = null; // Touch/move handlers function handleMove(x, y, obj) { // Drag player if (dragNode === player) { player.moveTo(x, y); } } game.move = handleMove; game.down = function (x, y, obj) { // If on attack button // Manual bounds check for attack button (fixes Script error) if (x >= atkBtn.bg.x - atkBtn.bg.width / 2 && x <= atkBtn.bg.x + atkBtn.bg.width / 2 && y >= atkBtn.bg.y - atkBtn.bg.height / 2 && y <= atkBtn.bg.y + atkBtn.bg.height / 2) { // Fire basic attack if not on cooldown if (player.basicCooldown <= 0) { var atk = new Attack(); atk.setType('basic'); atk.owner = 'player'; atk.x = player.x; atk.y = player.y - player.radius - 40; attacks.push(atk); game.addChild(atk); player.basicCooldown = 30; // 0.5s LK.getSound('atk_basic').play(); } return; } // Manual bounds check for special button (fixes Script error) if (x >= spcBtn.bg.x - spcBtn.bg.width / 2 && x <= spcBtn.bg.x + spcBtn.bg.width / 2 && y >= spcBtn.bg.y - spcBtn.bg.height / 2 && y <= spcBtn.bg.y + spcBtn.bg.height / 2) { // Fire special attack if not on cooldown if (player.specialCooldown <= 0) { var atk = new Attack(); atk.setType('special'); atk.owner = 'player'; atk.x = player.x; atk.y = player.y - player.radius - 40; attacks.push(atk); game.addChild(atk); player.specialCooldown = 120; // 2s LK.getSound('atk_special').play(); } return; } // If on player, start drag var local = player.toLocal({ x: x, y: y }); if (local.x * local.x + local.y * local.y <= player.radius * player.radius) { dragNode = player; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // Helper: collision between two circles function circlesCollide(a, b) { var dx = a.x - b.x; var dy = a.y - b.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < a.radius + b.radius - 10; } // AI logic function enemyAI() { // Move towards player x var dx = player.x - enemy.x; if (Math.abs(dx) > 10) { var dir = dx > 0 ? 1 : -1; enemy.moveTo(enemy.x + dir * enemy.speed, enemy.y); } // Randomly fire attack if (enemy.basicCooldown <= 0 && Math.random() < 0.04) { var atk = new Attack(); atk.setType('enemy'); atk.owner = 'enemy'; atk.x = enemy.x; atk.y = enemy.y + enemy.radius + 40; enemyAttacks.push(atk); game.addChild(atk); enemy.basicCooldown = 36 + Math.floor(Math.random() * 24); // 0.6-1s LK.getSound('atk_enemy').play(); } // Special attack less often if (enemy.specialCooldown <= 0 && Math.random() < 0.01) { var atk = new Attack(); atk.setType('enemy'); atk.owner = 'enemy'; atk.dmg = 22; atk.speed = 20; atk.radius = 40; atk.x = enemy.x; atk.y = enemy.y + enemy.radius + 40; enemyAttacks.push(atk); game.addChild(atk); enemy.specialCooldown = 120 + Math.floor(Math.random() * 60); LK.getSound('atk_enemy').play(); } } // Main update game.update = function () { // Cooldowns if (player.basicCooldown > 0) player.basicCooldown--; if (player.specialCooldown > 0) player.specialCooldown--; if (player.invuln > 0) player.invuln--; if (enemy.basicCooldown > 0) enemy.basicCooldown--; if (enemy.specialCooldown > 0) enemy.specialCooldown--; if (enemy.invuln > 0) enemy.invuln--; // Update attack cooldown overlays atkBtnCd.visible = player.basicCooldown > 0; if (atkBtnCd.visible) { atkBtnCd.width = 260 * (player.basicCooldown / 30); } spcBtnCd.visible = player.specialCooldown > 0; if (spcBtnCd.visible) { spcBtnCd.width = 260 * (player.specialCooldown / 120); } // Update attacks for (var i = attacks.length - 1; i >= 0; i--) { var atk = attacks[i]; atk.update(); // Remove if off screen if (atk.y < -100) { atk.destroy(); attacks.splice(i, 1); continue; } // Hit enemy if (circlesCollide(atk, enemy) && enemy.hp > 0) { enemy.takeDamage(atk.dmg); atk.destroy(); attacks.splice(i, 1); continue; } } for (var i = enemyAttacks.length - 1; i >= 0; i--) { var atk = enemyAttacks[i]; atk.update(); // Remove if off screen if (atk.y > 2732 + 100) { atk.destroy(); enemyAttacks.splice(i, 1); continue; } // Hit player if (circlesCollide(atk, player) && player.hp > 0) { player.takeDamage(atk.dmg); atk.destroy(); enemyAttacks.splice(i, 1); continue; } } // Enemy AI if (enemy.hp > 0) { enemyAI(); } // Update HP bars playerHpFg.width = 400 * (player.hp / player.maxHP); playerHpTxt.setText(player.hp + '/' + player.maxHP); enemyHpFg.width = 400 * (enemy.hp / enemy.maxHP); enemyHpTxt.setText(enemy.hp + '/' + enemy.maxHP); // Win/lose if (player.hp <= 0) { LK.effects.flashScreen(0xe53935, 800); LK.showGameOver(); return; } if (enemy.hp <= 0) { LK.effects.flashScreen(0x4caf50, 800); LK.setScore(1); LK.showYouWin(); return; } }; // GUI: show player and enemy names var playerName = new Text2('You', { size: 60, fill: 0x3B4CCA }); playerName.anchor.set(0, 1); playerName.x = 100; playerName.y = 2732 - 160; game.addChild(playerName); var enemyName = new Text2('Enemy', { size: 60, fill: 0xE53935 }); enemyName.anchor.set(0, 0); enemyName.x = 100; enemyName.y = 160; game.addChild(enemyName); // Center arena (no scrolling, so nothing to do)
===================================================================
--- original.js
+++ change.js
@@ -282,12 +282,10 @@
}
game.move = handleMove;
game.down = function (x, y, obj) {
// If on attack button
- if (atkBtn.bg.containsPoint({
- x: x,
- y: y
- })) {
+ // Manual bounds check for attack button (fixes Script error)
+ if (x >= atkBtn.bg.x - atkBtn.bg.width / 2 && x <= atkBtn.bg.x + atkBtn.bg.width / 2 && y >= atkBtn.bg.y - atkBtn.bg.height / 2 && y <= atkBtn.bg.y + atkBtn.bg.height / 2) {
// Fire basic attack if not on cooldown
if (player.basicCooldown <= 0) {
var atk = new Attack();
atk.setType('basic');
@@ -300,12 +298,10 @@
LK.getSound('atk_basic').play();
}
return;
}
- if (spcBtn.bg.containsPoint({
- x: x,
- y: y
- })) {
+ // Manual bounds check for special button (fixes Script error)
+ if (x >= spcBtn.bg.x - spcBtn.bg.width / 2 && x <= spcBtn.bg.x + spcBtn.bg.width / 2 && y >= spcBtn.bg.y - spcBtn.bg.height / 2 && y <= spcBtn.bg.y + spcBtn.bg.height / 2) {
// Fire special attack if not on cooldown
if (player.specialCooldown <= 0) {
var atk = new Attack();
atk.setType('special');
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Pokémon PvP: Battle Arena" and with the description "A real-time 2D Pokémon battle game where players control a Pokémon, use attacks, dodge, and outlast their opponent in quick PvP matches.". No text on banner!
Darkrai. In-Game asset. 2d. High contrast. No shadows
Lickilicky. In-Game asset. 2d. High contrast. No shadows
Dark ball. In-Game asset. 2d. High contrast. No shadows