User prompt
giriş ekranında entry_bg asseti gözüksün
User prompt
oyunun giriş ekranı için bir arka plan asseti oluştur
User prompt
Funky Groove haritasındaki canavarlar öldüğünde monsterdead2 sound asseti çalışsın
User prompt
ekranın altındaki tuşlara bastığımda key1 sound asseti çalışsın
User prompt
monster2 image assetini funky groove haritasına ekle
User prompt
monster2 adında bir image asseti oluştur
User prompt
monster2 diye bir asset oluştur
User prompt
funky groove haritası için yeni bir monster asseti oluştur bu haritada farkl canavarlar olacak
User prompt
çok çok azıcık daha yukarı çıkar
User prompt
çok azıcık daha yukarı çıkar funky groove haritası için çok çok az ama
User prompt
funky groove için karakteri çok azıcık yukarı çıkar fazla aşağıda oldu şuanda
User prompt
sadece funky groove haritası için karakteri birazcık aşağı indirir misin
User prompt
Funky Groove a tıkladıktan sonraki üstteki ekranın arka planı için yeni bir arka plan asseti oluştur bu giriş ekranındaki How to Play ve Choose the Music butonlarının arka planından farklı bir asset olarak tutulsun
User prompt
Choose The Play e bastıktan sonra Funky Groove şarkısı seçildiğinde ve oyun başladığında üstteki (karakterin ve canavarların olduğu) ekranın farklı bir arka plan asseti olsun
User prompt
Oyun giriş ekranındayken arka planda oyun çalışmasın sadece düz bir arka planı olsun
User prompt
Oyuna bir giriş ekranı oluştur bunun kendi bir arka plan asseti olsun ve Choose The music ve How to Play olarak 2 buton olsun bu butonlardan Choose the Music butonuna basınca karşısına 3 adet müzik ismi çıksın bu isimlerden herhangi birine basınca oyun başlasın How to Play kısmında oyunun nasıl oynandığı kısaca açıklanan bir açıklama kısmı çıksın
User prompt
giriş ekranı varken oyun arkada oynamasın giriş ekranına bir arka plan belirle ve Choose the Musicten bir music seçilince oyun ekranı gelsin ve oyun başlasın
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'btn.label.style.fill = "#fff";' Line Number: 410
User prompt
bu giriş ekranı oyundan bağımsız olsun Choose the Map yerine Choose The Music olsun ve o butona basınca toplam 3 adet seçilebilecek şarkı ismi olsun o şarkılardan birini seçince oyun başlasın o zaman
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'closeBtn.label.style.fill = "#fff";' Line Number: 407
User prompt
Oyuna bir giriş ekranı yap En üstte oyunun ismi yazsın altında 2 buton olsun Choose the Map ve How to Play olsun
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'visible')' in or related to this line: 'missTxt.visible = false;' Line Number: 372
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'visible')' in or related to this line: 'scoreTxt.visible = false;' Line Number: 369
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < pianoKeys.length; i++) {' Line Number: 363
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'visible')' in or related to this line: 'topBg.visible = false;' Line Number: 346
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Monster: Enemy advancing from the top var Monster = Container.expand(function () { var self = Container.call(this); // Attach monster asset (box/ellipse) var monsterAsset = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5, width: self.monsterSize, height: self.monsterSize }); // Health points self.hp = self.maxHp; // Called every tick self.update = function () { // Monsters only move forward if not destroyed if (!self.destroyed) { // Move toward hero using direction vector if set if (typeof self.dirX === "number" && typeof self.dirY === "number") { self.x += self.dirX * self.speed; self.y += self.dirY * self.speed; } else { // fallback: move down self.y += self.speed; } } }; // --- Flying effect: gentle up/down hover using tween --- function startFlyingEffect() { // Reset to default before starting self.yOffset = 0; // Animate up tween(self, { yOffset: -18 }, { duration: 420 + Math.random() * 120, easing: tween.easeInOut, onUpdate: function onUpdate() { // Apply yOffset to monster position self.y += self.yOffset - (self._lastYOffset || 0); self._lastYOffset = self.yOffset; }, onFinish: function onFinish() { // Animate down tween(self, { yOffset: 18 }, { duration: 420 + Math.random() * 120, easing: tween.easeInOut, onUpdate: function onUpdate() { self.y += self.yOffset - (self._lastYOffset || 0); self._lastYOffset = self.yOffset; }, onFinish: function onFinish() { startFlyingEffect(); } }); } }); } startFlyingEffect(); // Take damage self.hit = function () { self.hp -= 1; if (self.hp <= 0) { self.destroyed = true; // Play monster dead sound LK.getSound('monsterdead').play(); // Animate scale up and fade out for death effect tween(self, { alpha: 0, scaleX: 1.7, scaleY: 1.7 }, { duration: 220, onFinish: function onFinish() { self.destroy(); } }); } else { // Flash red tween(monsterAsset, { tint: 0xff4444 }, { duration: 80, onFinish: function onFinish() { tween(monsterAsset, { tint: self.baseColor }, { duration: 120 }); } }); } }; return self; }); // Note: Falling note that must be hit in time var Note = Container.expand(function () { var self = Container.call(this); // Pick a random note asset for this note var noteAssetId = 'note' + (1 + Math.floor(Math.random() * 4)); self.noteAssetId = noteAssetId; var noteAsset = self.attachAsset(noteAssetId, { anchorX: 0.5, anchorY: 0.5, width: self.noteSize, height: self.noteSize }); // Index of the key this note targets self.keyIndex = typeof self.keyIndex !== "undefined" ? self.keyIndex : self.index; // Used for hit/miss detection self.active = true; // Called every tick self.update = function () { // Notes always fall straight down toward their assigned key self.y += self.speed; // Find the key this note is assigned to var key = typeof self.keyIndex !== "undefined" && self.keyIndex >= 0 && self.keyIndex < NUM_KEYS ? pianoKeys[self.keyIndex] : null; if (key) { var missY = key.y + KEY_HEIGHT + NOTE_SIZE / 2; // If note crosses the bottom boundary and is still active, mark as miss and destroy if (self.y > missY && self.active) { self.active = false; // Register miss missCount += 1; missTxt.setText('Misses: ' + missCount); LK.effects.flashObject(hero, 0xff4444, 200); checkGameOver(); self.destroy(); } } else { // Defensive: If key is missing, just destroy the note self.destroy(); } }; return self; }); // PianoKey: Represents a single piano key at the bottom var PianoKey = Container.expand(function () { var self = Container.call(this); // Defensive: Set defaults if not set if (typeof self.assetId === "undefined") { self.assetId = 'key_piano'; } if (typeof self.keyWidth === "undefined") { self.keyWidth = KEY_WIDTH; } if (typeof self.keyHeight === "undefined") { self.keyHeight = KEY_HEIGHT; } if (typeof self.baseColor === "undefined") { self.baseColor = 0xffffff; } if (typeof self.index === "undefined") { self.index = 0; } // Attach key asset (always key_piano) var keyAsset = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0, width: self.keyWidth, height: self.keyHeight }); // Store index for reference self.keyIndex = self.index; // Set initial dim state keyAsset.alpha = 0.45; // Visual feedback for press self.flash = function () { // Light up key tween(keyAsset, { alpha: 1 }, { duration: 60, onFinish: function onFinish() { // Return to dim after a short time tween(keyAsset, { alpha: 0.45 }, { duration: 120 }); } }); }; return self; }); // Projectile: Fired by hero toward a monster var Projectile = Container.expand(function () { var self = Container.call(this); // Defensive: Set defaults if not set if (typeof self.size === "undefined") { self.size = PROJECTILE_SIZE; } if (typeof self.speed === "undefined") { self.speed = PROJECTILE_SPEED; } // Pick a random bullet asset for this projectile var bulletAssetIds = ['bullet', 'bullet2', 'bullet3', 'bullet4']; var chosenBulletAsset = bulletAssetIds[Math.floor(Math.random() * bulletAssetIds.length)]; // Attach bullet asset var projAsset = self.attachAsset(chosenBulletAsset, { anchorX: 0.5, anchorY: 0.5, width: self.size, height: self.size }); // Target monster (set externally) self.target = null; // Called every tick self.update = function () { // If no target or target destroyed, fade out and destroy if (!self.target || self.target.destroyed || !self.target.parent) { tween(self, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 120, onFinish: function onFinish() { self.destroy(); } }); return; } // Move toward target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.speed) { // Arrived at target: hit! if (typeof self.target.hit === "function") { self.target.hit(); } // Impact effect tween(self, { alpha: 0, scaleX: 1.7, scaleY: 1.7 }, { duration: 120, onFinish: function onFinish() { self.destroy(); } }); return; } // Move toward target self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181830 }); /**** * Game Code ****/ // --- Entry Screen UI --- var entryScreen = new Container(); // Title Text var titleText = new Text2("Piano Monster Defense", { size: 160, fill: "#fff", font: "Impact, Arial Black, Tahoma" }); titleText.anchor.set(0.5, 0); titleText.x = 2048 / 2; titleText.y = 180; entryScreen.addChild(titleText); // Button style function createButton(label, y) { var btnWidth = 700; var btnHeight = 170; var btn = new Container(); // Button background var btnBg = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: btnWidth, height: btnHeight }); btnBg.tint = 0x2d8cf0; btn.addChild(btnBg); // Button label var btnLabel = new Text2(label, { size: 80, fill: "#fff", font: "Impact, Arial Black, Tahoma" }); btnLabel.anchor.set(0.5, 0.5); btnLabel.x = 0; btnLabel.y = 0; btn.addChild(btnLabel); btn.x = 2048 / 2; btn.y = y; btn.interactive = true; btn.buttonMode = true; btn.bg = btnBg; btn.label = btnLabel; return btn; } // "Choose the Map" button var btnChooseMap = createButton("Choose the Map", 500); // "How to Play" button var btnHowToPlay = createButton("How to Play", 720); entryScreen.addChild(btnChooseMap); entryScreen.addChild(btnHowToPlay); // Add entry screen to game game.addChild(entryScreen); // Disable gameplay UI and input until entry screen is dismissed var entryActive = true; // Hide gameplay elements until entry screen is gone topBg = LK.getAsset('top_bg', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 1366 // Top half of the screen }); // Play background music when the game starts LK.playMusic('gamemusic'); // Button handlers btnChooseMap.down = function (x, y, obj) { if (!entryActive) return; entryActive = false; entryScreen.visible = false; // Show gameplay UI game.addChild(topBg); }; btnHowToPlay.down = function (x, y, obj) { if (!entryActive) return; // Show a simple how-to overlay (modal) if (typeof howToOverlay !== "undefined" && howToOverlay.parent) { howToOverlay.visible = true; return; } howToOverlay = new Container(); var bg = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 1400, height: 900 }); bg.tint = 0x222244; howToOverlay.addChild(bg); var txt = new Text2("Tap the falling notes or the piano keys at the right time!\n\n" + "Defeat monsters by hitting notes and firing projectiles.\n\n" + "Don't let monsters reach you or miss too many notes!", { size: 60, fill: "#fff", font: "Arial" }); txt.anchor.set(0.5, 0.5); txt.x = 0; txt.y = -100; howToOverlay.addChild(txt); // Close button var closeBtn = createButton("Close", 320); closeBtn.bg.tint = 0xe74c3c; closeBtn.label.style.fill = "#fff"; closeBtn.down = function () { howToOverlay.visible = false; }; howToOverlay.addChild(closeBtn); howToOverlay.x = 2048 / 2; howToOverlay.y = 2732 / 2; game.addChild(howToOverlay); }; // Block all gameplay input while entry screen is active var origGameDown = game.down; game.down = function (x, y, obj) { if (entryActive) { // Forward to entry screen buttons var local = entryScreen.toLocal({ x: x, y: y }); if (btnChooseMap.visible && Math.abs(local.x - btnChooseMap.x) < 350 && Math.abs(local.y - btnChooseMap.y) < 85) { btnChooseMap.down(x, y, obj); return; } if (btnHowToPlay.visible && Math.abs(local.x - btnHowToPlay.x) < 350 && Math.abs(local.y - btnHowToPlay.y) < 85) { btnHowToPlay.down(x, y, obj); return; } return; } if (typeof origGameDown === "function") origGameDown(x, y, obj); }; // --- Constants --- // Tween plugin for note/monster animations // Bullet asset for weapon fire // 5 separate heart assets for health (can be different colors for each) var NUM_KEYS = 5; // --- Piano and Layout Constants --- var KEY_WIDTH = Math.floor(2048 / NUM_KEYS); // Each key fills 1/5 of width var KEY_HEIGHT = Math.floor(2732 / 2); // Keys fill bottom half var KEY_GAP = 0; // No gap, keys are flush var KEY_BOTTOM_MARGIN = 0; // No margin, keys reach bottom var NOTE_SIZE = 170; var NOTE_BASE_SPEED = 7; // Start slower var NOTE_MAX_SPEED = 7; // Cap speed var NOTE_SPEED = NOTE_BASE_SPEED; // Will be updated dynamically var NOTE_SPAWN_INTERVAL = 60; // frames var NOTE_SPEEDUP_INTERVAL = 600; // Every 10 seconds at 60fps var NOTE_SPEEDUP_AMOUNT = 1.2; // Increase by this amount each interval var MONSTER_SIZE = 180; var MONSTER_SPEED = 2.5; var MONSTER_HP = 2; var MONSTER_SPAWN_INTERVAL = 180; // frames var PROJECTILE_SIZE = 60; var PROJECTILE_SPEED = 40; var MAX_MISSES = 8; // --- Asset Initialization --- // --- Game State --- var pianoKeys = []; var notes = []; var monsters = []; var projectiles = []; var missCount = 0; var score = 0; var lastNoteSpawn = 0; var lastMonsterSpawn = 0; // --- Health Bar State (global) --- if (typeof playerMaxHealth === "undefined") { var playerMaxHealth = 5; } if (typeof playerHealth === "undefined") { var playerHealth = playerMaxHealth; } var healthBar = null; // --- Layout Calculations --- var pianoWidth = NUM_KEYS * KEY_WIDTH + (NUM_KEYS - 1) * KEY_GAP; var pianoLeft = 0; // Keys start at left edge var pianoTop = 2732 / 2; // Keys start at vertical center // --- No divider line: screen is visually split by content only --- // --- Hero (player) --- // --- Breathing effect for hero and weapon (in sync) --- var hero = game.addChild(LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 770 })); // Add a weapon to hero's lap (bigger and centered for lap position) var weapon = LK.getAsset('projectile', { anchorX: 0.5, anchorY: 0.85, width: 230, height: 120, x: hero.x, y: hero.y + 90 }); game.addChild(weapon); function startHeroBreathing() { // Reset scale to default before starting hero.scaleX = 1; hero.scaleY = 1; weapon.scaleX = 1; weapon.scaleY = 1; // Helper to tween both hero and weapon together function inhale() { tween(hero, { scaleX: 1.07, scaleY: 0.93 }, { duration: 900, easing: tween.easeInOut, onFinish: function onFinish() { tween(hero, { scaleX: 1, scaleY: 1 }, { duration: 900, easing: tween.easeInOut, onFinish: inhale }); } }); tween(weapon, { scaleX: 1.07, scaleY: 0.93 }, { duration: 900, easing: tween.easeInOut, onFinish: function onFinish() { tween(weapon, { scaleX: 1, scaleY: 1 }, { duration: 900, easing: tween.easeInOut }); } }); } inhale(); } startHeroBreathing(); // Keep weapon attached to hero's lap game.updateWeapon = function () { weapon.x = hero.x; weapon.y = hero.y + 90; }; // --- Score Display --- var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Misses Display --- var missTxt = new Text2('Misses: 0', { size: 70, fill: 0xFF6666 }); missTxt.anchor.set(0.5, 0); LK.gui.top.addChild(missTxt); missTxt.y = 120; // --- Piano Keys --- for (var i = 0; i < NUM_KEYS; i++) { var key = new PianoKey(); key.index = i; key.keyWidth = KEY_WIDTH; key.keyHeight = KEY_HEIGHT; key.assetId = 'key_piano'; // Use key_piano asset for all keys key.baseColor = 0xffffff; key.x = pianoLeft + i * (KEY_WIDTH + KEY_GAP) + KEY_WIDTH / 2; key.y = 2732 - KEY_HEIGHT; // Place keys at the very bottom of the screen key.width = KEY_WIDTH; key.height = KEY_HEIGHT; game.addChild(key); pianoKeys.push(key); } // --- Input Handling --- game.down = function (x, y, obj) { // Check if a key was pressed var keyPressed = false; for (var i = 0; i < pianoKeys.length; i++) { var key = pianoKeys[i]; // Key bounds var left = key.x - KEY_WIDTH / 2; var right = key.x + KEY_WIDTH / 2; var top = key.y; var bottom = key.y + KEY_HEIGHT; if (x >= left && x <= right && y >= top && y <= bottom) { key.flash(); handleKeyPress(i); keyPressed = true; break; } } // If not on a key, check if a note was tapped if (!keyPressed) { for (var n = 0; n < notes.length; n++) { var note = notes[n]; if (!note.active) { continue; } // Note bounds var noteLeft = note.x - NOTE_SIZE / 2; var noteRight = note.x + NOTE_SIZE / 2; var noteTop = note.y - NOTE_SIZE / 2; var noteBottom = note.y + NOTE_SIZE / 2; if (x >= noteLeft && x <= noteRight && y >= noteTop && y <= noteBottom) { // Only allow firing if note is above the bottom of its assigned key (not missed yet) var key = typeof note.keyIndex !== "undefined" && note.keyIndex >= 0 && note.keyIndex < pianoKeys.length ? pianoKeys[note.keyIndex] : null; if (key) { var missY = key.y + KEY_HEIGHT + NOTE_SIZE / 2; if (note.y < missY) { // Fire projectile at nearest monster, scale speed by timing accuracy var target = findNearestMonster(); if (target) { // Calculate accuracy: closer to center line = more accurate var centerLine = 2732 / 2; var maxDist = pianoTop - centerLine; var distFromCenter = Math.abs(note.y - centerLine); var accuracy = 1 - Math.min(distFromCenter / maxDist, 1); // 1 = perfect, 0 = worst var proj = new Projectile(); proj.size = PROJECTILE_SIZE; proj.x = weapon.x; proj.y = weapon.y; // Scale projectile speed: min 60, max 120 proj.speed = PROJECTILE_SPEED + Math.floor(accuracy * 60); proj.monster = target; proj.target = target; projectiles.push(proj); game.addChild(proj); // Animate hero and weapon for shooting tween(hero, { scaleX: 1.15, scaleY: 0.92 }, { duration: 80, yoyo: true, repeat: 1, onFinish: function onFinish() { // Restart breathing effect after shooting startHeroBreathing(); } }); // Rotate weapon to point toward the enemy var angleToTarget = Math.atan2(target.y - weapon.y, target.x - weapon.x); tween(weapon, { rotation: angleToTarget }, { duration: 80, yoyo: true, repeat: 1 }); } // Animate note note.active = false; tween(note, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 120, onFinish: function onFinish() { note.destroy(); } }); break; } } } } } }; // --- Key Press Logic --- function handleKeyPress(keyIndex) { // Find the first active note for this key that is still in the air (not missed yet) var hit = false; for (var n = 0; n < notes.length; n++) { var note = notes[n]; if (!note.active) { continue; } if (note.keyIndex !== keyIndex) { continue; } var key = pianoKeys[keyIndex]; // Only allow hit if note is inside the key bounds (not just above the key) var keyTop = key.y; var keyBottom = key.y + KEY_HEIGHT; var noteTop = note.y - NOTE_SIZE / 2; var noteBottom = note.y + NOTE_SIZE / 2; // Check if note is at least partially inside the key area var insideKey = !(noteBottom < keyTop || noteTop > keyBottom); // Rhythm mechanic: Only allow hit if note is visible (not missed, not below key) var missY = key.y + KEY_HEIGHT + NOTE_SIZE / 2; if (note.y >= missY || !note.active) { // Note is no longer visible, or already inactive, so pressing now is a miss break; } if (insideKey) { // Only allow hit if note is still active (not destroyed/missed) if (!note.active) { continue; } // Hit! note.active = false; hit = true; score += 1; scoreTxt.setText(score); // Fire projectile at nearest monster, scale speed by timing accuracy var target = findNearestMonster(); if (target) { // Calculate accuracy: closer to center line = more accurate var centerLine = 2732 / 2; var maxDist = pianoTop - centerLine; var distFromCenter = Math.abs(note.y - centerLine); var accuracy = 1 - Math.min(distFromCenter / maxDist, 1); // 1 = perfect, 0 = worst var proj = new Projectile(); proj.size = PROJECTILE_SIZE; proj.x = weapon.x; proj.y = weapon.y; // Scale projectile speed: min 60, max 120 proj.speed = PROJECTILE_SPEED + Math.floor(accuracy * 60); proj.monster = target; proj.target = target; projectiles.push(proj); game.addChild(proj); // Animate hero and weapon for shooting tween(hero, { scaleX: 1.15, scaleY: 0.92 }, { duration: 80, yoyo: true, repeat: 1, onFinish: function onFinish() { // Restart breathing effect after shooting startHeroBreathing(); } }); // Rotate weapon to point toward the enemy var angleToTarget = Math.atan2(target.y - weapon.y, target.x - weapon.x); tween(weapon, { rotation: angleToTarget }, { duration: 80, yoyo: true, repeat: 1 }); } // Animate note tween(note, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 120, onFinish: function onFinish() { note.destroy(); } }); break; } } if (!hit) { // Missed (pressed wrong key, or note is not visible, or note was already destroyed/missed) missCount += 1; missTxt.setText('Misses: ' + missCount); LK.effects.flashObject(hero, 0xff4444, 200); checkGameOver(); } } // --- Find Nearest Monster --- function findNearestMonster() { var minDist = 99999; var nearest = null; for (var i = 0; i < monsters.length; i++) { var m = monsters[i]; if (m.destroyed) { continue; } // Distance from hero to monster var dx = m.x - hero.x; var dy = m.y - hero.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; nearest = m; } } return nearest; } // --- Game Update Loop --- game.update = function () { // Gradually speed up notes and monsters every NOTE_SPEEDUP_INTERVAL frames, up to max if (LK.ticks % NOTE_SPEEDUP_INTERVAL === 0 && LK.ticks > 0) { NOTE_SPEED = Math.min(NOTE_MAX_SPEED, NOTE_SPEED + NOTE_SPEEDUP_AMOUNT); // Cap monster speed at a separate, reasonable maximum var MONSTER_MAX_SPEED = 8; // Set a reasonable cap for monster speed // Also increase speed of all future monsters (and update current monsters to match) for (var i = 0; i < notes.length; i++) { notes[i].speed = NOTE_SPEED; } for (var j = 0; j < monsters.length; j++) { // Only update monsters that are not in attack range (so they don't "jump" while attacking) if (!monsters[j].destroyed && typeof monsters[j].dirX === "number" && typeof monsters[j].dirY === "number") { monsters[j].speed = Math.min(MONSTER_MAX_SPEED, NOTE_SPEED); } } } // --- DENSITY CONTROL --- // As the game progresses, increase the number of notes spawned at once (density), but cap speed // For example, every 20 seconds, increase density by 1, up to a max var DENSITY_INCREASE_INTERVAL = 1200; // every 20 seconds at 60fps var MAX_NOTE_DENSITY = Math.min(NUM_KEYS, 4); // never more than number of keys if (typeof noteDensity === "undefined") { var noteDensity = 1; } if (LK.ticks % DENSITY_INCREASE_INTERVAL === 0 && LK.ticks > 0) { noteDensity = Math.min(MAX_NOTE_DENSITY, noteDensity + 1); } // Spawn notes and corresponding monsters (enemies) in sync if (LK.ticks - lastNoteSpawn >= NOTE_SPAWN_INTERVAL) { lastNoteSpawn = LK.ticks; // Pick random keys to spawn notes on, up to noteDensity, never duplicate keys in one spawn var availableKeys = []; for (var i = 0; i < NUM_KEYS; i++) { availableKeys.push(i); } // Shuffle availableKeys for (var i = availableKeys.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = availableKeys[i]; availableKeys[i] = availableKeys[j]; availableKeys[j] = temp; } for (var d = 0; d < noteDensity; d++) { if (availableKeys.length === 0) { break; } var keyIdx = availableKeys.pop(); // Create note var note = new Note(); note.index = keyIdx; note.noteSize = NOTE_SIZE; // Assign note to a random key var key = pianoKeys[note.index]; note.keyIndex = note.index; // Start notes at the center line, horizontally aligned with their key note.x = key.x; note.y = 2732 / 2 - NOTE_SIZE / 2; note.speed = NOTE_SPEED; note.active = true; notes.push(note); game.addChild(note); // Create corresponding monster (enemy) for this note var monster = new Monster(); monster.monsterSize = MONSTER_SIZE; // Spawn at the top right of the upper half of the screen monster.x = 2048 - MONSTER_SIZE / 2 - 40; // right margin, with a little padding monster.y = 220 + Math.random() * (2732 / 2 - 220 - MONSTER_SIZE); // random Y in upper half, not below center // Monster speed matches note speed, but is capped var MONSTER_MAX_SPEED = 8; monster.speed = Math.min(MONSTER_MAX_SPEED, note.speed); monster.maxHp = MONSTER_HP; monster.hp = MONSTER_HP; monster.baseColor = 0x8e44ad; monster.destroyed = false; // Calculate direction vector: from spawn point toward hero var dx = hero.x - monster.x; var dy = hero.y - monster.y; var dist = Math.sqrt(dx * dx + dy * dy); monster.dirX = dx / dist; monster.dirY = dy / dist; // Link monster to note and note to monster for removal note.linkedMonster = monster; monster.linkedNote = note; monsters.push(monster); game.addChild(monster); } } // Update notes for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; if (note && typeof note.update === "function") { note.update(); } // Remove destroyed/inactive notes if (!note || !note.active || !note.parent || note.alpha === 0) { if (note && typeof note.destroy === "function" && note.parent) { note.destroy(); } notes.splice(i, 1); } else if (typeof note.keyIndex === "undefined" || note.keyIndex < 0 || note.keyIndex >= pianoKeys.length) { if (note && typeof note.destroy === "function" && note.parent) { note.destroy(); } notes.splice(i, 1); } } // Update projectiles for (var i = projectiles.length - 1; i >= 0; i--) { var p = projectiles[i]; if (p && typeof p.update === "function") { p.update(); } if (!p || !p.parent || p.alpha === 0) { if (p && typeof p.destroy === "function" && p.parent) { p.destroy(); } projectiles.splice(i, 1); } } // Update monsters and clean up destroyed ones for (var i = monsters.length - 1; i >= 0; i--) { var m = monsters[i]; if (m && typeof m.update === "function") { m.update(); } if (!m || m.destroyed && (!m.parent || m.alpha === 0)) { if (m && typeof m.destroy === "function" && m.parent) { m.destroy(); } monsters.splice(i, 1); } } // Defensive: Full cleanup for any orphaned/destroyed objects (extra safety, every 120 frames) if (LK.ticks % 120 === 0) { for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; if (!note || !note.parent || note.alpha === 0) { if (note && typeof note.destroy === "function" && note.parent) { note.destroy(); } notes.splice(i, 1); } } for (var i = monsters.length - 1; i >= 0; i--) { var m = monsters[i]; if (!m || !m.parent || m.alpha === 0) { if (m && typeof m.destroy === "function" && m.parent) { m.destroy(); } monsters.splice(i, 1); } } for (var i = projectiles.length - 1; i >= 0; i--) { var p = projectiles[i]; if (!p || !p.parent || p.alpha === 0) { if (p && typeof p.destroy === "function" && p.parent) { p.destroy(); } projectiles.splice(i, 1); } } } // --- Monster attack logic --- // Player health bar and state if (typeof playerMaxHealth === "undefined") { var playerMaxHealth = 5; } if (typeof playerHealth === "undefined") { var playerHealth = playerMaxHealth; } if (typeof healthBar === "undefined") { var healthBar = null; } // Health bar setup (if not already) if (!healthBar) { healthBar = new Container(); healthBar.segments = []; // Heart icon size and spacing (make hearts bigger and move higher) var heartSize = 90; var gap = 24; var barWidth = playerMaxHealth * heartSize + (playerMaxHealth - 1) * gap; for (var h = 0; h < playerMaxHealth; h++) { // Use heart1-heart5 assets for each health segment var heartAssetId = 'heart' + (h + 1); var seg = LK.getAsset(heartAssetId, { anchorX: 0.5, anchorY: 0.5, width: heartSize, height: heartSize, x: h * (heartSize + gap), y: 0 }); healthBar.addChild(seg); healthBar.segments.push(seg); } // Move health bar higher above the hero and center healthBar.x = hero.x - barWidth / 2 + heartSize / 2; healthBar.y = hero.y - 220; game.addChild(healthBar); healthBar.update = function () { // Immediately update segment alpha for lost health for (var s = 0; s < healthBar.segments.length; s++) { var seg = healthBar.segments[s]; var shouldBeVisible = s < playerHealth; if (shouldBeVisible && seg.alpha < 1) { seg.alpha = 1; } else if (!shouldBeVisible && seg.alpha > 0) { seg.alpha = 0; } } }; healthBar.reset = function () { playerHealth = playerMaxHealth; for (var s = 0; s < healthBar.segments.length; s++) { healthBar.segments[s].alpha = 1; } if (typeof healthBar.update === "function") { healthBar.update(); } if (typeof healthBar.update === "function") { healthBar.update(); } }; } // Update health bar position to follow hero if (healthBar && healthBar.segments && healthBar.segments.length > 0) { var heartSize = healthBar.segments[0].width; var gap = healthBar.segments.length > 1 ? healthBar.segments[1].x - healthBar.segments[0].x - heartSize : 0; var barWidth = playerMaxHealth * heartSize + (playerMaxHealth - 1) * gap; healthBar.x = hero.x - barWidth / 2 + heartSize / 2; healthBar.y = hero.y - 220; } if (typeof healthBar.update === "function") { healthBar.update(); } // --- Monster attack logic --- var ATTACK_RANGE = 120; var DAMAGE_INTERVAL = 180; // 3 seconds at 60fps if (typeof gameOverTriggered === "undefined") { var gameOverTriggered = false; } if (typeof playerMaxHealth === "undefined") { var playerMaxHealth = 5; } if (typeof playerHealth === "undefined") { var playerHealth = playerMaxHealth; } for (var i = monsters.length - 1; i >= 0; i--) { var m = monsters[i]; if (m.destroyed) { continue; } // Calculate distance to hero var dx = m.x - hero.x; var dy = m.y - hero.y; var dist = Math.sqrt(dx * dx + dy * dy); // If in attack range, stop moving and attack if (dist <= ATTACK_RANGE) { // Stop monster movement m.dirX = 0; m.dirY = 0; m.speed = 0; // Place monster at edge of attack range (don't overlap hero) var angle = Math.atan2(dy, dx); m.x = hero.x + Math.cos(angle) * ATTACK_RANGE; m.y = hero.y + Math.sin(angle) * ATTACK_RANGE; // Each monster tracks its own attack timer (in frames) if (typeof m.attackTimer === "undefined") { m.attackTimer = 0; } // Track if monster was previously in range if (typeof m.lastInAttackRange === "undefined") { m.lastInAttackRange = false; } // If just entered attack range, reset timer so damage doesn't happen instantly if (!m.lastInAttackRange) { m.attackTimer = 0; } m.lastInAttackRange = true; // --- BITE ANIMATION & DAMAGE --- // Only increment timer and deal damage if game is not over if (!gameOverTriggered && playerHealth > 0) { m.attackTimer++; // Monster bites every DAMAGE_INTERVAL frames (3 seconds) if (m.attackTimer >= DAMAGE_INTERVAL) { m.attackTimer = 0; if (playerHealth > 0) { // Animate monster "bite" (scale up and back quickly) tween(m, { scaleX: 1.18, scaleY: 0.82 }, { duration: 90, yoyo: true, repeat: 1 }); // Remove the highest-numbered visible heart asset var heartToRemove = -1; for (var h = healthBar.segments.length - 1; h >= 0; h--) { if (healthBar.segments[h].alpha > 0) { heartToRemove = h; break; } } if (heartToRemove !== -1) { // Visually remove the heart asset healthBar.segments[heartToRemove].alpha = 0; // Flash hero red LK.effects.flashObject(hero, 0xff0000, 300); // Flash health bar segment (the one just lost) tween(healthBar.segments[heartToRemove], { tint: 0xff4444 }, { duration: 120, onFinish: function onFinish() { tween(this, { tint: 0x2ecc71 }, { duration: 120 }); }.bind(healthBar.segments[heartToRemove]) }); // Decrement playerHealth playerHealth--; if (typeof healthBar.update === "function") { healthBar.update(); } // If all hearts are gone, trigger game over var allHeartsGone = true; for (var h = 0; h < healthBar.segments.length; h++) { if (healthBar.segments[h].alpha > 0) { allHeartsGone = false; break; } } if (allHeartsGone && !gameOverTriggered) { gameOverTriggered = true; if (healthBar && typeof healthBar.update === "function") { healthBar.update(); } LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); // Reset health bar and health for next game if (healthBar) { healthBar.reset(); } playerHealth = playerMaxHealth; // Clean up all notes, monsters, and projectiles to prevent buildup for (var i = notes.length - 1; i >= 0; i--) { if (notes[i] && typeof notes[i].destroy === "function" && notes[i].parent) { notes[i].destroy(); } notes.splice(i, 1); } for (var i = monsters.length - 1; i >= 0; i--) { if (monsters[i] && typeof monsters[i].destroy === "function" && monsters[i].parent) { monsters[i].destroy(); } monsters.splice(i, 1); } for (var i = projectiles.length - 1; i >= 0; i--) { if (projectiles[i] && typeof projectiles[i].destroy === "function" && projectiles[i].parent) { projectiles[i].destroy(); } projectiles.splice(i, 1); } // Defensive: Reset arrays to empty to ensure no lingering references notes = []; monsters = []; projectiles = []; } } } } } } else { // Not in attack range, monster moves as normal if (typeof m.dirX !== "number" || typeof m.dirY !== "number" || m.speed === 0) { // Recalculate direction and restore speed, but cap it var dx2 = hero.x - m.x; var dy2 = hero.y - m.y; var dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); m.dirX = dx2 / dist2; m.dirY = dy2 / dist2; } // Always set monster speed to match note speed (capped) var MONSTER_MAX_SPEED = 8; m.speed = Math.min(MONSTER_MAX_SPEED, NOTE_SPEED); // Reset attack timer and in-range flag if monster leaves range if (typeof m.attackTimer !== "undefined") { m.attackTimer = 0; } m.lastInAttackRange = false; } // Clean up timer if monster destroyed if (m.destroyed && typeof m.attackTimer !== "undefined") { delete m.attackTimer; delete m.lastInAttackRange; } } // Game over if health reaches 0 if (playerHealth <= 0 && !gameOverTriggered) { gameOverTriggered = true; if (healthBar && typeof healthBar.update === "function") { healthBar.update(); } LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); // Stop all monster attacks and player actions // Reset health bar and health for next game if (healthBar) { healthBar.reset(); } playerHealth = playerMaxHealth; // Clean up all notes, monsters, and projectiles to prevent buildup for (var i = notes.length - 1; i >= 0; i--) { if (notes[i] && typeof notes[i].destroy === "function" && notes[i].parent) { notes[i].destroy(); } notes.splice(i, 1); } for (var i = monsters.length - 1; i >= 0; i--) { if (monsters[i] && typeof monsters[i].destroy === "function" && monsters[i].parent) { monsters[i].destroy(); } monsters.splice(i, 1); } for (var i = projectiles.length - 1; i >= 0; i--) { if (projectiles[i] && typeof projectiles[i].destroy === "function" && projectiles[i].parent) { projectiles[i].destroy(); } projectiles.splice(i, 1); } // Defensive: Reset arrays to empty to ensure no lingering references notes = []; monsters = []; projectiles = []; } // Update weapon position to follow hero if (typeof game.updateWeapon === "function") { game.updateWeapon(); } }; // --- Game Over Check --- function checkGameOver() { if (missCount >= MAX_MISSES) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); // Reset health bar and health for next game if (healthBar && typeof healthBar.update === "function") { healthBar.update(); } if (healthBar) { healthBar.reset(); } playerHealth = playerMaxHealth; // Clean up all notes, monsters, and projectiles to prevent buildup for (var i = notes.length - 1; i >= 0; i--) { if (notes[i] && typeof notes[i].destroy === "function" && notes[i].parent) { notes[i].destroy(); } notes.splice(i, 1); } for (var i = monsters.length - 1; i >= 0; i--) { if (monsters[i] && typeof monsters[i].destroy === "function" && monsters[i].parent) { monsters[i].destroy(); } monsters.splice(i, 1); } for (var i = projectiles.length - 1; i >= 0; i--) { if (projectiles[i] && typeof projectiles[i].destroy === "function" && projectiles[i].parent) { projectiles[i].destroy(); } projectiles.splice(i, 1); } // Defensive: Reset arrays to empty to ensure no lingering references notes = []; monsters = []; projectiles = []; } } // --- Win Condition (Optional: e.g. score 30) --- /* if (score >= 30) { LK.showYouWin(); } */ // --- End of File ---
===================================================================
--- original.js
+++ change.js
@@ -268,200 +268,139 @@
/****
* Game Code
****/
-// --- Start Screen Implementation ---
-var startScreen = new Container();
-var startBg = LK.getAsset('top_bg', {
- anchorX: 0,
- anchorY: 0,
- x: 0,
- y: 0,
- width: 2048,
- height: 1366
-});
-startScreen.addChild(startBg);
-// Game Title
-var titleTxt = new Text2('PIANO MONSTER', {
- size: 180,
+// --- Entry Screen UI ---
+var entryScreen = new Container();
+// Title Text
+var titleText = new Text2("Piano Monster Defense", {
+ size: 160,
fill: "#fff",
font: "Impact, Arial Black, Tahoma"
});
-titleTxt.anchor.set(0.5, 0);
-titleTxt.x = 2048 / 2;
-titleTxt.y = 320;
-startScreen.addChild(titleTxt);
-// Menu Option: Choose The Music
-var chooseMusicTxt = new Text2('Choose The Music', {
- size: 100,
- fill: 0xFFE066,
- font: "Impact, Arial Black, Tahoma"
-});
-chooseMusicTxt.anchor.set(0.5, 0);
-chooseMusicTxt.x = 2048 / 2;
-chooseMusicTxt.y = titleTxt.y + 320;
-startScreen.addChild(chooseMusicTxt);
-// Menu Option: How to Play
-var howToPlayTxt = new Text2('How to Play', {
- size: 100,
- fill: 0x66E0FF,
- font: "Impact, Arial Black, Tahoma"
-});
-howToPlayTxt.anchor.set(0.5, 0);
-howToPlayTxt.x = 2048 / 2;
-howToPlayTxt.y = chooseMusicTxt.y + 180;
-startScreen.addChild(howToPlayTxt);
-// Add startScreen to the game
-game.addChild(startScreen);
-// Top background asset for upper part of the screen
-var topBg = LK.getAsset('top_bg', {
+titleText.anchor.set(0.5, 0);
+titleText.x = 2048 / 2;
+titleText.y = 180;
+entryScreen.addChild(titleText);
+// Button style
+function createButton(label, y) {
+ var btnWidth = 700;
+ var btnHeight = 170;
+ var btn = new Container();
+ // Button background
+ var btnBg = LK.getAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: btnWidth,
+ height: btnHeight
+ });
+ btnBg.tint = 0x2d8cf0;
+ btn.addChild(btnBg);
+ // Button label
+ var btnLabel = new Text2(label, {
+ size: 80,
+ fill: "#fff",
+ font: "Impact, Arial Black, Tahoma"
+ });
+ btnLabel.anchor.set(0.5, 0.5);
+ btnLabel.x = 0;
+ btnLabel.y = 0;
+ btn.addChild(btnLabel);
+ btn.x = 2048 / 2;
+ btn.y = y;
+ btn.interactive = true;
+ btn.buttonMode = true;
+ btn.bg = btnBg;
+ btn.label = btnLabel;
+ return btn;
+}
+// "Choose the Map" button
+var btnChooseMap = createButton("Choose the Map", 500);
+// "How to Play" button
+var btnHowToPlay = createButton("How to Play", 720);
+entryScreen.addChild(btnChooseMap);
+entryScreen.addChild(btnHowToPlay);
+// Add entry screen to game
+game.addChild(entryScreen);
+// Disable gameplay UI and input until entry screen is dismissed
+var entryActive = true;
+// Hide gameplay elements until entry screen is gone
+topBg = LK.getAsset('top_bg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 1366 // Top half of the screen
});
-game.addChild(topBg);
-// Hide game elements until start
-var gameStarted = false;
-topBg.visible = false;
-if (typeof hero !== "undefined") hero.visible = false;
-if (typeof weapon !== "undefined") weapon.visible = false;
-if (typeof pianoKeys === "undefined") pianoKeys = [];
-for (var i = 0; i < pianoKeys.length; i++) pianoKeys[i].visible = false;
-if (typeof scoreTxt !== "undefined" && scoreTxt) scoreTxt.visible = false;
-if (typeof missTxt !== "undefined" && missTxt) missTxt.visible = false;
-if (healthBar) healthBar.visible = false;
-// Start game on any tap/click on startScreen
-startScreen.down = function (x, y, obj) {
- if (gameStarted) return;
- gameStarted = true;
- // Remove start screen
- if (startScreen.parent) startScreen.parent.removeChild(startScreen);
- // Show game elements
- topBg.visible = true;
- if (typeof hero !== "undefined") hero.visible = true;
- if (typeof weapon !== "undefined") weapon.visible = true;
- if (typeof pianoKeys === "undefined") pianoKeys = [];
- for (var i = 0; i < pianoKeys.length; i++) pianoKeys[i].visible = true;
- scoreTxt.visible = true;
- missTxt.visible = true;
- if (healthBar) healthBar.visible = true;
- // Play background music when the game starts
- LK.playMusic('gamemusic');
+// Play background music when the game starts
+LK.playMusic('gamemusic');
+// Button handlers
+btnChooseMap.down = function (x, y, obj) {
+ if (!entryActive) return;
+ entryActive = false;
+ entryScreen.visible = false;
+ // Show gameplay UI
+ game.addChild(topBg);
};
-// Forward input to startScreen while visible
-game.down = function (x, y, obj) {
- if (!gameStarted && typeof startScreen.down === "function") {
- startScreen.down(x, y, obj);
+btnHowToPlay.down = function (x, y, obj) {
+ if (!entryActive) return;
+ // Show a simple how-to overlay (modal)
+ if (typeof howToOverlay !== "undefined" && howToOverlay.parent) {
+ howToOverlay.visible = true;
return;
}
- // --- original game.down code below ---
- // Check if a key was pressed
- var keyPressed = false;
- for (var i = 0; i < pianoKeys.length; i++) {
- var key = pianoKeys[i];
- // Key bounds
- var left = key.x - KEY_WIDTH / 2;
- var right = key.x + KEY_WIDTH / 2;
- var top = key.y;
- var bottom = key.y + KEY_HEIGHT;
- if (x >= left && x <= right && y >= top && y <= bottom) {
- key.flash();
- handleKeyPress(i);
- keyPressed = true;
- break;
+ howToOverlay = new Container();
+ var bg = LK.getAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 1400,
+ height: 900
+ });
+ bg.tint = 0x222244;
+ howToOverlay.addChild(bg);
+ var txt = new Text2("Tap the falling notes or the piano keys at the right time!\n\n" + "Defeat monsters by hitting notes and firing projectiles.\n\n" + "Don't let monsters reach you or miss too many notes!", {
+ size: 60,
+ fill: "#fff",
+ font: "Arial"
+ });
+ txt.anchor.set(0.5, 0.5);
+ txt.x = 0;
+ txt.y = -100;
+ howToOverlay.addChild(txt);
+ // Close button
+ var closeBtn = createButton("Close", 320);
+ closeBtn.bg.tint = 0xe74c3c;
+ closeBtn.label.style.fill = "#fff";
+ closeBtn.down = function () {
+ howToOverlay.visible = false;
+ };
+ howToOverlay.addChild(closeBtn);
+ howToOverlay.x = 2048 / 2;
+ howToOverlay.y = 2732 / 2;
+ game.addChild(howToOverlay);
+};
+// Block all gameplay input while entry screen is active
+var origGameDown = game.down;
+game.down = function (x, y, obj) {
+ if (entryActive) {
+ // Forward to entry screen buttons
+ var local = entryScreen.toLocal({
+ x: x,
+ y: y
+ });
+ if (btnChooseMap.visible && Math.abs(local.x - btnChooseMap.x) < 350 && Math.abs(local.y - btnChooseMap.y) < 85) {
+ btnChooseMap.down(x, y, obj);
+ return;
}
- }
- // If not on a key, check if a note was tapped
- if (!keyPressed) {
- for (var n = 0; n < notes.length; n++) {
- var note = notes[n];
- if (!note.active) {
- continue;
- }
- // Note bounds
- var noteLeft = note.x - NOTE_SIZE / 2;
- var noteRight = note.x + NOTE_SIZE / 2;
- var noteTop = note.y - NOTE_SIZE / 2;
- var noteBottom = note.y + NOTE_SIZE / 2;
- if (x >= noteLeft && x <= noteRight && y >= noteTop && y <= noteBottom) {
- // Only allow firing if note is above the bottom of its assigned key (not missed yet)
- var key = typeof note.keyIndex !== "undefined" && note.keyIndex >= 0 && note.keyIndex < pianoKeys.length ? pianoKeys[note.keyIndex] : null;
- if (key) {
- var missY = key.y + KEY_HEIGHT + NOTE_SIZE / 2;
- if (note.y < missY) {
- // Fire projectile at nearest monster, scale speed by timing accuracy
- var target = findNearestMonster();
- if (target) {
- // Calculate accuracy: closer to center line = more accurate
- var centerLine = 2732 / 2;
- var maxDist = pianoTop - centerLine;
- var distFromCenter = Math.abs(note.y - centerLine);
- var accuracy = 1 - Math.min(distFromCenter / maxDist, 1); // 1 = perfect, 0 = worst
- var proj = new Projectile();
- proj.size = PROJECTILE_SIZE;
- proj.x = weapon.x;
- proj.y = weapon.y;
- // Scale projectile speed: min 60, max 120
- proj.speed = PROJECTILE_SPEED + Math.floor(accuracy * 60);
- proj.monster = target;
- proj.target = target;
- projectiles.push(proj);
- game.addChild(proj);
- // Animate hero and weapon for shooting
- tween(hero, {
- scaleX: 1.15,
- scaleY: 0.92
- }, {
- duration: 80,
- yoyo: true,
- repeat: 1,
- onFinish: function onFinish() {
- // Restart breathing effect after shooting
- startHeroBreathing();
- }
- });
- // Rotate weapon to point toward the enemy
- var angleToTarget = Math.atan2(target.y - weapon.y, target.x - weapon.x);
- tween(weapon, {
- rotation: angleToTarget
- }, {
- duration: 80,
- yoyo: true,
- repeat: 1
- });
- }
- // Animate note
- note.active = false;
- tween(note, {
- alpha: 0,
- scaleX: 1.5,
- scaleY: 1.5
- }, {
- duration: 120,
- onFinish: function onFinish() {
- note.destroy();
- }
- });
- break;
- }
- }
- }
+ if (btnHowToPlay.visible && Math.abs(local.x - btnHowToPlay.x) < 350 && Math.abs(local.y - btnHowToPlay.y) < 85) {
+ btnHowToPlay.down(x, y, obj);
+ return;
}
+ return;
}
+ if (typeof origGameDown === "function") origGameDown(x, y, obj);
};
-// Top background asset for upper part of the screen
-var topBg = LK.getAsset('top_bg', {
- anchorX: 0,
- anchorY: 0,
- x: 0,
- y: 0,
- width: 2048,
- height: 1366 // Top half of the screen
-});
-game.addChild(topBg);
// --- Constants ---
// Tween plugin for note/monster animations
// Bullet asset for weapon fire
// 5 separate heart assets for health (can be different colors for each)
shine yellow color music note. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
robotic monster. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
blue shiny music note. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat