User prompt
put the 3 characters(susie,kris and ralsei) at the left middle
User prompt
add more attacking phases to the enemy ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make the hearth move with the cursor
User prompt
make the hearth move with the mouse
User prompt
Generate the first version of the source code of my game: Crystal Quest: Dark World Adventure. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Crystal Quest: Dark World Adventure
User prompt
👾 GENEL BAKIŞ Oyun Adı: Deltarune Yaratıcı: Toby Fox Oyun Motoru: GameMaker Studio (orijinal oyun için) Kategori: 2D RPG (Role-Playing Game), Bullet-Hell, Hikâye tabanlı Görünüş: Top-down 2D pixel-art Platformlar: PC, macOS, Nintendo Switch (orijinal oyun) 🛠️ OYUN MEKANİKLERİ ✅ Temel Sistem Oyuncu, Kristal adlı bir karakteri kontrol eder. Oyun boyunca, Ralsei ve Susie gibi karakterler partiye dahil olur. Yürüme, nesne etkileşimi, diyalog ve düşmanlarla savaş gibi eylemler bulunur. ⚔️ Savaş Sistemi (Turn-Based + Bullet Hell) Savaşlar, Chrono Trigger benzeri alan tabanlı dönüşülü sistemiyle oynanır. Oyuncunun şu tercihleri vardır: FIGHT: Düşmanlara fiziksel saldırı yapar. Zamanlamalı tuş basışı gerekir. ACT: Düşmanla etkileşime girer (konuşmak, dalga geçmek vb.) ITEM: Envanterden iyileştirici veya başka eşya kullanır. DEFEND: TP (Tension Points) kazandırır ve hasarı azaltır. SPARE: Düşmanlar pasifleştirildiğinde savaşı sona erdirir. 📊 TP Sistemi TP (Tension Points): Özel büyüler için kullanılan enerji. Düşman saldırılarına "GRAZE" (yakın geçme) ile yaklaşıldığında TP artar. ⚡ Bullet Hell (Kurşun Yağmuru) Sistemi Düşmanlar sırasında karakter, bir kalp olarak temsil edilir. Bu kalple, ekran üzerinde hareket edilerek saldırılardan kaçılır. Her düşmanın farklı desenleri ve mekaniği vardır. 🔊 Ses ve Müzik Orijinal müzikler Toby Fox tarafından bestelenmiştir. Tüm sçeneler için sahneye uygun, lo-fi, retro, karanlık/komik tonlarda özgün müzikler. 🎨 GÖRSEL STİL Grafik Tarzı: 8-bit ve 16-bit retro estetik Renk Paleti: İkonik ve sade paletler (siyah-beyaz kontrastlar, pastel tonlar) Sprite'lar: Piksel-art, animasyonlu yürüme, yüz ifadeleri, saldırı pozları Dünyalar: Light World: Düzenli, sessiz, okul ortamı gibi Dark World: Sırlarla dolu fantastik bölgeler (Card Kingdom, Cyber World) 🔹 KARAKTER SİSTEMİ Ana Parti Karakterleri Kris: Sessiz ana karakter Oyuncu tarafından kontrol edilir Susie: Saldırgan ve bağımsız karakter Oyunun başında oyuncuya dirençli ama sonra takıma dahil olur Ralsei: Destek karakter, iyileştirici büyüler yapar Diğer Önemli NPC'ler Lancer, Rouxls Kaard, Queen, Spamton, Noelle vb. Karakter Değerleri HP: Can ATK / DEF: Saldırı / Savunma MAG: Büyü etkisi Speed: Bullet Hell için hareket hızı 📖 DİYALOG VE HİKÂYE YAPI Diyalog Sistemi Karakterler yüz ikonlarıyla beraber konuşur Seçimli diyaloglar oyuncunun ilerleyişini etkiler Mizah, drama ve gizem dengelidir Hikâye Özeti (Bölümlerle birlikte) Chapter 1: Kris ve Susie, okulun temizlik dolabından Dark World'e geçer Chapter 2: Cyber World'e gidilir, Queen ve Noelle hikâyesi Her bölümde yeni dünyalar, yeni karakterler ve oyun mekanikleri tanıtılır Alternatif Yollar Pacifist Route: Tüm düşmanları pasifleştirme Snowgrave Route (Chapter 2): Karanlık alternatif bir yol (genocide benzeri) 💡 TEKNİK UNSURLAR Input: Klavye (yön tuşları, enter, z/x tuşları) Save Sistemi: Belirli noktalarda ilerleme kaydedilir Script Sistemi: Diyalog ve olaylar zamanlı kodlanmıştır (trigger-based event) Collision: Tile-based harita, katı blok sistemi UI: Menüler basit, pixel yazı tipi, simgelerle dolu 🌍 HARİTA VE BÖLÜMLER Harita Türleri Overworld (keşif bölgesi) Battle Screen (savaş ekranı) Shops / Save Rooms / Puzzle Rooms Özellikler Gizli alanlar, bulmacalar, sandıklar, NPC etkileşimleri Oyun dünyası "karakter gibi" davranır; duvarlar konuşur, kayalar espri yapar vb. 🔹 SES VE MÜZİK DETAYI .OGG formatında lo-fi ve synthwave öğeleri Temalar: "Field of Hopes and Dreams", "Rude Buster", "The World Revolving" Her karaktere özgü tema müzikler Ses efektleri: retro 8-bit tarzı menü, vuruş, kazınmalar vb. ✨ EKSTRA Oyunun özü, "oyuncunun tercihleriyle karakterlerin gelişimi" eksenindedir. Hiçbir tercih mutlak "iyi/kötü" olarak sunulmaz. Oyunda mizah, hüzün, gerilim bir arada kullanılır.
Initial prompt
قضيب
/**** * 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;
}
};