/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function (speed, direction) { var self = Container.call(this); var bulletSprite = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = speed || 3; self.direction = direction || 0; // radians self.lastX = self.x; self.lastY = self.y; self.update = function () { self.lastX = self.x; self.lastY = self.y; self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; // Remove bullet if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); } }; return self; }); var Character = Container.expand(function (name, assetId) { var self = Container.call(this); // Character graphics var characterSprite = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Character stats self.name = name; self.hp = 100; self.maxHp = 100; self.atk = 10; self.def = 5; self.mag = 8; self.speed = 5; self.tp = 0; self.maxTp = 100; // Battle state self.isDefending = false; self.lastX = self.x; self.lastY = self.y; self.takeDamage = function (amount) { if (self.isDefending) { amount = Math.floor(amount * 0.5); } self.hp = Math.max(0, self.hp - amount); // Flash red when taking damage tween(characterSprite, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(characterSprite, { tint: 0xFFFFFF }, { duration: 200 }); } }); }; self.heal = function (amount) { self.hp = Math.min(self.maxHp, self.hp + amount); // Flash green when healing tween(characterSprite, { tint: 0x00FF00 }, { duration: 300, onFinish: function onFinish() { tween(characterSprite, { tint: 0xFFFFFF }, { duration: 200 }); } }); }; self.gainTp = function (amount) { self.tp = Math.min(self.maxTp, self.tp + amount); }; return self; }); var Enemy = Container.expand(function (name, assetId, hp, atk) { var self = Container.call(this); var enemySprite = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.name = name; self.hp = hp || 50; self.maxHp = self.hp; self.atk = atk || 15; self.isSpared = false; self.mercyLevel = 0; self.takeDamage = function (amount) { self.hp = Math.max(0, self.hp - amount); // Flash white when taking damage tween(enemySprite, { tint: 0xFFFFFF }, { duration: 100, onFinish: function onFinish() { tween(enemySprite, { tint: 0xFFFFFF }, { duration: 100 }); } }); if (self.hp <= 0) { self.destroy(); } }; self.increaseMercy = function (amount) { self.mercyLevel = Math.min(100, self.mercyLevel + (amount || 20)); if (self.mercyLevel >= 100) { enemySprite.tint = 0xFFFF00; // Yellow tint when spareable } }; return self; }); var Heart = Container.expand(function () { var self = Container.call(this); var heartSprite = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.lastX = self.x; self.lastY = self.y; self.graceTime = 0; self.update = function () { self.lastX = self.x; self.lastY = self.y; if (self.graceTime > 0) { self.graceTime--; heartSprite.alpha = 0.5 + 0.5 * Math.sin(LK.ticks * 0.3); } else { heartSprite.alpha = 1; } }; self.takeHit = function () { if (self.graceTime <= 0) { self.graceTime = 120; // 2 seconds of invincibility return true; } return false; }; return self; }); var ItemMenu = Container.expand(function () { var self = Container.call(this); self.isVisible = false; self.selectedItemIndex = 0; self.items = [{ name: 'Dark Candy', description: 'Restores 10 HP', effect: 'heal', value: 10, quantity: 3 }, { name: 'Revive Mint', description: 'Restores 30 HP', effect: 'heal', value: 30, quantity: 2 }, { name: 'Tension Bit', description: 'Restores 20 TP', effect: 'tp', value: 20, quantity: 1 }]; // Create menu background var menuBg = self.attachAsset('dialogBox', { anchorX: 0.5, anchorY: 0.5 }); menuBg.width = 1200; menuBg.height = 800; menuBg.tint = 0x222222; // Create title text var titleText = new Text2('ITEMS', { size: 48, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.x = 0; titleText.y = -350; self.addChild(titleText); // Create item buttons and text self.itemButtons = []; self.itemTexts = []; for (var i = 0; i < self.items.length; i++) { var itemButton = LK.getAsset('menuButton', { anchorX: 0, anchorY: 0.5 }); itemButton.x = -550; itemButton.y = -200 + i * 100; itemButton.width = 1000; itemButton.height = 80; itemButton.tint = 0x444444; itemButton.itemIndex = i; self.addChild(itemButton); self.itemButtons.push(itemButton); var itemText = new Text2(self.items[i].name + ' x' + self.items[i].quantity, { size: 32, fill: 0xFFFFFF }); itemText.x = -500; itemText.y = -200 + i * 100; itemText.anchor.set(0, 0.5); self.addChild(itemText); self.itemTexts.push(itemText); var descText = new Text2(self.items[i].description, { size: 24, fill: 0xCCCCCC }); descText.x = -200; descText.y = -200 + i * 100; descText.anchor.set(0, 0.5); self.addChild(descText); } // Create back button var backButton = LK.getAsset('menuButton', { anchorX: 0.5, anchorY: 0.5 }); backButton.x = 0; backButton.y = 300; backButton.width = 200; backButton.height = 60; backButton.tint = 0x666666; self.addChild(backButton); var backText = new Text2('BACK', { size: 32, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backButton.addChild(backText); self.show = function () { self.isVisible = true; self.alpha = 1; self.updateSelection(); }; self.hide = function () { self.isVisible = false; self.alpha = 0; }; self.updateSelection = function () { for (var i = 0; i < self.itemButtons.length; i++) { if (i === self.selectedItemIndex) { self.itemButtons[i].tint = 0x666666; } else { self.itemButtons[i].tint = 0x444444; } } }; self.useSelectedItem = function () { var item = self.items[self.selectedItemIndex]; if (item.quantity <= 0) return false; item.quantity--; self.itemTexts[self.selectedItemIndex].setText(item.name + ' x' + item.quantity); if (item.effect === 'heal') { kris.heal(item.value); } else if (item.effect === 'tp') { kris.gainTp(item.value); } return true; }; // Handle button clicks for (var i = 0; i < self.itemButtons.length; i++) { self.itemButtons[i].down = function (x, y, obj) { if (self.isVisible && self.items[obj.itemIndex].quantity > 0) { self.selectedItemIndex = obj.itemIndex; if (self.useSelectedItem()) { LK.getSound('heal').play(); self.hide(); if (gameState === 'battle') { selectedAction = 'ITEM'; executePlayerAction(); } } } }; } backButton.down = function (x, y, obj) { if (self.isVisible) { LK.getSound('select').play(); self.hide(); if (gameState === 'battle') { battlePhase = 'menu'; for (var j = 0; j < menuButtons.length; j++) { tween(menuButtons[j], { alpha: 1 }, { duration: 200 }); } } } }; return self; }); var LaserBullet = Container.expand(function (speed, direction) { var self = Container.call(this); var laserSprite = self.attachAsset('laserBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = speed || 6; self.direction = direction || 0; self.lastX = self.x; self.lastY = self.y; // Set rotation to match direction laserSprite.rotation = self.direction; // Pulsing effect self.pulseTime = 0; self.update = function () { self.lastX = self.x; self.lastY = self.y; // Track heart position if in bullet hell phase if (gameState === 'battle' && battlePhase === 'bullet_hell' && heart && heart.alpha > 0) { // Calculate angle to heart var deltaX = heart.x - self.x; var deltaY = heart.y - self.y; var targetAngle = Math.atan2(deltaY, deltaX); // Smoothly rotate towards heart var angleDiff = targetAngle - self.direction; // Normalize angle difference to -PI to PI while (angleDiff > Math.PI) angleDiff -= Math.PI * 2; while (angleDiff < -Math.PI) angleDiff += Math.PI * 2; // Rotate at a maximum rate for smooth turning var rotationSpeed = 0.08; self.direction += Math.sign(angleDiff) * Math.min(Math.abs(angleDiff), rotationSpeed); // Update sprite rotation to match direction laserSprite.rotation = self.direction; } self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; // Pulsing scale effect self.pulseTime += 0.2; laserSprite.scaleY = 1 + 0.3 * Math.sin(self.pulseTime); // Remove bullet if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); } }; return self; }); /**** * Initialize Game ****/ // Game state variables var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ // Create background sprite // Sounds // World elements // UI elements // Battle elements // Enemy sprites // Character sprites // Game state variables var backgroundSprite = LK.getAsset('background', { anchorX: 0, anchorY: 0, scaleX: 1, scaleY: 1 }); backgroundSprite.x = 0; backgroundSprite.y = 0; game.addChild(backgroundSprite); // Move background to the bottom layer game.setChildIndex(backgroundSprite, 0); var gameState = 'overworld'; // 'overworld', 'battle', 'dialogue' var currentChapter = 1; var partyMembers = []; var currentEnemies = []; var selectedAction = null; var selectedTarget = null; var turnOrder = []; var currentTurnIndex = 0; var battlePhase = 'menu'; // 'menu', 'action', 'enemy_turn', 'bullet_hell' var bullets = []; var dialogueText = ''; var dialogueIndex = 0; // Enemy dialogue options var enemyDialogues = ["UEE HEE HEE! VISITORS, VISITORS! NOW WE CAN PLAY, PLAY!", "THEN, AFTER YOU, I CAN PLAY WITH EVERYONE ELSE, TOO!", "So what are we playing, exactly...?", "OH, IT'S JUST A SIMPLE NUMBERS GAME.", "WHEN YOUR HP DROPS TO 0, YOU LOSE!", "So that's the kinda game you wanna play, huh...?", "Then, I gotta warn you...", "You're dealing with a couple of sharks.", "UEE HEE HEE! SHARK-TO-SHARK! I WOULDN'T HAVE IT ANY OTHER WAY!", "NOW, NOW!! LET THE GAMES BEGIN!!"]; // UI elements var hpText, tpText, menuButtons = [], dialogueBox, battleBox, itemMenu, enemyDialogBox, enemyDialogText; var heart; // Create party members var kris = new Character('Kris', 'kris'); kris.x = 300; kris.y = 1366; partyMembers.push(kris); game.addChild(kris); var susie = new Character('Susie', 'susie'); susie.x = 300; susie.y = 1000; susie.atk = 15; susie.hp = 120; susie.maxHp = 120; partyMembers.push(susie); game.addChild(susie); var ralsei = new Character('Ralsei', 'ralsei'); ralsei.x = 300; ralsei.y = 1732; ralsei.mag = 15; ralsei.hp = 80; ralsei.maxHp = 80; partyMembers.push(ralsei); game.addChild(ralsei); // Create TP bars for party members (behind characters) var tpBars = []; for (var i = 0; i < partyMembers.length; i++) { var tpBarBg = LK.getAsset('menuButton', { anchorX: 0, anchorY: 0.5 }); tpBarBg.x = 50; tpBarBg.y = partyMembers[i].y; tpBarBg.width = 400; tpBarBg.height = 30; tpBarBg.tint = 0x333333; game.addChild(tpBarBg); var tpBarFill = LK.getAsset('menuButton', { anchorX: 0, anchorY: 0.5 }); tpBarFill.x = 50; tpBarFill.y = partyMembers[i].y; tpBarFill.width = 0; tpBarFill.height = 30; tpBarFill.tint = 0xFFFF00; game.addChild(tpBarFill); tpBars.push({ bg: tpBarBg, fill: tpBarFill, character: partyMembers[i] }); } // Create UI hpText = new Text2('HP: 100/100', { size: 40, fill: 0xFFFFFF }); hpText.anchor.set(1, 1); hpText.x = -50; hpText.y = -50; LK.gui.bottomRight.addChild(hpText); tpText = new Text2('TP: 0/100', { size: 40, fill: 0xFFFF00 }); tpText.anchor.set(1, 1); tpText.x = -50; tpText.y = -100; LK.gui.bottomRight.addChild(tpText); // Create battle menu buttons var actionNames = ['FIGHT', 'ACT', 'ITEM', 'DEFEND', 'SPARE']; for (var i = 0; i < actionNames.length; i++) { var button = LK.getAsset('menuButton', { anchorX: 0.5, anchorY: 0.5 }); button.x = 200 + i * 220; button.y = 2600; button.actionType = actionNames[i]; button.alpha = 0; var buttonText = new Text2(actionNames[i], { size: 32, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); button.addChild(buttonText); button.down = function (x, y, obj) { if (gameState === 'battle' && battlePhase === 'menu') { selectedAction = obj.actionType; LK.getSound('select').play(); if (obj.actionType === 'ITEM') { // Show item menu for (var j = 0; j < menuButtons.length; j++) { tween(menuButtons[j], { alpha: 0 }, { duration: 200 }); } itemMenu.show(); } else { // Hide menu and start action for (var j = 0; j < menuButtons.length; j++) { tween(menuButtons[j], { alpha: 0 }, { duration: 200 }); } battlePhase = 'action'; executePlayerAction(); } } }; menuButtons.push(button); game.addChild(button); } // Create dialogue box dialogueBox = LK.getAsset('dialogBox', { anchorX: 0.5, anchorY: 0 }); dialogueBox.x = 1024; dialogueBox.y = 2532; dialogueBox.alpha = 0; game.addChild(dialogueBox); var dialogueTextObj = new Text2('', { size: 36, fill: 0xFFFFFF }); dialogueTextObj.x = 100; dialogueTextObj.y = 50; dialogueBox.addChild(dialogueTextObj); // Create battle box battleBox = LK.getAsset('battleBox', { anchorX: 0.5, anchorY: 0.5 }); battleBox.x = 1024; battleBox.y = 1366; battleBox.alpha = 0; game.addChild(battleBox); // Create heart for bullet hell heart = new Heart(); heart.x = 1024; heart.y = 1366; heart.alpha = 0; game.addChild(heart); // Create item menu itemMenu = new ItemMenu(); itemMenu.x = 1024; itemMenu.y = 1366; itemMenu.alpha = 0; game.addChild(itemMenu); // Create enemy dialog box enemyDialogBox = LK.getAsset('enemyDialogBox', { anchorX: 0.5, anchorY: 0.5 }); enemyDialogBox.x = 1024; enemyDialogBox.y = 800; enemyDialogBox.alpha = 0; game.addChild(enemyDialogBox); // Create enemy dialog text enemyDialogText = new Text2('', { size: 32, fill: 0xFFFFFF }); enemyDialogText.anchor.set(0.5, 0.5); enemyDialogText.x = 0; enemyDialogText.y = 0; enemyDialogBox.addChild(enemyDialogText); // Functions function startBattle() { gameState = 'battle'; battlePhase = 'menu'; // Create enemy var enemy = new Enemy('Dark Creature', 'enemy', 60, 12); enemy.x = 1700; enemy.y = 1366; currentEnemies.push(enemy); game.addChild(enemy); // Show battle UI for (var i = 0; i < menuButtons.length; i++) { tween(menuButtons[i], { alpha: 1 }, { duration: 500 }); } tween(battleBox, { alpha: 0.3 }, { duration: 500 }); } function executePlayerAction() { var target = currentEnemies[0]; if (!target) return; switch (selectedAction) { case 'FIGHT': var damage = Math.floor(kris.atk + Math.random() * 10); target.takeDamage(damage); LK.getSound('attack').play(); showDialogue(kris.name + ' deals ' + damage + ' damage!'); break; case 'ACT': target.increaseMercy(25); showDialogue(kris.name + ' tries to reason with ' + target.name); break; case 'ITEM': // Item action is now handled by the ItemMenu class showDialogue(kris.name + ' used an item!'); break; case 'DEFEND': kris.isDefending = true; kris.gainTp(15); showDialogue(kris.name + ' guards and gains TP!'); break; case 'SPARE': if (target.mercyLevel >= 100) { target.isSpared = true; showDialogue(target.name + ' was spared!'); currentEnemies = []; target.destroy(); endBattle(); return; } else { showDialogue(target.name + ' is not ready to be spared.'); } break; } LK.setTimeout(function () { if (currentEnemies.length > 0 && !currentEnemies[0].isSpared) { startEnemyTurn(); } else { endBattle(); } }, 2000); } function startEnemyTurn() { battlePhase = 'bullet_hell'; // Show random enemy dialogue first var randomDialogue = enemyDialogues[Math.floor(Math.random() * enemyDialogues.length)]; showEnemyDialogue(randomDialogue); // Delay bullet hell start to show dialogue LK.setTimeout(function () { hideEnemyDialogue(); // Show heart and battle box tween(heart, { alpha: 1 }, { duration: 300 }); tween(battleBox, { alpha: 0.8 }, { duration: 300 }); // Start bullet pattern var bulletPatternCount = 0; var bulletTimer = LK.setInterval(function () { createBulletPattern(); // Play random enemy attack audio only every 3rd bullet pattern to avoid overuse if (bulletPatternCount % 3 === 0) { var enemyAttackAudios = ['enemyrandom1', 'enemyrandom2', 'enemyrandom3']; var randomAttackAudio = enemyAttackAudios[Math.floor(Math.random() * enemyAttackAudios.length)]; LK.getSound(randomAttackAudio).play(); } bulletPatternCount++; }, 600); // End bullet hell after 8 seconds (doubled duration) LK.setTimeout(function () { LK.clearInterval(bulletTimer); endEnemyTurn(); }, 8000); }, 2000); // Show dialogue for 2 seconds } var maxAttackPhases = 10; function createBulletPattern() { var centerX = battleBox.x; var centerY = battleBox.y; // Random attack phase selection var attackPhase = Math.floor(Math.random() * maxAttackPhases); switch (attackPhase) { case 0: // Original circular pattern for (var i = 0; i < 8; i++) { var angle = i / 8 * Math.PI * 2; var bullet = new Bullet(4, angle); bullet.x = centerX; bullet.y = centerY; bullets.push(bullet); game.addChild(bullet); } break; case 1: // Spiral pattern with tween animation for (var i = 0; i < 12; i++) { var angle = i / 12 * Math.PI * 2; var bullet = new Bullet(2, angle); bullet.x = centerX; bullet.y = centerY; bullets.push(bullet); game.addChild(bullet); // Animate bullet in a spiral tween(bullet, { speed: 6 }, { duration: 1000, easing: tween.easeOut }); } break; case 2: // Wave pattern from sides for (var i = 0; i < 6; i++) { // Left side bullets var leftBullet = new Bullet(3, 0); // Moving right leftBullet.x = centerX - 280; leftBullet.y = centerY - 150 + i * 60; bullets.push(leftBullet); game.addChild(leftBullet); // Animate with sine wave motion tween(leftBullet, { direction: Math.PI / 4 }, { duration: 800, easing: tween.sinceOut }); // Right side bullets var rightBullet = new Bullet(3, Math.PI); // Moving left rightBullet.x = centerX + 280; rightBullet.y = centerY - 150 + i * 60; bullets.push(rightBullet); game.addChild(rightBullet); // Animate with sine wave motion tween(rightBullet, { direction: Math.PI * 3 / 4 }, { duration: 800, easing: tween.sinceOut }); } break; case 3: // Expanding ring pattern for (var ring = 0; ring < 3; ring++) { for (var i = 0; i < 6; i++) { var angle = i / 6 * Math.PI * 2; var bullet = new Bullet(1, angle); bullet.x = centerX; bullet.y = centerY; bullets.push(bullet); game.addChild(bullet); // Animate expanding rings with delay tween(bullet, { speed: 4 + ring * 2 }, { duration: 500, easing: tween.easeInOut, onFinish: function () { // Change direction slightly for unpredictability this.direction += (Math.random() - 0.5) * 0.5; }.bind(bullet) }); } // Delay between rings LK.setTimeout(function (ringIndex) { return function () { // Ring animation complete }; }(ring), ring * 200); } break; case 4: // Cross pattern with rotating bullets for (var i = 0; i < 4; i++) { var angle = i * Math.PI / 2; var bullet = new Bullet(3, angle); bullet.x = centerX; bullet.y = centerY; bullets.push(bullet); game.addChild(bullet); // Rotate direction while moving tween(bullet, { direction: angle + Math.PI * 2 }, { duration: 1500, easing: tween.linear }); } // Add diagonal bullets for (var i = 0; i < 4; i++) { var angle = i * Math.PI / 2 + Math.PI / 4; var bullet = new Bullet(2, angle); bullet.x = centerX; bullet.y = centerY; bullets.push(bullet); game.addChild(bullet); tween(bullet, { speed: 5 }, { duration: 800, easing: tween.easeInOut }); } break; case 5: // Zigzag pattern from corners for (var corner = 0; corner < 4; corner++) { var startX = centerX + (corner < 2 ? -250 : 250); var startY = centerY + (corner % 2 === 0 ? -150 : 150); var bullet = new Bullet(4, corner * Math.PI / 2); bullet.x = startX; bullet.y = startY; bullets.push(bullet); game.addChild(bullet); // Create zigzag motion tween(bullet, { direction: bullet.direction + Math.PI / 3 }, { duration: 400, easing: tween.easeInOut, onFinish: function () { tween(this, { direction: this.direction - Math.PI / 1.5 }, { duration: 400, easing: tween.easeInOut }); }.bind(bullet) }); } break; case 6: // Bouncing bullets from walls for (var i = 0; i < 8; i++) { var side = i % 4; // 0=top, 1=right, 2=bottom, 3=left var bullet = new Bullet(5, 0); switch (side) { case 0: // Top bullet.x = centerX - 200 + i / 4 * 400; bullet.y = centerY - 180; bullet.direction = Math.PI / 2; break; case 1: // Right bullet.x = centerX + 280; bullet.y = centerY - 100 + i / 4 * 200; bullet.direction = Math.PI; break; case 2: // Bottom bullet.x = centerX - 200 + i / 4 * 400; bullet.y = centerY + 180; bullet.direction = -Math.PI / 2; break; case 3: // Left bullet.x = centerX - 280; bullet.y = centerY - 100 + i / 4 * 200; bullet.direction = 0; break; } bullets.push(bullet); game.addChild(bullet); // Add bouncing behavior tween(bullet, { speed: 3 }, { duration: 1200, easing: tween.bounceOut }); } break; case 7: // Converging then diverging pattern for (var i = 0; i < 16; i++) { var angle = i / 16 * Math.PI * 2; var distance = 300; var bullet = new Bullet(1, angle + Math.PI); bullet.x = centerX + Math.cos(angle) * distance; bullet.y = centerY + Math.sin(angle) * distance; bullets.push(bullet); game.addChild(bullet); // First converge to center tween(bullet, { speed: 4 }, { duration: 800, easing: tween.easeIn, onFinish: function () { // Then diverge outward tween(this, { direction: this.direction + Math.PI, speed: 6 }, { duration: 600, easing: tween.easeOut }); }.bind(bullet) }); } break; case 8: // Horizontal laser sweeps for (var i = 0; i < 2; i++) { var laser = new LaserBullet(4, 0); // Moving right laser.x = centerX - 300; laser.y = centerY - 80 + i * 160; bullets.push(laser); game.addChild(laser); // Animate speed increase tween(laser, { speed: 8 }, { duration: 1000, easing: tween.easeInOut }); } // Vertical laser sweeps for (var i = 0; i < 2; i++) { var laser = new LaserBullet(3, Math.PI / 2); // Moving down laser.x = centerX - 50 + i * 100; laser.y = centerY - 200; bullets.push(laser); game.addChild(laser); // Animate with slight direction change tween(laser, { direction: Math.PI / 2 + 0.3 }, { duration: 800, easing: tween.easeInOut }); } break; case 9: // Rotating laser beams from center for (var i = 0; i < 4; i++) { var angle = i / 4 * Math.PI * 2; var laser = new LaserBullet(2, angle); laser.x = centerX; laser.y = centerY; bullets.push(laser); game.addChild(laser); // Rotate continuously while moving tween(laser, { direction: angle + Math.PI * 1.5, speed: 5 }, { duration: 1200, easing: tween.linear }); } break; } } function endEnemyTurn() { battlePhase = 'menu'; // Hide heart and dim battle box tween(heart, { alpha: 0 }, { duration: 300 }); tween(battleBox, { alpha: 0.3 }, { duration: 300 }); // Show menu buttons again for (var i = 0; i < menuButtons.length; i++) { tween(menuButtons[i], { alpha: 1 }, { duration: 500 }); } kris.isDefending = false; } function endBattle() { gameState = 'overworld'; // Hide battle UI for (var i = 0; i < menuButtons.length; i++) { tween(menuButtons[i], { alpha: 0 }, { duration: 500 }); } tween(battleBox, { alpha: 0 }, { duration: 500 }); tween(heart, { alpha: 0 }, { duration: 500 }); hideDialogue(); // Clear bullets for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].destroy(); bullets.splice(i, 1); } } function showDialogue(text) { dialogueText = text; dialogueTextObj.setText(text); tween(dialogueBox, { alpha: 1 }, { duration: 300 }); } function showEnemyDialogue(text) { enemyDialogText.setText(text); // Play random enemy dialogue audio var dialogueAudios = ['enemyDialogue1', 'enemyDialogue2', 'enemyDialogue3']; var randomAudio = dialogueAudios[Math.floor(Math.random() * dialogueAudios.length)]; LK.getSound(randomAudio).play(); // Change enemy sprite to talking version if (currentEnemies.length > 0) { var enemy = currentEnemies[0]; // Hide original sprite and show talking sprite enemy.originalSprite = enemy.children[0]; enemy.originalSprite.alpha = 0; var enemyTalkingSprite = LK.getAsset('enemyTalking', { anchorX: 0.5, anchorY: 0.5 }); enemyTalkingSprite.x = 0; enemyTalkingSprite.y = 0; enemy.addChild(enemyTalkingSprite); enemy.talkingSprite = enemyTalkingSprite; } tween(enemyDialogBox, { alpha: 1 }, { duration: 300 }); } function hideEnemyDialogue() { // Restore original enemy sprite if (currentEnemies.length > 0) { var enemy = currentEnemies[0]; if (enemy.talkingSprite) { enemy.talkingSprite.destroy(); enemy.talkingSprite = null; } if (enemy.originalSprite) { enemy.originalSprite.alpha = 1; } } tween(enemyDialogBox, { alpha: 0 }, { duration: 300 }); } function hideDialogue() { tween(dialogueBox, { alpha: 0 }, { duration: 300 }); } function showDeathScreen() { // Stop all music and sounds LK.stopMusic(); // Stop all sound effects LK.getSound('attack').stop(); LK.getSound('damage').stop(); LK.getSound('heal').stop(); LK.getSound('select').stop(); LK.getSound('enemyDialogue1').stop(); LK.getSound('enemyDialogue2').stop(); LK.getSound('enemyDialogue3').stop(); LK.getSound('enemyrandom1').stop(); LK.getSound('enemyrandom2').stop(); LK.getSound('enemyrandom3').stop(); // Play Faint Courage music LK.playMusic('Faint-Courage'); // Fade background to black tween(game, { backgroundColor: 0x000000 }, { duration: 1000 }); // Make everything invisible except the heart tween(backgroundSprite, { alpha: 0 }, { duration: 1000 }); for (var i = 0; i < partyMembers.length; i++) { tween(partyMembers[i], { alpha: 0 }, { duration: 1000 }); } for (var i = 0; i < currentEnemies.length; i++) { tween(currentEnemies[i], { alpha: 0 }, { duration: 1000 }); } tween(battleBox, { alpha: 0 }, { duration: 1000 }); // Keep battleBox invisible until player chooses option battleBox.visible = false; for (var i = 0; i < menuButtons.length; i++) { tween(menuButtons[i], { alpha: 0 }, { duration: 1000 }); } tween(dialogueBox, { alpha: 0 }, { duration: 1000 }); // Make enemy dialog box invisible tween(enemyDialogBox, { alpha: 0 }, { duration: 1000 }); // Make TP bars invisible for (var i = 0; i < tpBars.length; i++) { tween(tpBars[i].bg, { alpha: 0 }, { duration: 1000 }); tween(tpBars[i].fill, { alpha: 0 }, { duration: 1000 }); } // Make all bullets invisible for (var i = 0; i < bullets.length; i++) { tween(bullets[i], { alpha: 0 }, { duration: 1000 }); } // Make item menu invisible tween(itemMenu, { alpha: 0 }, { duration: 1000 }); // Create two heart pieces var heartLeft = LK.getAsset('heart', { anchorX: 1, anchorY: 0.5 }); heartLeft.x = heart.x; heartLeft.y = heart.y; heartLeft.width = heart.width / 2; game.addChild(heartLeft); var heartRight = LK.getAsset('heart', { anchorX: 0, anchorY: 0.5 }); heartRight.x = heart.x; heartRight.y = heart.y; heartRight.width = heart.width / 2; game.addChild(heartRight); // Hide original heart heart.alpha = 0; // Animate heart pieces splitting apart tween(heartLeft, { x: heart.x - 100, rotation: -0.5 }, { duration: 1500, easing: tween.easeOut }); tween(heartRight, { x: heart.x + 100, rotation: 0.5 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { // Show death dialogue and choices after heart split animation LK.setTimeout(function () { showDeathDialogue(); }, 500); } }); } function showDeathDialogue() { // Create death dialogue background (transparent) var deathDialogueBox = new Container(); deathDialogueBox.x = 1024; deathDialogueBox.y = 1366; game.addChild(deathDialogueBox); // Create death dialogue text var deathText = new Text2('Is this the end? You can keep going no matter what happens..', { size: 48, fill: 0xFFFFFF }); deathText.anchor.set(0.5, 0.5); deathText.x = 0; deathText.y = -150; deathDialogueBox.addChild(deathText); // Create "..." button var dotsButton = LK.getAsset('menuButton', { anchorX: 0.5, anchorY: 0.5 }); dotsButton.x = -200; dotsButton.y = 100; dotsButton.width = 300; dotsButton.height = 80; dotsButton.tint = 0x444444; deathDialogueBox.addChild(dotsButton); var dotsText = new Text2('...', { size: 36, fill: 0xFFFFFF }); dotsText.anchor.set(0.5, 0.5); dotsButton.addChild(dotsText); // Create "Proceed" button var proceedButton = LK.getAsset('menuButton', { anchorX: 0.5, anchorY: 0.5 }); proceedButton.x = 200; proceedButton.y = 100; proceedButton.width = 300; proceedButton.height = 80; proceedButton.tint = 0x444444; deathDialogueBox.addChild(proceedButton); var proceedText = new Text2('Proceed', { size: 36, fill: 0xFFFFFF }); proceedText.anchor.set(0.5, 0.5); proceedButton.addChild(proceedText); // Handle "..." button click (death) dotsButton.down = function (x, y, obj) { // Stop Faint Courage music LK.stopMusic(); // Hide dialogue and options immediately deathDialogueBox.alpha = 0; LK.setTimeout(function () { LK.showGameOver(); }, 500); }; // Handle "Proceed" button click (restart with flash) proceedButton.down = function (x, y, obj) { // Stop Faint Courage music LK.stopMusic(); // Hide dialogue and options immediately deathDialogueBox.alpha = 0; // White flash effect LK.effects.flashScreen(0xFFFFFF, 1000); LK.setTimeout(function () { // Restart the game by recreating the game state restartGame(); }, 1000); }; // Fade in the death dialogue tween(deathDialogueBox, { alpha: 1 }, { duration: 1000 }); } function restartGame() { // Destroy current game elements for (var i = 0; i < partyMembers.length; i++) { partyMembers[i].destroy(); } for (var i = 0; i < currentEnemies.length; i++) { currentEnemies[i].destroy(); } for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].destroy(); bullets.splice(i, 1); } // Reset game state gameState = 'overworld'; currentEnemies = []; partyMembers = []; bullets = []; selectedAction = null; selectedTarget = null; battlePhase = 'menu'; // Reset background color game.setBackgroundColor(0x1a1a2e); // Restore background sprite visibility backgroundSprite.alpha = 1; // Recreate party members kris = new Character('Kris', 'kris'); kris.x = 300; kris.y = 1366; kris.hp = 100; kris.tp = 0; partyMembers.push(kris); game.addChild(kris); susie = new Character('Susie', 'susie'); susie.x = 300; susie.y = 1000; susie.atk = 15; susie.hp = 120; susie.maxHp = 120; susie.tp = 0; partyMembers.push(susie); game.addChild(susie); ralsei = new Character('Ralsei', 'ralsei'); ralsei.x = 300; ralsei.y = 1732; ralsei.mag = 15; ralsei.hp = 80; ralsei.maxHp = 80; ralsei.tp = 0; partyMembers.push(ralsei); game.addChild(ralsei); // Reset heart heart.x = 1024; heart.y = 1366; heart.alpha = 0; heart.graceTime = 0; // Reset UI visibility for (var i = 0; i < menuButtons.length; i++) { menuButtons[i].alpha = 0; } battleBox.alpha = 0; battleBox.visible = true; dialogueBox.alpha = 0; enemyDialogBox.alpha = 0; itemMenu.alpha = 0; itemMenu.hide(); // Update TP bars for (var i = 0; i < tpBars.length; i++) { tpBars[i].fill.width = 0; } } // Dragging variables var dragTarget = null; var dragOffset = { x: 0, y: 0 }; // Input handlers game.down = function (x, y, obj) { if (gameState === 'overworld') { // Check if clicking on Kris for dragging var distToKris = Math.sqrt(Math.pow(x - kris.x, 2) + Math.pow(y - kris.y, 2)); if (distToKris < 100) { // Within dragging range dragTarget = kris; dragOffset.x = x - kris.x; dragOffset.y = y - kris.y; } else { // Start battle when touching screen elsewhere startBattle(); } } else if (gameState === 'battle' && battlePhase === 'bullet_hell') { // Move heart to touch position var localPos = battleBox.toLocal({ x: x, y: y }); var boundedX = Math.max(-280, Math.min(280, localPos.x)); var boundedY = Math.max(-180, Math.min(180, localPos.y)); tween(heart, { x: battleBox.x + boundedX, y: battleBox.y + boundedY }, { duration: 100 }); } }; game.up = function (x, y, obj) { // Stop dragging when mouse/touch is released dragTarget = null; }; game.move = function (x, y, obj) { if (gameState === 'overworld' && dragTarget) { // Drag Kris around the screen dragTarget.x = x - dragOffset.x; dragTarget.y = y - dragOffset.y; // Keep Kris within screen bounds dragTarget.x = Math.max(50, Math.min(1998, dragTarget.x)); dragTarget.y = Math.max(50, Math.min(2682, dragTarget.y)); } else if (gameState === 'battle' && battlePhase === 'bullet_hell') { // Move heart to cursor position within battle box bounds var localPos = battleBox.toLocal({ x: x, y: y }); var boundedX = Math.max(-280, Math.min(280, localPos.x)); var boundedY = Math.max(-180, Math.min(180, localPos.y)); heart.x = battleBox.x + boundedX; heart.y = battleBox.y + boundedY; } }; // Start battle sound when game begins, then play music after it ends var battleStartSound = LK.getSound('battlestart'); battleStartSound.play(); // Play THE WORLD REVOLVING music after battlestart sound ends // Estimate battlestart duration and delay music start LK.setTimeout(function () { LK.playMusic('World_Revolving'); }, 3000); // Adjust timing based on battlestart audio length // Main game update loop game.update = function () { // Update UI hpText.setText('HP: ' + kris.hp + '/' + kris.maxHp); tpText.setText('TP: ' + kris.tp + '/' + kris.maxTp); // Update TP bars for (var i = 0; i < tpBars.length; i++) { var tpBar = tpBars[i]; var tpPercent = tpBar.character.tp / tpBar.character.maxTp; tpBar.fill.width = 400 * tpPercent; } // Update bullets and check collisions for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.lastX === undefined) bullet.lastX = bullet.x; if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Check if bullet is outside battle box var distFromCenter = Math.sqrt(Math.pow(bullet.x - battleBox.x, 2) + Math.pow(bullet.y - battleBox.y, 2)); if (distFromCenter > 320) { bullet.destroy(); bullets.splice(i, 1); continue; } // Check collision with heart during bullet hell if (gameState === 'battle' && battlePhase === 'bullet_hell' && heart.alpha > 0) { if (bullet.intersects(heart)) { var distance = Math.sqrt(Math.pow(bullet.x - heart.x, 2) + Math.pow(bullet.y - heart.y, 2)); if (distance < 40 && distance > 20) { // Graze - gain TP kris.gainTp(5); LK.effects.flashObject(heart, 0x00FF00, 200); } else if (distance <= 20) { // Hit - take damage if (heart.takeHit()) { kris.takeDamage(10); LK.getSound('damage').play(); LK.effects.flashScreen(0xFF0000, 300); if (kris.hp <= 0) { showDeathScreen(); } } } } } bullet.lastX = bullet.x; bullet.lastY = bullet.y; } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (speed, direction) {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = speed || 3;
self.direction = direction || 0; // radians
self.lastX = self.x;
self.lastY = self.y;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
// Remove bullet if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
}
};
return self;
});
var Character = Container.expand(function (name, assetId) {
var self = Container.call(this);
// Character graphics
var characterSprite = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Character stats
self.name = name;
self.hp = 100;
self.maxHp = 100;
self.atk = 10;
self.def = 5;
self.mag = 8;
self.speed = 5;
self.tp = 0;
self.maxTp = 100;
// Battle state
self.isDefending = false;
self.lastX = self.x;
self.lastY = self.y;
self.takeDamage = function (amount) {
if (self.isDefending) {
amount = Math.floor(amount * 0.5);
}
self.hp = Math.max(0, self.hp - amount);
// Flash red when taking damage
tween(characterSprite, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(characterSprite, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
};
self.heal = function (amount) {
self.hp = Math.min(self.maxHp, self.hp + amount);
// Flash green when healing
tween(characterSprite, {
tint: 0x00FF00
}, {
duration: 300,
onFinish: function onFinish() {
tween(characterSprite, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
};
self.gainTp = function (amount) {
self.tp = Math.min(self.maxTp, self.tp + amount);
};
return self;
});
var Enemy = Container.expand(function (name, assetId, hp, atk) {
var self = Container.call(this);
var enemySprite = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.name = name;
self.hp = hp || 50;
self.maxHp = self.hp;
self.atk = atk || 15;
self.isSpared = false;
self.mercyLevel = 0;
self.takeDamage = function (amount) {
self.hp = Math.max(0, self.hp - amount);
// Flash white when taking damage
tween(enemySprite, {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemySprite, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
if (self.hp <= 0) {
self.destroy();
}
};
self.increaseMercy = function (amount) {
self.mercyLevel = Math.min(100, self.mercyLevel + (amount || 20));
if (self.mercyLevel >= 100) {
enemySprite.tint = 0xFFFF00; // Yellow tint when spareable
}
};
return self;
});
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartSprite = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.lastX = self.x;
self.lastY = self.y;
self.graceTime = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (self.graceTime > 0) {
self.graceTime--;
heartSprite.alpha = 0.5 + 0.5 * Math.sin(LK.ticks * 0.3);
} else {
heartSprite.alpha = 1;
}
};
self.takeHit = function () {
if (self.graceTime <= 0) {
self.graceTime = 120; // 2 seconds of invincibility
return true;
}
return false;
};
return self;
});
var ItemMenu = Container.expand(function () {
var self = Container.call(this);
self.isVisible = false;
self.selectedItemIndex = 0;
self.items = [{
name: 'Dark Candy',
description: 'Restores 10 HP',
effect: 'heal',
value: 10,
quantity: 3
}, {
name: 'Revive Mint',
description: 'Restores 30 HP',
effect: 'heal',
value: 30,
quantity: 2
}, {
name: 'Tension Bit',
description: 'Restores 20 TP',
effect: 'tp',
value: 20,
quantity: 1
}];
// Create menu background
var menuBg = self.attachAsset('dialogBox', {
anchorX: 0.5,
anchorY: 0.5
});
menuBg.width = 1200;
menuBg.height = 800;
menuBg.tint = 0x222222;
// Create title text
var titleText = new Text2('ITEMS', {
size: 48,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -350;
self.addChild(titleText);
// Create item buttons and text
self.itemButtons = [];
self.itemTexts = [];
for (var i = 0; i < self.items.length; i++) {
var itemButton = LK.getAsset('menuButton', {
anchorX: 0,
anchorY: 0.5
});
itemButton.x = -550;
itemButton.y = -200 + i * 100;
itemButton.width = 1000;
itemButton.height = 80;
itemButton.tint = 0x444444;
itemButton.itemIndex = i;
self.addChild(itemButton);
self.itemButtons.push(itemButton);
var itemText = new Text2(self.items[i].name + ' x' + self.items[i].quantity, {
size: 32,
fill: 0xFFFFFF
});
itemText.x = -500;
itemText.y = -200 + i * 100;
itemText.anchor.set(0, 0.5);
self.addChild(itemText);
self.itemTexts.push(itemText);
var descText = new Text2(self.items[i].description, {
size: 24,
fill: 0xCCCCCC
});
descText.x = -200;
descText.y = -200 + i * 100;
descText.anchor.set(0, 0.5);
self.addChild(descText);
}
// Create back button
var backButton = LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
backButton.x = 0;
backButton.y = 300;
backButton.width = 200;
backButton.height = 60;
backButton.tint = 0x666666;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 32,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
self.show = function () {
self.isVisible = true;
self.alpha = 1;
self.updateSelection();
};
self.hide = function () {
self.isVisible = false;
self.alpha = 0;
};
self.updateSelection = function () {
for (var i = 0; i < self.itemButtons.length; i++) {
if (i === self.selectedItemIndex) {
self.itemButtons[i].tint = 0x666666;
} else {
self.itemButtons[i].tint = 0x444444;
}
}
};
self.useSelectedItem = function () {
var item = self.items[self.selectedItemIndex];
if (item.quantity <= 0) return false;
item.quantity--;
self.itemTexts[self.selectedItemIndex].setText(item.name + ' x' + item.quantity);
if (item.effect === 'heal') {
kris.heal(item.value);
} else if (item.effect === 'tp') {
kris.gainTp(item.value);
}
return true;
};
// Handle button clicks
for (var i = 0; i < self.itemButtons.length; i++) {
self.itemButtons[i].down = function (x, y, obj) {
if (self.isVisible && self.items[obj.itemIndex].quantity > 0) {
self.selectedItemIndex = obj.itemIndex;
if (self.useSelectedItem()) {
LK.getSound('heal').play();
self.hide();
if (gameState === 'battle') {
selectedAction = 'ITEM';
executePlayerAction();
}
}
}
};
}
backButton.down = function (x, y, obj) {
if (self.isVisible) {
LK.getSound('select').play();
self.hide();
if (gameState === 'battle') {
battlePhase = 'menu';
for (var j = 0; j < menuButtons.length; j++) {
tween(menuButtons[j], {
alpha: 1
}, {
duration: 200
});
}
}
}
};
return self;
});
var LaserBullet = Container.expand(function (speed, direction) {
var self = Container.call(this);
var laserSprite = self.attachAsset('laserBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = speed || 6;
self.direction = direction || 0;
self.lastX = self.x;
self.lastY = self.y;
// Set rotation to match direction
laserSprite.rotation = self.direction;
// Pulsing effect
self.pulseTime = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Track heart position if in bullet hell phase
if (gameState === 'battle' && battlePhase === 'bullet_hell' && heart && heart.alpha > 0) {
// Calculate angle to heart
var deltaX = heart.x - self.x;
var deltaY = heart.y - self.y;
var targetAngle = Math.atan2(deltaY, deltaX);
// Smoothly rotate towards heart
var angleDiff = targetAngle - self.direction;
// Normalize angle difference to -PI to PI
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
// Rotate at a maximum rate for smooth turning
var rotationSpeed = 0.08;
self.direction += Math.sign(angleDiff) * Math.min(Math.abs(angleDiff), rotationSpeed);
// Update sprite rotation to match direction
laserSprite.rotation = self.direction;
}
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
// Pulsing scale effect
self.pulseTime += 0.2;
laserSprite.scaleY = 1 + 0.3 * Math.sin(self.pulseTime);
// Remove bullet if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
// Game state variables
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Create background sprite
// Sounds
// World elements
// UI elements
// Battle elements
// Enemy sprites
// Character sprites
// Game state variables
var backgroundSprite = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
scaleX: 1,
scaleY: 1
});
backgroundSprite.x = 0;
backgroundSprite.y = 0;
game.addChild(backgroundSprite);
// Move background to the bottom layer
game.setChildIndex(backgroundSprite, 0);
var gameState = 'overworld'; // 'overworld', 'battle', 'dialogue'
var currentChapter = 1;
var partyMembers = [];
var currentEnemies = [];
var selectedAction = null;
var selectedTarget = null;
var turnOrder = [];
var currentTurnIndex = 0;
var battlePhase = 'menu'; // 'menu', 'action', 'enemy_turn', 'bullet_hell'
var bullets = [];
var dialogueText = '';
var dialogueIndex = 0;
// Enemy dialogue options
var enemyDialogues = ["UEE HEE HEE! VISITORS, VISITORS! NOW WE CAN PLAY, PLAY!", "THEN, AFTER YOU, I CAN PLAY WITH EVERYONE ELSE, TOO!", "So what are we playing, exactly...?", "OH, IT'S JUST A SIMPLE NUMBERS GAME.", "WHEN YOUR HP DROPS TO 0, YOU LOSE!", "So that's the kinda game you wanna play, huh...?", "Then, I gotta warn you...", "You're dealing with a couple of sharks.", "UEE HEE HEE! SHARK-TO-SHARK! I WOULDN'T HAVE IT ANY OTHER WAY!", "NOW, NOW!! LET THE GAMES BEGIN!!"];
// UI elements
var hpText,
tpText,
menuButtons = [],
dialogueBox,
battleBox,
itemMenu,
enemyDialogBox,
enemyDialogText;
var heart;
// Create party members
var kris = new Character('Kris', 'kris');
kris.x = 300;
kris.y = 1366;
partyMembers.push(kris);
game.addChild(kris);
var susie = new Character('Susie', 'susie');
susie.x = 300;
susie.y = 1000;
susie.atk = 15;
susie.hp = 120;
susie.maxHp = 120;
partyMembers.push(susie);
game.addChild(susie);
var ralsei = new Character('Ralsei', 'ralsei');
ralsei.x = 300;
ralsei.y = 1732;
ralsei.mag = 15;
ralsei.hp = 80;
ralsei.maxHp = 80;
partyMembers.push(ralsei);
game.addChild(ralsei);
// Create TP bars for party members (behind characters)
var tpBars = [];
for (var i = 0; i < partyMembers.length; i++) {
var tpBarBg = LK.getAsset('menuButton', {
anchorX: 0,
anchorY: 0.5
});
tpBarBg.x = 50;
tpBarBg.y = partyMembers[i].y;
tpBarBg.width = 400;
tpBarBg.height = 30;
tpBarBg.tint = 0x333333;
game.addChild(tpBarBg);
var tpBarFill = LK.getAsset('menuButton', {
anchorX: 0,
anchorY: 0.5
});
tpBarFill.x = 50;
tpBarFill.y = partyMembers[i].y;
tpBarFill.width = 0;
tpBarFill.height = 30;
tpBarFill.tint = 0xFFFF00;
game.addChild(tpBarFill);
tpBars.push({
bg: tpBarBg,
fill: tpBarFill,
character: partyMembers[i]
});
}
// Create UI
hpText = new Text2('HP: 100/100', {
size: 40,
fill: 0xFFFFFF
});
hpText.anchor.set(1, 1);
hpText.x = -50;
hpText.y = -50;
LK.gui.bottomRight.addChild(hpText);
tpText = new Text2('TP: 0/100', {
size: 40,
fill: 0xFFFF00
});
tpText.anchor.set(1, 1);
tpText.x = -50;
tpText.y = -100;
LK.gui.bottomRight.addChild(tpText);
// Create battle menu buttons
var actionNames = ['FIGHT', 'ACT', 'ITEM', 'DEFEND', 'SPARE'];
for (var i = 0; i < actionNames.length; i++) {
var button = LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
button.x = 200 + i * 220;
button.y = 2600;
button.actionType = actionNames[i];
button.alpha = 0;
var buttonText = new Text2(actionNames[i], {
size: 32,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
button.addChild(buttonText);
button.down = function (x, y, obj) {
if (gameState === 'battle' && battlePhase === 'menu') {
selectedAction = obj.actionType;
LK.getSound('select').play();
if (obj.actionType === 'ITEM') {
// Show item menu
for (var j = 0; j < menuButtons.length; j++) {
tween(menuButtons[j], {
alpha: 0
}, {
duration: 200
});
}
itemMenu.show();
} else {
// Hide menu and start action
for (var j = 0; j < menuButtons.length; j++) {
tween(menuButtons[j], {
alpha: 0
}, {
duration: 200
});
}
battlePhase = 'action';
executePlayerAction();
}
}
};
menuButtons.push(button);
game.addChild(button);
}
// Create dialogue box
dialogueBox = LK.getAsset('dialogBox', {
anchorX: 0.5,
anchorY: 0
});
dialogueBox.x = 1024;
dialogueBox.y = 2532;
dialogueBox.alpha = 0;
game.addChild(dialogueBox);
var dialogueTextObj = new Text2('', {
size: 36,
fill: 0xFFFFFF
});
dialogueTextObj.x = 100;
dialogueTextObj.y = 50;
dialogueBox.addChild(dialogueTextObj);
// Create battle box
battleBox = LK.getAsset('battleBox', {
anchorX: 0.5,
anchorY: 0.5
});
battleBox.x = 1024;
battleBox.y = 1366;
battleBox.alpha = 0;
game.addChild(battleBox);
// Create heart for bullet hell
heart = new Heart();
heart.x = 1024;
heart.y = 1366;
heart.alpha = 0;
game.addChild(heart);
// Create item menu
itemMenu = new ItemMenu();
itemMenu.x = 1024;
itemMenu.y = 1366;
itemMenu.alpha = 0;
game.addChild(itemMenu);
// Create enemy dialog box
enemyDialogBox = LK.getAsset('enemyDialogBox', {
anchorX: 0.5,
anchorY: 0.5
});
enemyDialogBox.x = 1024;
enemyDialogBox.y = 800;
enemyDialogBox.alpha = 0;
game.addChild(enemyDialogBox);
// Create enemy dialog text
enemyDialogText = new Text2('', {
size: 32,
fill: 0xFFFFFF
});
enemyDialogText.anchor.set(0.5, 0.5);
enemyDialogText.x = 0;
enemyDialogText.y = 0;
enemyDialogBox.addChild(enemyDialogText);
// Functions
function startBattle() {
gameState = 'battle';
battlePhase = 'menu';
// Create enemy
var enemy = new Enemy('Dark Creature', 'enemy', 60, 12);
enemy.x = 1700;
enemy.y = 1366;
currentEnemies.push(enemy);
game.addChild(enemy);
// Show battle UI
for (var i = 0; i < menuButtons.length; i++) {
tween(menuButtons[i], {
alpha: 1
}, {
duration: 500
});
}
tween(battleBox, {
alpha: 0.3
}, {
duration: 500
});
}
function executePlayerAction() {
var target = currentEnemies[0];
if (!target) return;
switch (selectedAction) {
case 'FIGHT':
var damage = Math.floor(kris.atk + Math.random() * 10);
target.takeDamage(damage);
LK.getSound('attack').play();
showDialogue(kris.name + ' deals ' + damage + ' damage!');
break;
case 'ACT':
target.increaseMercy(25);
showDialogue(kris.name + ' tries to reason with ' + target.name);
break;
case 'ITEM':
// Item action is now handled by the ItemMenu class
showDialogue(kris.name + ' used an item!');
break;
case 'DEFEND':
kris.isDefending = true;
kris.gainTp(15);
showDialogue(kris.name + ' guards and gains TP!');
break;
case 'SPARE':
if (target.mercyLevel >= 100) {
target.isSpared = true;
showDialogue(target.name + ' was spared!');
currentEnemies = [];
target.destroy();
endBattle();
return;
} else {
showDialogue(target.name + ' is not ready to be spared.');
}
break;
}
LK.setTimeout(function () {
if (currentEnemies.length > 0 && !currentEnemies[0].isSpared) {
startEnemyTurn();
} else {
endBattle();
}
}, 2000);
}
function startEnemyTurn() {
battlePhase = 'bullet_hell';
// Show random enemy dialogue first
var randomDialogue = enemyDialogues[Math.floor(Math.random() * enemyDialogues.length)];
showEnemyDialogue(randomDialogue);
// Delay bullet hell start to show dialogue
LK.setTimeout(function () {
hideEnemyDialogue();
// Show heart and battle box
tween(heart, {
alpha: 1
}, {
duration: 300
});
tween(battleBox, {
alpha: 0.8
}, {
duration: 300
});
// Start bullet pattern
var bulletPatternCount = 0;
var bulletTimer = LK.setInterval(function () {
createBulletPattern();
// Play random enemy attack audio only every 3rd bullet pattern to avoid overuse
if (bulletPatternCount % 3 === 0) {
var enemyAttackAudios = ['enemyrandom1', 'enemyrandom2', 'enemyrandom3'];
var randomAttackAudio = enemyAttackAudios[Math.floor(Math.random() * enemyAttackAudios.length)];
LK.getSound(randomAttackAudio).play();
}
bulletPatternCount++;
}, 600);
// End bullet hell after 8 seconds (doubled duration)
LK.setTimeout(function () {
LK.clearInterval(bulletTimer);
endEnemyTurn();
}, 8000);
}, 2000); // Show dialogue for 2 seconds
}
var maxAttackPhases = 10;
function createBulletPattern() {
var centerX = battleBox.x;
var centerY = battleBox.y;
// Random attack phase selection
var attackPhase = Math.floor(Math.random() * maxAttackPhases);
switch (attackPhase) {
case 0:
// Original circular pattern
for (var i = 0; i < 8; i++) {
var angle = i / 8 * Math.PI * 2;
var bullet = new Bullet(4, angle);
bullet.x = centerX;
bullet.y = centerY;
bullets.push(bullet);
game.addChild(bullet);
}
break;
case 1:
// Spiral pattern with tween animation
for (var i = 0; i < 12; i++) {
var angle = i / 12 * Math.PI * 2;
var bullet = new Bullet(2, angle);
bullet.x = centerX;
bullet.y = centerY;
bullets.push(bullet);
game.addChild(bullet);
// Animate bullet in a spiral
tween(bullet, {
speed: 6
}, {
duration: 1000,
easing: tween.easeOut
});
}
break;
case 2:
// Wave pattern from sides
for (var i = 0; i < 6; i++) {
// Left side bullets
var leftBullet = new Bullet(3, 0); // Moving right
leftBullet.x = centerX - 280;
leftBullet.y = centerY - 150 + i * 60;
bullets.push(leftBullet);
game.addChild(leftBullet);
// Animate with sine wave motion
tween(leftBullet, {
direction: Math.PI / 4
}, {
duration: 800,
easing: tween.sinceOut
});
// Right side bullets
var rightBullet = new Bullet(3, Math.PI); // Moving left
rightBullet.x = centerX + 280;
rightBullet.y = centerY - 150 + i * 60;
bullets.push(rightBullet);
game.addChild(rightBullet);
// Animate with sine wave motion
tween(rightBullet, {
direction: Math.PI * 3 / 4
}, {
duration: 800,
easing: tween.sinceOut
});
}
break;
case 3:
// Expanding ring pattern
for (var ring = 0; ring < 3; ring++) {
for (var i = 0; i < 6; i++) {
var angle = i / 6 * Math.PI * 2;
var bullet = new Bullet(1, angle);
bullet.x = centerX;
bullet.y = centerY;
bullets.push(bullet);
game.addChild(bullet);
// Animate expanding rings with delay
tween(bullet, {
speed: 4 + ring * 2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function () {
// Change direction slightly for unpredictability
this.direction += (Math.random() - 0.5) * 0.5;
}.bind(bullet)
});
}
// Delay between rings
LK.setTimeout(function (ringIndex) {
return function () {
// Ring animation complete
};
}(ring), ring * 200);
}
break;
case 4:
// Cross pattern with rotating bullets
for (var i = 0; i < 4; i++) {
var angle = i * Math.PI / 2;
var bullet = new Bullet(3, angle);
bullet.x = centerX;
bullet.y = centerY;
bullets.push(bullet);
game.addChild(bullet);
// Rotate direction while moving
tween(bullet, {
direction: angle + Math.PI * 2
}, {
duration: 1500,
easing: tween.linear
});
}
// Add diagonal bullets
for (var i = 0; i < 4; i++) {
var angle = i * Math.PI / 2 + Math.PI / 4;
var bullet = new Bullet(2, angle);
bullet.x = centerX;
bullet.y = centerY;
bullets.push(bullet);
game.addChild(bullet);
tween(bullet, {
speed: 5
}, {
duration: 800,
easing: tween.easeInOut
});
}
break;
case 5:
// Zigzag pattern from corners
for (var corner = 0; corner < 4; corner++) {
var startX = centerX + (corner < 2 ? -250 : 250);
var startY = centerY + (corner % 2 === 0 ? -150 : 150);
var bullet = new Bullet(4, corner * Math.PI / 2);
bullet.x = startX;
bullet.y = startY;
bullets.push(bullet);
game.addChild(bullet);
// Create zigzag motion
tween(bullet, {
direction: bullet.direction + Math.PI / 3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function () {
tween(this, {
direction: this.direction - Math.PI / 1.5
}, {
duration: 400,
easing: tween.easeInOut
});
}.bind(bullet)
});
}
break;
case 6:
// Bouncing bullets from walls
for (var i = 0; i < 8; i++) {
var side = i % 4; // 0=top, 1=right, 2=bottom, 3=left
var bullet = new Bullet(5, 0);
switch (side) {
case 0:
// Top
bullet.x = centerX - 200 + i / 4 * 400;
bullet.y = centerY - 180;
bullet.direction = Math.PI / 2;
break;
case 1:
// Right
bullet.x = centerX + 280;
bullet.y = centerY - 100 + i / 4 * 200;
bullet.direction = Math.PI;
break;
case 2:
// Bottom
bullet.x = centerX - 200 + i / 4 * 400;
bullet.y = centerY + 180;
bullet.direction = -Math.PI / 2;
break;
case 3:
// Left
bullet.x = centerX - 280;
bullet.y = centerY - 100 + i / 4 * 200;
bullet.direction = 0;
break;
}
bullets.push(bullet);
game.addChild(bullet);
// Add bouncing behavior
tween(bullet, {
speed: 3
}, {
duration: 1200,
easing: tween.bounceOut
});
}
break;
case 7:
// Converging then diverging pattern
for (var i = 0; i < 16; i++) {
var angle = i / 16 * Math.PI * 2;
var distance = 300;
var bullet = new Bullet(1, angle + Math.PI);
bullet.x = centerX + Math.cos(angle) * distance;
bullet.y = centerY + Math.sin(angle) * distance;
bullets.push(bullet);
game.addChild(bullet);
// First converge to center
tween(bullet, {
speed: 4
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function () {
// Then diverge outward
tween(this, {
direction: this.direction + Math.PI,
speed: 6
}, {
duration: 600,
easing: tween.easeOut
});
}.bind(bullet)
});
}
break;
case 8:
// Horizontal laser sweeps
for (var i = 0; i < 2; i++) {
var laser = new LaserBullet(4, 0); // Moving right
laser.x = centerX - 300;
laser.y = centerY - 80 + i * 160;
bullets.push(laser);
game.addChild(laser);
// Animate speed increase
tween(laser, {
speed: 8
}, {
duration: 1000,
easing: tween.easeInOut
});
}
// Vertical laser sweeps
for (var i = 0; i < 2; i++) {
var laser = new LaserBullet(3, Math.PI / 2); // Moving down
laser.x = centerX - 50 + i * 100;
laser.y = centerY - 200;
bullets.push(laser);
game.addChild(laser);
// Animate with slight direction change
tween(laser, {
direction: Math.PI / 2 + 0.3
}, {
duration: 800,
easing: tween.easeInOut
});
}
break;
case 9:
// Rotating laser beams from center
for (var i = 0; i < 4; i++) {
var angle = i / 4 * Math.PI * 2;
var laser = new LaserBullet(2, angle);
laser.x = centerX;
laser.y = centerY;
bullets.push(laser);
game.addChild(laser);
// Rotate continuously while moving
tween(laser, {
direction: angle + Math.PI * 1.5,
speed: 5
}, {
duration: 1200,
easing: tween.linear
});
}
break;
}
}
function endEnemyTurn() {
battlePhase = 'menu';
// Hide heart and dim battle box
tween(heart, {
alpha: 0
}, {
duration: 300
});
tween(battleBox, {
alpha: 0.3
}, {
duration: 300
});
// Show menu buttons again
for (var i = 0; i < menuButtons.length; i++) {
tween(menuButtons[i], {
alpha: 1
}, {
duration: 500
});
}
kris.isDefending = false;
}
function endBattle() {
gameState = 'overworld';
// Hide battle UI
for (var i = 0; i < menuButtons.length; i++) {
tween(menuButtons[i], {
alpha: 0
}, {
duration: 500
});
}
tween(battleBox, {
alpha: 0
}, {
duration: 500
});
tween(heart, {
alpha: 0
}, {
duration: 500
});
hideDialogue();
// Clear bullets
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
bullets.splice(i, 1);
}
}
function showDialogue(text) {
dialogueText = text;
dialogueTextObj.setText(text);
tween(dialogueBox, {
alpha: 1
}, {
duration: 300
});
}
function showEnemyDialogue(text) {
enemyDialogText.setText(text);
// Play random enemy dialogue audio
var dialogueAudios = ['enemyDialogue1', 'enemyDialogue2', 'enemyDialogue3'];
var randomAudio = dialogueAudios[Math.floor(Math.random() * dialogueAudios.length)];
LK.getSound(randomAudio).play();
// Change enemy sprite to talking version
if (currentEnemies.length > 0) {
var enemy = currentEnemies[0];
// Hide original sprite and show talking sprite
enemy.originalSprite = enemy.children[0];
enemy.originalSprite.alpha = 0;
var enemyTalkingSprite = LK.getAsset('enemyTalking', {
anchorX: 0.5,
anchorY: 0.5
});
enemyTalkingSprite.x = 0;
enemyTalkingSprite.y = 0;
enemy.addChild(enemyTalkingSprite);
enemy.talkingSprite = enemyTalkingSprite;
}
tween(enemyDialogBox, {
alpha: 1
}, {
duration: 300
});
}
function hideEnemyDialogue() {
// Restore original enemy sprite
if (currentEnemies.length > 0) {
var enemy = currentEnemies[0];
if (enemy.talkingSprite) {
enemy.talkingSprite.destroy();
enemy.talkingSprite = null;
}
if (enemy.originalSprite) {
enemy.originalSprite.alpha = 1;
}
}
tween(enemyDialogBox, {
alpha: 0
}, {
duration: 300
});
}
function hideDialogue() {
tween(dialogueBox, {
alpha: 0
}, {
duration: 300
});
}
function showDeathScreen() {
// Stop all music and sounds
LK.stopMusic();
// Stop all sound effects
LK.getSound('attack').stop();
LK.getSound('damage').stop();
LK.getSound('heal').stop();
LK.getSound('select').stop();
LK.getSound('enemyDialogue1').stop();
LK.getSound('enemyDialogue2').stop();
LK.getSound('enemyDialogue3').stop();
LK.getSound('enemyrandom1').stop();
LK.getSound('enemyrandom2').stop();
LK.getSound('enemyrandom3').stop();
// Play Faint Courage music
LK.playMusic('Faint-Courage');
// Fade background to black
tween(game, {
backgroundColor: 0x000000
}, {
duration: 1000
});
// Make everything invisible except the heart
tween(backgroundSprite, {
alpha: 0
}, {
duration: 1000
});
for (var i = 0; i < partyMembers.length; i++) {
tween(partyMembers[i], {
alpha: 0
}, {
duration: 1000
});
}
for (var i = 0; i < currentEnemies.length; i++) {
tween(currentEnemies[i], {
alpha: 0
}, {
duration: 1000
});
}
tween(battleBox, {
alpha: 0
}, {
duration: 1000
});
// Keep battleBox invisible until player chooses option
battleBox.visible = false;
for (var i = 0; i < menuButtons.length; i++) {
tween(menuButtons[i], {
alpha: 0
}, {
duration: 1000
});
}
tween(dialogueBox, {
alpha: 0
}, {
duration: 1000
});
// Make enemy dialog box invisible
tween(enemyDialogBox, {
alpha: 0
}, {
duration: 1000
});
// Make TP bars invisible
for (var i = 0; i < tpBars.length; i++) {
tween(tpBars[i].bg, {
alpha: 0
}, {
duration: 1000
});
tween(tpBars[i].fill, {
alpha: 0
}, {
duration: 1000
});
}
// Make all bullets invisible
for (var i = 0; i < bullets.length; i++) {
tween(bullets[i], {
alpha: 0
}, {
duration: 1000
});
}
// Make item menu invisible
tween(itemMenu, {
alpha: 0
}, {
duration: 1000
});
// Create two heart pieces
var heartLeft = LK.getAsset('heart', {
anchorX: 1,
anchorY: 0.5
});
heartLeft.x = heart.x;
heartLeft.y = heart.y;
heartLeft.width = heart.width / 2;
game.addChild(heartLeft);
var heartRight = LK.getAsset('heart', {
anchorX: 0,
anchorY: 0.5
});
heartRight.x = heart.x;
heartRight.y = heart.y;
heartRight.width = heart.width / 2;
game.addChild(heartRight);
// Hide original heart
heart.alpha = 0;
// Animate heart pieces splitting apart
tween(heartLeft, {
x: heart.x - 100,
rotation: -0.5
}, {
duration: 1500,
easing: tween.easeOut
});
tween(heartRight, {
x: heart.x + 100,
rotation: 0.5
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show death dialogue and choices after heart split animation
LK.setTimeout(function () {
showDeathDialogue();
}, 500);
}
});
}
function showDeathDialogue() {
// Create death dialogue background (transparent)
var deathDialogueBox = new Container();
deathDialogueBox.x = 1024;
deathDialogueBox.y = 1366;
game.addChild(deathDialogueBox);
// Create death dialogue text
var deathText = new Text2('Is this the end? You can keep going no matter what happens..', {
size: 48,
fill: 0xFFFFFF
});
deathText.anchor.set(0.5, 0.5);
deathText.x = 0;
deathText.y = -150;
deathDialogueBox.addChild(deathText);
// Create "..." button
var dotsButton = LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
dotsButton.x = -200;
dotsButton.y = 100;
dotsButton.width = 300;
dotsButton.height = 80;
dotsButton.tint = 0x444444;
deathDialogueBox.addChild(dotsButton);
var dotsText = new Text2('...', {
size: 36,
fill: 0xFFFFFF
});
dotsText.anchor.set(0.5, 0.5);
dotsButton.addChild(dotsText);
// Create "Proceed" button
var proceedButton = LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
proceedButton.x = 200;
proceedButton.y = 100;
proceedButton.width = 300;
proceedButton.height = 80;
proceedButton.tint = 0x444444;
deathDialogueBox.addChild(proceedButton);
var proceedText = new Text2('Proceed', {
size: 36,
fill: 0xFFFFFF
});
proceedText.anchor.set(0.5, 0.5);
proceedButton.addChild(proceedText);
// Handle "..." button click (death)
dotsButton.down = function (x, y, obj) {
// Stop Faint Courage music
LK.stopMusic();
// Hide dialogue and options immediately
deathDialogueBox.alpha = 0;
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
};
// Handle "Proceed" button click (restart with flash)
proceedButton.down = function (x, y, obj) {
// Stop Faint Courage music
LK.stopMusic();
// Hide dialogue and options immediately
deathDialogueBox.alpha = 0;
// White flash effect
LK.effects.flashScreen(0xFFFFFF, 1000);
LK.setTimeout(function () {
// Restart the game by recreating the game state
restartGame();
}, 1000);
};
// Fade in the death dialogue
tween(deathDialogueBox, {
alpha: 1
}, {
duration: 1000
});
}
function restartGame() {
// Destroy current game elements
for (var i = 0; i < partyMembers.length; i++) {
partyMembers[i].destroy();
}
for (var i = 0; i < currentEnemies.length; i++) {
currentEnemies[i].destroy();
}
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
bullets.splice(i, 1);
}
// Reset game state
gameState = 'overworld';
currentEnemies = [];
partyMembers = [];
bullets = [];
selectedAction = null;
selectedTarget = null;
battlePhase = 'menu';
// Reset background color
game.setBackgroundColor(0x1a1a2e);
// Restore background sprite visibility
backgroundSprite.alpha = 1;
// Recreate party members
kris = new Character('Kris', 'kris');
kris.x = 300;
kris.y = 1366;
kris.hp = 100;
kris.tp = 0;
partyMembers.push(kris);
game.addChild(kris);
susie = new Character('Susie', 'susie');
susie.x = 300;
susie.y = 1000;
susie.atk = 15;
susie.hp = 120;
susie.maxHp = 120;
susie.tp = 0;
partyMembers.push(susie);
game.addChild(susie);
ralsei = new Character('Ralsei', 'ralsei');
ralsei.x = 300;
ralsei.y = 1732;
ralsei.mag = 15;
ralsei.hp = 80;
ralsei.maxHp = 80;
ralsei.tp = 0;
partyMembers.push(ralsei);
game.addChild(ralsei);
// Reset heart
heart.x = 1024;
heart.y = 1366;
heart.alpha = 0;
heart.graceTime = 0;
// Reset UI visibility
for (var i = 0; i < menuButtons.length; i++) {
menuButtons[i].alpha = 0;
}
battleBox.alpha = 0;
battleBox.visible = true;
dialogueBox.alpha = 0;
enemyDialogBox.alpha = 0;
itemMenu.alpha = 0;
itemMenu.hide();
// Update TP bars
for (var i = 0; i < tpBars.length; i++) {
tpBars[i].fill.width = 0;
}
}
// Dragging variables
var dragTarget = null;
var dragOffset = {
x: 0,
y: 0
};
// Input handlers
game.down = function (x, y, obj) {
if (gameState === 'overworld') {
// Check if clicking on Kris for dragging
var distToKris = Math.sqrt(Math.pow(x - kris.x, 2) + Math.pow(y - kris.y, 2));
if (distToKris < 100) {
// Within dragging range
dragTarget = kris;
dragOffset.x = x - kris.x;
dragOffset.y = y - kris.y;
} else {
// Start battle when touching screen elsewhere
startBattle();
}
} else if (gameState === 'battle' && battlePhase === 'bullet_hell') {
// Move heart to touch position
var localPos = battleBox.toLocal({
x: x,
y: y
});
var boundedX = Math.max(-280, Math.min(280, localPos.x));
var boundedY = Math.max(-180, Math.min(180, localPos.y));
tween(heart, {
x: battleBox.x + boundedX,
y: battleBox.y + boundedY
}, {
duration: 100
});
}
};
game.up = function (x, y, obj) {
// Stop dragging when mouse/touch is released
dragTarget = null;
};
game.move = function (x, y, obj) {
if (gameState === 'overworld' && dragTarget) {
// Drag Kris around the screen
dragTarget.x = x - dragOffset.x;
dragTarget.y = y - dragOffset.y;
// Keep Kris within screen bounds
dragTarget.x = Math.max(50, Math.min(1998, dragTarget.x));
dragTarget.y = Math.max(50, Math.min(2682, dragTarget.y));
} else if (gameState === 'battle' && battlePhase === 'bullet_hell') {
// Move heart to cursor position within battle box bounds
var localPos = battleBox.toLocal({
x: x,
y: y
});
var boundedX = Math.max(-280, Math.min(280, localPos.x));
var boundedY = Math.max(-180, Math.min(180, localPos.y));
heart.x = battleBox.x + boundedX;
heart.y = battleBox.y + boundedY;
}
};
// Start battle sound when game begins, then play music after it ends
var battleStartSound = LK.getSound('battlestart');
battleStartSound.play();
// Play THE WORLD REVOLVING music after battlestart sound ends
// Estimate battlestart duration and delay music start
LK.setTimeout(function () {
LK.playMusic('World_Revolving');
}, 3000); // Adjust timing based on battlestart audio length
// Main game update loop
game.update = function () {
// Update UI
hpText.setText('HP: ' + kris.hp + '/' + kris.maxHp);
tpText.setText('TP: ' + kris.tp + '/' + kris.maxTp);
// Update TP bars
for (var i = 0; i < tpBars.length; i++) {
var tpBar = tpBars[i];
var tpPercent = tpBar.character.tp / tpBar.character.maxTp;
tpBar.fill.width = 400 * tpPercent;
}
// Update bullets and check collisions
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.lastX === undefined) bullet.lastX = bullet.x;
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Check if bullet is outside battle box
var distFromCenter = Math.sqrt(Math.pow(bullet.x - battleBox.x, 2) + Math.pow(bullet.y - battleBox.y, 2));
if (distFromCenter > 320) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with heart during bullet hell
if (gameState === 'battle' && battlePhase === 'bullet_hell' && heart.alpha > 0) {
if (bullet.intersects(heart)) {
var distance = Math.sqrt(Math.pow(bullet.x - heart.x, 2) + Math.pow(bullet.y - heart.y, 2));
if (distance < 40 && distance > 20) {
// Graze - gain TP
kris.gainTp(5);
LK.effects.flashObject(heart, 0x00FF00, 200);
} else if (distance <= 20) {
// Hit - take damage
if (heart.takeHit()) {
kris.takeDamage(10);
LK.getSound('damage').play();
LK.effects.flashScreen(0xFF0000, 300);
if (kris.hp <= 0) {
showDeathScreen();
}
}
}
}
}
bullet.lastX = bullet.x;
bullet.lastY = bullet.y;
}
};