User prompt
The text will have an impact effect every time the warriors' combo count increases
User prompt
add an outline
User prompt
dark red
User prompt
make the text red
User prompt
Write the combo text in capital letters and put an exclamation mark at the end.
User prompt
reduce the timer to 1.5 secs
User prompt
If the 2-second timer runs out, the combo will reset.
User prompt
Show the number of combos warriors have made under their health bars
User prompt
make If a warrior hits again within 2 seconds after hitting it, it counts as a combo.
User prompt
add an outline to the healthbar
User prompt
reduce the damage of all attacks
User prompt
significiantly increase the knockback of special attacks
User prompt
increase the knockback of all attakcs
User prompt
reduce it to 1 sec
User prompt
if a fighter is hit with a speacial attack he cant move for 1.3 second
User prompt
make a cooldown for 6 seconds for the speccial attack
User prompt
make the throw buutton an individual asset
User prompt
move it slightly up
User prompt
put it on top of the jumpo button not inside
User prompt
place the throw button over the jump button
User prompt
add a throw button
User prompt
Please fix the bug: 'ReferenceError: Attack is not defined' in or related to this line: 'var atk = new Attack(self, false);' Line Number: 112
User prompt
Please fix the bug: 'Fighter is not defined' in or related to this line: 'var fighter1 = new Fighter();' Line Number: 80
User prompt
Please fix the bug: 'Arena is not defined' in or related to this line: 'var arena = new Arena();' Line Number: 35
User prompt
add throwable class
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // --- Arena class --- // Provides a background and groundY property for fighter placement var Arena = Container.expand(function () { var self = Container.call(this); // Add arena background image, anchored at top-left var bg = self.attachAsset('arena_bg', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Set groundY to a reasonable value for fighter placement // Place ground near the bottom of the visible area, but above the very bottom self.groundY = 2048 + 300; // 2048 is the base height, +300 for a bit above the bottom return self; }); // --- Attack class --- // Represents a regular or special attack hitbox, with owner and damage var Attack = Container.expand(function () { var self = Container.call(this); // Arguments: owner (Fighter), isSpecial (bool) self.owner = arguments[0] || null; self.isSpecial = !!arguments[1]; self.hit = false; self.destroyed = false; // Damage values self.damage = self.isSpecial ? 24 : 8; // Visual var assetId = self.isSpecial ? 'special_attack' : 'attack'; var sprite = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); // Set size for hitbox (use asset size) self.width = sprite.width; self.height = sprite.height; // Set owner after creation if needed self.setOwner = function (fighter) { self.owner = fighter; }; // Update: attacks last for a short time, then destroy self.lifetime = self.isSpecial ? 30 : 18; // special lasts longer self.update = function () { if (self.destroyed) return; self.lifetime--; if (self.lifetime <= 0) { self.destroy(); } }; self.destroy = function () { if (self.destroyed) return; self.destroyed = true; if (self.parent && self.parent.removeChild) { self.parent.removeChild(self); } }; return self; }); // --- Fighter class --- // Handles player and AI fighter logic, health, attacks, movement, and health bar var Fighter = Container.expand(function () { var self = Container.call(this); // --- Properties --- self.maxHealth = 100; self.health = self.maxHealth; self.moveSpeed = 18; self.jumpStrength = 60; self.gravity = 6; self.isJumping = false; self.isAttacking = false; self.isSpecialReady = true; self.specialCooldown = 360; // 6 seconds at 60fps self.specialTimer = 0; self.immobile = false; self.isFacingRight = true; self.attackCooldown = 72; // 1.2 seconds at 60fps self.attackTimer = 0; self.lastJumpPressed = false; // --- Visuals --- var sprite = self.attachAsset('fighter', { anchorX: 0.5, anchorY: 1.0, x: 0, y: 0 }); // Health bar container self.healthBar = new Container(); // Health bar outline (drawn behind the background) var healthBarOutline = new Container(); var outlineThickness = 6; var outlineW = 300 + outlineThickness * 2; var outlineH = 50 + outlineThickness * 2; var outlineRect = LK.getAsset('healthbar_bg', { anchorX: 0, anchorY: 0.5, x: -outlineThickness, y: 0, width: outlineW, height: outlineH, color: 0xffffff // white outline }); healthBarOutline.addChild(outlineRect); self.healthBar.addChild(healthBarOutline); // Health bar background var healthBarBg = LK.getAsset('healthbar_bg', { anchorX: 0, anchorY: 0.5, x: 0, y: 0 }); self.healthBar.addChild(healthBarBg); // Health bar foreground var healthBarFg = LK.getAsset('healthbar_fg', { anchorX: 0, anchorY: 0.5, x: 0, y: 0 }); self.healthBar.addChild(healthBarFg); self.healthBar.width = 300; self.healthBar.height = 50; self.healthBarFg = healthBarFg; self.updateHealthBar = function () { var pct = Math.max(0, self.health / self.maxHealth); self.healthBarFg.width = 300 * pct; }; self.updateHealthBar(); // --- Methods --- self.setFacing = function (right) { self.isFacingRight = right; sprite.scaleX = right ? 1 : -1; }; self.jump = function () { if (!self.isJumping && !self.immobile) { self.isJumping = true; self.vy = -self.jumpStrength; } }; self.attack = function () { if (self.isAttacking || self.immobile || self.attackTimer > 0) return; self.isAttacking = true; self.attackTimer = self.attackCooldown; // Create attack object var atk = new Attack(self, false); atk.x = self.x + (self.isFacingRight ? 120 : -120); atk.y = self.y - 200; attacks.push(atk); game.addChild(atk); // End attack after short delay LK.setTimeout(function () { self.isAttacking = false; }, 300); }; self.special = function () { if (!self.isSpecialReady || self.immobile) return; self.isSpecialReady = false; self.specialTimer = self.specialCooldown; // Create special attack object var atk = new Attack(self, true); atk.x = self.x + (self.isFacingRight ? 180 : -180); atk.y = self.y - 200; attacks.push(atk); game.addChild(atk); }; self.takeDamage = function (amount, dir, knockback) { if (self.immobile) return; self.health -= amount; self.updateHealthBar(); // Knockback self.x += (dir || 1) * (knockback || 32); // Clamp to arena self.x = Math.max(120, Math.min(2048 - 120, self.x)); }; self.update = function () { // Attack cooldown if (self.attackTimer > 0) { self.attackTimer--; } // Special cooldown if (!self.isSpecialReady) { if (self.specialTimer > 0) { self.specialTimer--; } if (self.specialTimer <= 0) { self.isSpecialReady = true; } } // Jumping physics if (self.isJumping) { if (typeof self.vy === "undefined") self.vy = 0; self.y += self.vy; self.vy += self.gravity; // Land on ground if (self.y >= arena.groundY) { self.y = arena.groundY; self.isJumping = false; self.vy = 0; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- Arena setup --- //Note game dimensions are 2048x2732 //Minimalistic tween library which should be used for animations over time, including tinting / colouring an object, scaling, rotating, or changing any game object property. var arena = new Arena(); arena.x = 0; arena.y = 0; game.addChild(arena); // --- Fixed ground platform (visual) --- var groundHeight = 60; var groundTileWidth = LK.getAsset('ground', { anchorX: 0, anchorY: 0 }).width; var groundY = arena.groundY - 100; // Move ground even further down by reducing the offset (was -300) var groundTiles = []; var numGroundTiles = Math.ceil(2048 / groundTileWidth); // Only enough to fill the screen for (var i = 0; i < numGroundTiles; i++) { var groundTile = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: i * groundTileWidth, y: groundY }); game.addChild(groundTile); groundTiles.push(groundTile); } // No infinite scrolling, ground is fixed and does not move // --- Fighters --- var fighter1 = new Fighter(); fighter1.lastJumpPressed = false; fighter1.lastHitTime = -9999; // Track last time this fighter hit the other fighter1.comboCount = 0; // Current combo count var fighter2 = new Fighter(); fighter2.lastHitTime = -9999; fighter2.comboCount = 0; // Place fighters on opposite sides fighter1.x = 600; fighter1.y = arena.groundY; fighter1.setFacing(true); fighter2.x = 2048 - 600; fighter2.y = arena.groundY; fighter2.setFacing(false); game.addChild(fighter1); game.addChild(fighter2); // --- Health bar GUI --- // Removed number healthbars (Text2 healthBar1 and healthBar2) // Move the box healthbars (fighter healthBar containers) to a more visible, symmetric place below the timer at the top center // Remove from fighter containers and add to GUI, position symmetrically below timer fighter1.removeChild(fighter1.healthBar); fighter2.removeChild(fighter2.healthBar); // Place both healthbars below the timer, offset horizontally from center fighter1.healthBar.x = -550; fighter1.healthBar.y = 170; fighter2.healthBar.x = 250; fighter2.healthBar.y = 170; LK.gui.top.addChild(fighter1.healthBar); LK.gui.top.addChild(fighter2.healthBar); // --- Combo counters under health bars --- var comboText1 = new Text2('Combo: 0', { size: 48, fill: 0xFF0000, stroke: 0xffffff, strokeThickness: 8 }); comboText1.anchor.set(0.5, 0); comboText1.x = fighter1.healthBar.x + 150; // center under health bar (300px wide) comboText1.y = fighter1.healthBar.y + 60; // just below health bar var comboText2 = new Text2('Combo: 0', { size: 48, fill: 0xFF0000, stroke: 0xffffff, strokeThickness: 8 }); comboText2.anchor.set(0.5, 0); comboText2.x = fighter2.healthBar.x + 150; comboText2.y = fighter2.healthBar.y + 60; LK.gui.top.addChild(comboText1); LK.gui.top.addChild(comboText2); // --- Round and timer --- var round = 1; var maxRounds = 3; var wins1 = 0; var wins2 = 0; var roundTime = 60 * 30; // 30 seconds at 60fps var roundTimer = roundTime; var timerText = new Text2('30', { size: 80, fill: "#fff" }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); // --- Touch controls (mobile-friendly) --- var leftPressed = false, rightPressed = false, atkPressed = false, jumpPressed = false, spcPressed = false, throwPressed = false; // Special attack cooldown overlay for button var spcCooldownOverlay = LK.getAsset('healthbar_bg', { anchorX: 0.5, anchorY: 0.5, width: 180, height: 180, color: 0x000000 }); spcCooldownOverlay.alpha = 0.5; spcCooldownOverlay.visible = false; spcCooldownOverlay.x = 2048 - 350; spcCooldownOverlay.y = 2450; game.addChild(spcCooldownOverlay); var spcCooldownText = new Text2('3', { size: 90, fill: "#fff" }); spcCooldownText.anchor.set(0.5, 0.5); spcCooldownText.x = 2048 - 350; spcCooldownText.y = 2450; spcCooldownText.visible = false; game.addChild(spcCooldownText); // Special attack cooldown counter above the special attack button var spcCooldownCounter = new Text2('', { size: 60, fill: "#fff" }); spcCooldownCounter.anchor.set(0.5, 1.0); // Place above the special button (btnSpc) spcCooldownCounter.x = 2048 - 350; spcCooldownCounter.y = 2450 - 120; spcCooldownCounter.visible = false; game.addChild(spcCooldownCounter); // Control buttons (simple rectangles for now) var btnLeft = LK.getAsset('btn_left', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 2600, scaleX: 1.8, scaleY: 1.8 }); var btnRight = LK.getAsset('btn_right', { anchorX: 0.5, anchorY: 0.5, x: 500, y: 2600, scaleX: 1.8, scaleY: 1.8 }); var btnAtk = LK.getAsset('btn_atk', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 500, y: 2600, scaleX: 1.8, scaleY: 1.8 }); var btnJump = LK.getAsset('btn_jump', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 200, y: 2600, scaleX: 1.8, scaleY: 1.8 }); var btnSpc = LK.getAsset('btn_spc', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 350, y: 2450, scaleX: 1.8, scaleY: 1.8 }); // Add throw button (use unique btn_throw asset, place visually above the jump button) var btnThrow = LK.getAsset('btn_throw', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 200, // Move throw button slightly further up above the jump button (offset y by -220px) y: 2600 - 220, scaleX: 1.8, scaleY: 1.8 }); game.addChild(btnLeft); game.addChild(btnRight); game.addChild(btnAtk); game.addChild(btnJump); game.addChild(btnSpc); game.addChild(btnThrow); // Button event helpers btnLeft.down = function () { leftPressed = true; }; btnLeft.up = function () { leftPressed = false; }; btnRight.down = function () { rightPressed = true; }; btnRight.up = function () { rightPressed = false; }; btnAtk.down = function () { atkPressed = true; }; btnAtk.up = function () { atkPressed = false; }; btnJump.down = function () { jumpPressed = true; }; btnJump.up = function () { jumpPressed = false; }; btnSpc.down = function () { spcPressed = true; }; btnSpc.up = function () { spcPressed = false; }; btnThrow.down = function () { throwPressed = true; }; btnThrow.up = function () { throwPressed = false; }; // --- Attacks array --- var attacks = []; // --- Game update loop --- game.update = function () { // --- Timer --- if (roundTimer > 0) { roundTimer--; timerText.setText(Math.ceil(roundTimer / 60)); } // --- Ground is fixed, no update needed --- // --- Player 1 controls (left side) --- if (!fighter1.immobile) { if (leftPressed) { fighter1.x = Math.max(120, fighter1.x - fighter1.moveSpeed); fighter1.setFacing(false); } if (rightPressed) { fighter1.x = Math.min(2048 - 120, fighter1.x + fighter1.moveSpeed); fighter1.setFacing(true); } // Only trigger jump on the frame the button is pressed (rising edge) if (jumpPressed && !fighter1.lastJumpPressed) { fighter1.jump(); } if (atkPressed) { fighter1.attack(); } if (throwPressed && typeof fighter1.throwObject === "function") { fighter1.throwObject(); } if (spcPressed) { if (fighter1.isSpecialReady) { fighter1.special(); } } } fighter1.lastJumpPressed = jumpPressed; // --- Special attack cooldown overlay update --- if (!fighter1.isSpecialReady) { spcCooldownOverlay.visible = true; spcCooldownText.visible = true; var secondsLeft = Math.ceil(fighter1.specialTimer / 60); spcCooldownText.setText(secondsLeft); // Show and update the counter above the special button spcCooldownCounter.visible = true; spcCooldownCounter.setText(secondsLeft + "s"); } else { spcCooldownOverlay.visible = false; spcCooldownText.visible = false; spcCooldownCounter.visible = false; } // --- AI for fighter2 (simple: move toward player, attack if close) --- if (!fighter2.immobile) { var dx = fighter1.x - fighter2.x; if (Math.abs(dx) > 180) { if (dx < 0) { fighter2.x -= fighter2.moveSpeed * 0.7; fighter2.setFacing(false); } else { fighter2.x += fighter2.moveSpeed * 0.7; fighter2.setFacing(true); } } else if (!fighter2.isAttacking && Math.random() < 0.04) { fighter2.attack(); } if (!fighter2.isJumping && Math.random() < 0.01) { fighter2.jump(); } if (fighter2.isSpecialReady && Math.random() < 0.008) { fighter2.special(); } } // --- Update fighters --- fighter1.update(); fighter2.update(); // --- Update attacks --- for (var i = attacks.length - 1; i >= 0; i--) { var atk = attacks[i]; atk.update(); // Remove if destroyed if (atk.destroyed) { attacks.splice(i, 1); continue; } // Set owner if not set if (!atk.owner) { atk.setOwner(atk.isSpecial ? atk.x < 2048 / 2 ? fighter1 : fighter2 : fighter1); } // Check collision var target = atk.owner === fighter1 ? fighter2 : fighter1; if (atk.intersects(target) && !atk.hit) { // Combo logic: check if this hit is within 1.5 seconds (90 frames) of the last hit by this attacker var nowTick = LK.ticks || 0; if (typeof atk.owner.lastHitTime === "undefined") atk.owner.lastHitTime = -9999; if (typeof atk.owner.comboCount === "undefined") atk.owner.comboCount = 0; var prevCombo = atk.owner.comboCount; if (nowTick - atk.owner.lastHitTime <= 90) { atk.owner.comboCount++; // Impact effect on combo text when combo increases if (atk.owner === fighter1 && atk.owner.comboCount > prevCombo) { comboText1.scaleX = comboText1.scaleY = 1.3; tween(comboText1, { scaleX: 1, scaleY: 1 }, { duration: 180 }); } else if (atk.owner === fighter2 && atk.owner.comboCount > prevCombo) { comboText2.scaleX = comboText2.scaleY = 1.3; tween(comboText2, { scaleX: 1, scaleY: 1 }, { duration: 180 }); } } else { atk.owner.comboCount = 1; // Impact effect on combo text when combo increases from 0 if (atk.owner === fighter1 && atk.owner.comboCount > prevCombo) { comboText1.scaleX = comboText1.scaleY = 1.3; tween(comboText1, { scaleX: 1, scaleY: 1 }, { duration: 180 }); } else if (atk.owner === fighter2 && atk.owner.comboCount > prevCombo) { comboText2.scaleX = comboText2.scaleY = 1.3; tween(comboText2, { scaleX: 1, scaleY: 1 }, { duration: 180 }); } } atk.owner.lastHitTime = nowTick; // Knockback direction: +1 if attacker is facing right, -1 if left var knockbackDir = atk.owner && atk.owner.isFacingRight ? 1 : -1; // Knockback strength: special attacks knock back SIGNIFICANTLY more (much stronger effect) var knockbackStrength = atk.isSpecial ? 480 : 64; target.takeDamage(atk.damage, knockbackDir, knockbackStrength); // If hit by a special attack, make target immobile for 1 second (60 frames) if (atk.isSpecial) { target.immobile = true; LK.setTimeout(function () { target.immobile = false; }, 1000); } atk.hit = true; atk.destroy(); // Flash on hit LK.effects.flashObject(target, 0xff0000, 200); // Show a visible attack effect at the hit location, rotated to match the attack direction var hitEffect = LK.getAsset(atk.isSpecial ? 'special_attack' : 'attack', { anchorX: 0.5, anchorY: 0.5, x: target.x, y: target.y - 200 }); // Rotate the effect to match the direction the attacker is facing if (atk.owner && atk.owner.isFacingRight === false) { hitEffect.rotation = Math.PI; // Face left } else { hitEffect.rotation = 0; // Face right (default) } game.addChild(hitEffect); // Animate the effect: scale up and fade out, then destroy hitEffect.scaleX = hitEffect.scaleY = 1.2; hitEffect.alpha = 1; tween(hitEffect, { scaleX: 2.0, scaleY: 2.0, alpha: 0 }, { duration: 350, onFinish: function onFinish() { if (hitEffect && hitEffect.destroy) hitEffect.destroy(); } }); } } // --- Health bar updates --- // Removed number healthbars update // --- Combo reset logic: reset combo if 1.5 seconds (90 frames) have passed since last hit --- var nowTick = LK.ticks || 0; if (nowTick - fighter1.lastHitTime > 90) { fighter1.comboCount = 0; } if (nowTick - fighter2.lastHitTime > 90) { fighter2.comboCount = 0; } // Update combo counters under health bars comboText1.setText(('COMBO: ' + (fighter1.comboCount || 0) + '!').toUpperCase()); comboText2.setText(('COMBO: ' + (fighter2.comboCount || 0) + '!').toUpperCase()); // --- Win/lose/round logic --- var roundOver = false; var winner = null; if (fighter1.health <= 0) { wins2++; roundOver = true; winner = 2; } else if (fighter2.health <= 0) { wins1++; roundOver = true; winner = 1; } else if (roundTimer <= 0) { if (fighter1.health > fighter2.health) { wins1++; winner = 1; } else if (fighter2.health > fighter1.health) { wins2++; winner = 2; } else { // Draw, no one gets a win } roundOver = true; } if (roundOver) { if (wins1 >= 2) { LK.showYouWin(); return; } else if (wins2 >= 2) { LK.showGameOver(); return; } // Next round round++; roundTimer = roundTime; fighter1.health = fighter1.maxHealth; fighter2.health = fighter2.maxHealth; fighter1.updateHealthBar(); fighter2.updateHealthBar(); fighter1.x = 600; fighter2.x = 2048 - 600; fighter1.setFacing(true); fighter2.setFacing(false); // Remove all attacks for (var j = attacks.length - 1; j >= 0; j--) { if (attacks[j].destroy) attacks[j].destroy(); } attacks = []; } };
===================================================================
--- original.js
+++ change.js
@@ -540,14 +540,49 @@
// Combo logic: check if this hit is within 1.5 seconds (90 frames) of the last hit by this attacker
var nowTick = LK.ticks || 0;
if (typeof atk.owner.lastHitTime === "undefined") atk.owner.lastHitTime = -9999;
if (typeof atk.owner.comboCount === "undefined") atk.owner.comboCount = 0;
+ var prevCombo = atk.owner.comboCount;
if (nowTick - atk.owner.lastHitTime <= 90) {
atk.owner.comboCount++;
- // Optionally: show combo feedback (e.g. flash, text, etc)
- // Example: LK.effects.flashObject(atk.owner, 0xffff00, 150);
+ // Impact effect on combo text when combo increases
+ if (atk.owner === fighter1 && atk.owner.comboCount > prevCombo) {
+ comboText1.scaleX = comboText1.scaleY = 1.3;
+ tween(comboText1, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 180
+ });
+ } else if (atk.owner === fighter2 && atk.owner.comboCount > prevCombo) {
+ comboText2.scaleX = comboText2.scaleY = 1.3;
+ tween(comboText2, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 180
+ });
+ }
} else {
atk.owner.comboCount = 1;
+ // Impact effect on combo text when combo increases from 0
+ if (atk.owner === fighter1 && atk.owner.comboCount > prevCombo) {
+ comboText1.scaleX = comboText1.scaleY = 1.3;
+ tween(comboText1, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 180
+ });
+ } else if (atk.owner === fighter2 && atk.owner.comboCount > prevCombo) {
+ comboText2.scaleX = comboText2.scaleY = 1.3;
+ tween(comboText2, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 180
+ });
+ }
}
atk.owner.lastHitTime = nowTick;
// Knockback direction: +1 if attacker is facing right, -1 if left
var knockbackDir = atk.owner && atk.owner.isFacingRight ? 1 : -1;