User prompt
make egegokalp item next to bandeage item bro you are not doing ts
User prompt
there is no item
User prompt
there is no egegokalp in items box make it next to bandage
User prompt
make an item egegokalp and it gives extra 10 sezaryen yumurtası
User prompt
make more items
User prompt
again..fix it
User prompt
there is no item in item box fix it
User prompt
Please fix the bug: 'idx is not defined' in or related to this line: 'var item = playerInventory[idx];' Line Number: 1468
User prompt
Please fix the bug: 'idx is not defined' in or related to this line: 'var item = playerInventory[idx];' Line Number: 1468
User prompt
make items like candy chocolate egegokalp and make them have own health
User prompt
make a health bar for all boss and write down how much healths they have
User prompt
make joystick more faster and make more more space please bro
User prompt
make joystick a round not square
User prompt
make joystick more faster
User prompt
make more space to protect attacks from bosses
User prompt
item is buggy to like act command to it same thing and fix bug
User prompt
make more space to run attacks its so hard like that
User prompt
when i act and spare hopster i can spare every bosses fix it and we can act every boss and then spar
User prompt
its still buggy when i press act and chose anything i cant press anything insteadr
User prompt
when i press act button its get buggy and i cant press any buttons fix the bug
User prompt
make croaknight a sword attack
User prompt
croaknight is still same move with hopster change them moves and make special moves to every boss
User prompt
make croaknight a sword attack and more like that
User prompt
Have attacks specific to each boss in the game not dialagoue make speacial attacks for bosses
User prompt
Have attacks specific to each boss in the game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Attack slash (enemy attack) var AttackSlash = Container.expand(function () { var self = Container.call(this); var slash = self.attachAsset('attack_slash', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 22 + Math.random() * 8; self.direction = Math.random() < 0.5 ? 1 : -1; self.active = true; self.update = function () { self.x += self.speed * self.direction; if (self.x < 200 || self.x > 1848) { self.active = false; } }; return self; }); // Dialogue box var DialogueBox = Container.expand(function () { var self = Container.call(this); self.bg = self.attachAsset('dialogue_box', { anchorX: 0.5, anchorY: 0.5 }); self.txt = new Text2('', { size: 54, fill: "#fff" }); self.txt.anchor.set(0.5, 0.5); self.txt.x = 0; self.txt.y = 0; self.addChild(self.txt); self.setText = function (t) { self.txt.setText(t); }; return self; }); // Hero soul in battle (the heart you move to dodge attacks) var Heart = Container.expand(function () { var self = Container.call(this); var heart = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); self.radius = 30; self.speed = 18; self.update = function () { // Movement handled in game.move }; return self; }); // Button for menu var MenuButton = Container.expand(function () { var self = Container.call(this); self.bg = self.attachAsset('btn_bg', { anchorX: 0.5, anchorY: 0.5 }); self.hl = self.attachAsset('btn_hl', { anchorX: 0.5, anchorY: 0.5 }); self.hl.visible = false; self.txt = new Text2('', { size: 60, fill: "#fff" }); self.txt.anchor.set(0.5, 0.5); self.addChild(self.txt); self.setText = function (t) { self.txt.setText(t); }; self.setHighlight = function (v) { self.hl.visible = v; }; return self; }); // Monster class (for battle) var Monster = Container.expand(function () { var self = Container.call(this); var monster = self.attachAsset('monster1', { anchorX: 0.5, anchorY: 0.5 }); self.hp = 12; self.maxHp = 12; self.name = "Froggle"; self.state = "neutral"; // can be "neutral", "spared", "angry" self.dialogue = ["Ribbit... A human? Down here?", "Froggle seems nervous.", "Froggle croaks softly.", "Froggle is watching you closely."]; self.dialogueIndex = 0; self.getDialogue = function () { if (self.state === "spared") return self.name + " looks relieved."; if (self.state === "angry") return self.name + " is furious!"; return self.dialogue[self.dialogueIndex % self.dialogue.length]; }; self.nextDialogue = function () { self.dialogueIndex++; }; self.update = function () { // No movement for monster }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181824 }); /**** * Game Code ****/ // Mercy yellow // Dialogue box // Button highlight // Button backgrounds // Heart (player soul in battle) // Attack indicator (red slash) // Enemy monster (first encounter, "Froggit" inspired, but original) // Main character (the "child") // --- Game State --- var STATE = { STORY: 0, BATTLE_INTRO: 1, BATTLE_MENU: 2, BATTLE_ACT: 3, BATTLE_ATTACK: 4, BATTLE_DODGE: 5, BATTLE_RESULT: 6, ENDING: 7 }; var gameState = STATE.STORY; // --- Main variables --- var hero, heart, dialogueBox; var monsters = []; var currentMonsterIndex = 0; var monster = null; var menuButtons = []; var selectedMenu = 0; var actButtons = []; var selectedAct = 0; var mercyAvailable = false; var playerHP = 20; var playerMaxHP = 20; var playerInventory = [{ name: "Candy", heal: 10, qty: 1 }]; var selectedItem = 0; var itemButtons = []; var mercyButton; var attackSlashes = []; var attackTimer = 0; var attackDuration = 90; var battleResultText = ''; var storyStep = 0; var storyTexts = ["You wake up in a dark, unfamiliar place.", "A soft croak echoes in the distance...", "A strange creature approaches!", "You survived the first battle. But the journey continues...", "A new monster blocks your path!", "You brace yourself for another fight."]; // Add more monsters for a longer game function createMonsters() { // 1. Froggle var m1 = new Monster(); m1.x = 2048 / 2; m1.y = 900; m1.hp = 12; m1.maxHp = 12; m1.name = "Froggle"; m1.state = "neutral"; m1.dialogue = ["Ribbit... A human? Down here?", "Froggle seems nervous.", "Froggle croaks softly.", "Froggle is watching you closely."]; m1.dialogueIndex = 0; // 2. Hopster var m2 = new Monster(); m2.x = 2048 / 2; m2.y = 900; m2.hp = 18; m2.maxHp = 18; m2.name = "Hopster"; m2.state = "neutral"; m2.dialogue = ["Hopster bounces in!", "Hopster looks at you with big eyes.", "Hopster is ready to pounce.", "Hopster wiggles its ears."]; m2.dialogueIndex = 0; // 3. Croaknight var m3 = new Monster(); m3.x = 2048 / 2; m3.y = 900; m3.hp = 25; m3.maxHp = 25; m3.name = "Croaknight"; m3.state = "neutral"; m3.dialogue = ["Croaknight blocks your way!", "Croaknight polishes its armor.", "Croaknight croaks with authority.", "Croaknight stands tall."]; m3.dialogueIndex = 0; // 4. Salamandra var m4 = new Monster(); m4.x = 2048 / 2; m4.y = 900; m4.hp = 22; m4.maxHp = 22; m4.name = "Salamandra"; m4.state = "neutral"; m4.dialogue = ["Salamandra flicks its tail.", "Salamandra's eyes glow in the dark.", "Salamandra hisses quietly.", "Salamandra is sizing you up."]; m4.dialogueIndex = 0; // 5. Turtloid var m5 = new Monster(); m5.x = 2048 / 2; m5.y = 900; m5.hp = 30; m5.maxHp = 30; m5.name = "Turtloid"; m5.state = "neutral"; m5.dialogue = ["Turtloid withdraws into its shell.", "Turtloid peeks out cautiously.", "Turtloid spins slowly.", "Turtloid seems unhurried."]; m5.dialogueIndex = 0; // 6. Mothmire var m6 = new Monster(); m6.x = 2048 / 2; m6.y = 900; m6.hp = 16; m6.maxHp = 16; m6.name = "Mothmire"; m6.state = "neutral"; m6.dialogue = ["Mothmire flutters its wings.", "Mothmire is drawn to the light.", "Mothmire hovers silently.", "Mothmire's dust sparkles."]; m6.dialogueIndex = 0; // 7. Spindle var m7 = new Monster(); m7.x = 2048 / 2; m7.y = 900; m7.hp = 20; m7.maxHp = 20; m7.name = "Spindle"; m7.state = "neutral"; m7.dialogue = ["Spindle weaves a web.", "Spindle's eyes glint.", "Spindle scuttles around.", "Spindle is alert."]; m7.dialogueIndex = 0; // 8. Barkbark var m8 = new Monster(); m8.x = 2048 / 2; m8.y = 900; m8.hp = 24; m8.maxHp = 24; m8.name = "Barkbark"; m8.state = "neutral"; m8.dialogue = ["Barkbark wags its tail.", "Barkbark barks excitedly.", "Barkbark circles you.", "Barkbark pants happily."]; m8.dialogueIndex = 0; // 9. Glimmerbug var m9 = new Monster(); m9.x = 2048 / 2; m9.y = 900; m9.hp = 15; m9.maxHp = 15; m9.name = "Glimmerbug"; m9.state = "neutral"; m9.dialogue = ["Glimmerbug glows softly.", "Glimmerbug zips around.", "Glimmerbug leaves a trail of light.", "Glimmerbug hums a tune."]; m9.dialogueIndex = 0; // 10. Thornet var m10 = new Monster(); m10.x = 2048 / 2; m10.y = 900; m10.hp = 28; m10.maxHp = 28; m10.name = "Thornet"; m10.state = "neutral"; m10.dialogue = ["Thornet buzzes menacingly.", "Thornet sharpens its stinger.", "Thornet hovers above.", "Thornet's wings vibrate."]; m10.dialogueIndex = 0; // 11. Shadewisp var m11 = new Monster(); m11.x = 2048 / 2; m11.y = 900; m11.hp = 35; m11.maxHp = 35; m11.name = "Shadewisp"; m11.state = "neutral"; m11.dialogue = ["Shadewisp flickers in and out.", "Shadewisp's form is hard to see.", "Shadewisp whispers your name.", "Shadewisp chills the air."]; m11.dialogueIndex = 0; // 12. Queen Lily var m12 = new Monster(); m12.x = 2048 / 2; m12.y = 900; m12.hp = 40; m12.maxHp = 40; m12.name = "Queen Lily"; m12.state = "neutral"; m12.dialogue = ["Queen Lily gazes at you regally.", "Queen Lily's petals shimmer.", "Queen Lily radiates calm.", "Queen Lily stands tall."]; m12.dialogueIndex = 0; monsters = [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12]; } createMonsters(); monster = monsters[0]; // --- Utility functions --- function showDialogue(text) { dialogueBox.setText(text); dialogueBox.visible = true; } function hideDialogue() { dialogueBox.visible = false; } function updateHPText() { hpText.setText("HP: " + playerHP + "/" + playerMaxHP); } function clamp(val, min, max) { return val < min ? min : val > max ? max : val; } // --- UI Elements --- var hpText = new Text2("HP: 20/20", { size: 60, fill: 0xFF6666 }); hpText.anchor.set(0, 0); LK.gui.top.addChild(hpText); hpText.x = 120; hpText.y = 20; // Dialogue box dialogueBox = new DialogueBox(); dialogueBox.x = 2048 / 2; dialogueBox.y = 2732 - 220; game.addChild(dialogueBox); // --- Main character (not visible in battle) --- hero = LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 400 }); game.addChild(hero); // --- Monster (battle only) --- monster = new Monster(); monster.x = 2048 / 2; monster.y = 900; monster.visible = false; game.addChild(monster); // --- Heart (player soul in battle) --- heart = new Heart(); heart.x = 2048 / 2; heart.y = 1800; heart.visible = false; game.addChild(heart); // --- Menu buttons (Fight, Act, Item, Mercy) --- // Now with clear button backgrounds, NO "ENTER" or "CLICK" label, tap only var menuNames = ["Fight", "Act", "Item", "Mercy"]; for (var i = 0; i < 4; i++) { var btn = new MenuButton(); btn.x = 524 + i * 350; btn.y = 2732 - 500; btn.setText(menuNames[i]); btn.visible = false; menuButtons.push(btn); game.addChild(btn); } menuButtons[0].setHighlight(true); // --- Act buttons (contextual) --- var actNames = ["Check", "Compliment", "Threaten"]; for (var i = 0; i < 3; i++) { var btn = new MenuButton(); btn.x = 524 + i * 500; btn.y = 2732 - 500; btn.setText(actNames[i]); btn.visible = false; actButtons.push(btn); game.addChild(btn); } actButtons[0].setHighlight(true); // --- Item buttons (contextual) --- for (var i = 0; i < 2; i++) { var btn = new MenuButton(); btn.x = 524 + i * 500; btn.y = 2732 - 500; btn.setText(''); btn.visible = false; itemButtons.push(btn); game.addChild(btn); } itemButtons[0].setHighlight(true); // --- Mercy button (yellow if available) --- mercyButton = menuButtons[3]; // --- Battle result text --- var resultText = new Text2('', { size: 80, fill: "#fff" }); resultText.anchor.set(0.5, 0.5); resultText.x = 2048 / 2; resultText.y = 2732 / 2; resultText.visible = false; game.addChild(resultText); // --- Story flow --- function nextStory() { if (storyStep < storyTexts.length) { showDialogue(storyTexts[storyStep]); storyStep++; // If we just finished the first story arc, and there are more monsters, prep next monster if (storyStep === 3 && currentMonsterIndex === 0) { // After first monster, show next monster LK.setTimeout(function () { currentMonsterIndex = 1; monster.visible = false; monster = monsters[currentMonsterIndex]; monster.visible = false; game.addChild(monster); }, 1200); } if (storyStep === 5 && currentMonsterIndex === 1) { LK.setTimeout(function () { currentMonsterIndex = 2; monster.visible = false; monster = monsters[currentMonsterIndex]; monster.visible = false; game.addChild(monster); }, 1200); } } else { // Start battle with current monster gameState = STATE.BATTLE_INTRO; hero.visible = false; monster.visible = true; showDialogue(monster.getDialogue()); LK.setTimeout(function () { gameState = STATE.BATTLE_MENU; showBattleMenu(); }, 1200); } } nextStory(); // --- Battle menu logic --- function showBattleMenu() { for (var i = 0; i < menuButtons.length; i++) { menuButtons[i].visible = true; menuButtons[i].setHighlight(i === selectedMenu); } for (var i = 0; i < actButtons.length; i++) actButtons[i].visible = false; for (var i = 0; i < itemButtons.length; i++) itemButtons[i].visible = false; dialogueBox.visible = true; dialogueBox.setText(monster.name + ": " + monster.getDialogue()); mercyButton.bg.visible = !mercyAvailable; if (mercyAvailable) { mercyButton.bg.visible = false; if (!mercyButton.mercyAsset) { mercyButton.mercyAsset = mercyButton.attachAsset('mercy_yellow', { anchorX: 0.5, anchorY: 0.5 }); } mercyButton.mercyAsset.visible = true; } else if (mercyButton.mercyAsset) { mercyButton.mercyAsset.visible = false; } } // --- Act menu logic --- function showActMenu() { for (var i = 0; i < menuButtons.length; i++) menuButtons[i].visible = false; for (var i = 0; i < actButtons.length; i++) { actButtons[i].visible = true; actButtons[i].setHighlight(i === selectedAct); } for (var i = 0; i < itemButtons.length; i++) itemButtons[i].visible = false; dialogueBox.visible = true; dialogueBox.setText("What will you do?"); } // --- Item menu logic --- function showItemMenu() { for (var i = 0; i < menuButtons.length; i++) menuButtons[i].visible = false; for (var i = 0; i < actButtons.length; i++) actButtons[i].visible = false; for (var i = 0; i < itemButtons.length; i++) { var item = playerInventory[i]; if (item && item.qty > 0) { itemButtons[i].setText(item.name + " (" + item.qty + ")"); itemButtons[i].visible = true; itemButtons[i].setHighlight(i === selectedItem); } else { itemButtons[i].visible = false; } } dialogueBox.visible = true; dialogueBox.setText("Use which item?"); } // --- Hide all menus --- function hideAllMenus() { for (var i = 0; i < menuButtons.length; i++) { menuButtons[i].visible = false; } for (var i = 0; i < actButtons.length; i++) actButtons[i].visible = false; for (var i = 0; i < itemButtons.length; i++) itemButtons[i].visible = false; } // --- Handle menu selection (touch) --- function handleMenuTouch(x, y) { if (gameState === STATE.BATTLE_MENU) { for (var i = 0; i < menuButtons.length; i++) { var btn = menuButtons[i]; if (btn.visible && x > btn.x - 200 && x < btn.x + 200 && y > btn.y - 60 && y < btn.y + 60) { selectedMenu = i; for (var j = 0; j < menuButtons.length; j++) menuButtons[j].setHighlight(j === i); selectMenuOption(i); return; } } } else if (gameState === STATE.BATTLE_ACT) { for (var i = 0; i < actButtons.length; i++) { var btn = actButtons[i]; if (btn.visible && x > btn.x - 200 && x < btn.x + 200 && y > btn.y - 60 && y < btn.y + 60) { selectedAct = i; for (var j = 0; j < actButtons.length; j++) actButtons[j].setHighlight(j === i); selectActOption(i); return; } } } else if (gameState === STATE.BATTLE_ATTACK) { // No touch input } else if (gameState === STATE.BATTLE_DODGE) { // Heart movement handled in move } else if (gameState === STATE.BATTLE_RESULT) { // Tap to continue resultText.visible = false; if (monster.hp <= 0 || monster.state === "spared") { // If there are more monsters, continue to next battle if (currentMonsterIndex < monsters.length - 1) { currentMonsterIndex++; monster.visible = false; monster = monsters[currentMonsterIndex]; monster.visible = false; game.addChild(monster); // Show next story segment before next battle storyStep++; nextStory(); } else { gameState = STATE.ENDING; showEnding(); } } else { gameState = STATE.BATTLE_MENU; showBattleMenu(); } } else if (gameState === STATE.BATTLE_INTRO) { // skip intro } else if (gameState === STATE.STORY) { nextStory(); } else if (gameState === STATE.BATTLE_ITEM) { for (var i = 0; i < itemButtons.length; i++) { var btn = itemButtons[i]; if (btn.visible && x > btn.x - 200 && x < btn.x + 200 && y > btn.y - 60 && y < btn.y + 60) { selectedItem = i; for (var j = 0; j < itemButtons.length; j++) itemButtons[j].setHighlight(j === i); selectItemOption(i); return; } } } } // --- Menu option selection --- function selectMenuOption(idx) { if (idx === 0) { // Fight hideAllMenus(); dialogueBox.setText("You attack!"); gameState = STATE.BATTLE_ATTACK; LK.setTimeout(function () { // Simple attack: always hit for 5 monster.hp -= 5; if (monster.hp < 0) monster.hp = 0; dialogueBox.setText("You hit " + monster.name + " for 5 damage!"); monster.state = "angry"; monster.nextDialogue(); LK.setTimeout(function () { if (monster.hp <= 0) { battleResultText = "You defeated " + monster.name + "!"; resultText.setText(battleResultText); resultText.visible = true; gameState = STATE.BATTLE_RESULT; } else { startEnemyAttack(); } }, 900); }, 600); } else if (idx === 1) { // Act hideAllMenus(); gameState = STATE.BATTLE_ACT; showActMenu(); } else if (idx === 2) { // Item hideAllMenus(); gameState = STATE.BATTLE_ITEM; showItemMenu(); } else if (idx === 3) { // Mercy if (mercyAvailable) { hideAllMenus(); dialogueBox.setText("You spare " + monster.name + "."); monster.state = "spared"; LK.setTimeout(function () { battleResultText = "You showed mercy to " + monster.name + "!"; resultText.setText(battleResultText); resultText.visible = true; gameState = STATE.BATTLE_RESULT; }, 900); } else { dialogueBox.setText("You tried to spare, but Froggle isn't ready."); LK.setTimeout(function () { showBattleMenu(); }, 900); } } } // --- Act option selection --- function selectActOption(idx) { hideAllMenus(); if (idx === 0) { // Check dialogueBox.setText(monster.name + " - ATK 4 DEF 2\nA nervous little monster."); LK.setTimeout(function () { showBattleMenu(); }, 1200); } else if (idx === 1) { // Compliment dialogueBox.setText("You compliment " + monster.name + ".\n" + monster.name + " blushes."); monster.state = "neutral"; mercyAvailable = true; LK.setTimeout(function () { showBattleMenu(); }, 1200); } else if (idx === 2) { // Threaten dialogueBox.setText("You threaten " + monster.name + ".\n" + monster.name + " looks scared."); monster.state = "angry"; mercyAvailable = false; LK.setTimeout(function () { showBattleMenu(); }, 1200); } } // --- Item option selection --- function selectItemOption(idx) { var item = playerInventory[idx]; if (item && item.qty > 0) { playerHP += item.heal; if (playerHP > playerMaxHP) playerHP = playerMaxHP; item.qty--; updateHPText(); dialogueBox.setText("You used " + item.name + ".\nRecovered " + item.heal + " HP!"); LK.setTimeout(function () { showBattleMenu(); }, 1200); } } // --- Enemy attack phase --- function startEnemyAttack() { hideAllMenus(); // Show unique, attentive attack dialogue for each monster var attackDialogue = ""; switch (monster.name) { case "Froggle": attackDialogue = "Froggle leaps at you, eyes wide with fear and determination!"; break; case "Hopster": attackDialogue = "Hopster springs high, aiming to land right on your head!"; break; case "Croaknight": attackDialogue = "Croaknight draws his sword and charges with a thunderous croak!"; break; case "Salamandra": attackDialogue = "Salamandra spits a sizzling stream of fire in your direction!"; break; case "Turtloid": attackDialogue = "Turtloid spins in its shell, barreling toward you with surprising speed!"; break; case "Mothmire": attackDialogue = "Mothmire flutters above, scattering a cloud of dazzling, stinging dust!"; break; case "Spindle": attackDialogue = "Spindle weaves a sticky web and flings it at you!"; break; case "Barkbark": attackDialogue = "Barkbark barks wildly and dashes in circles, trying to trip you!"; break; case "Glimmerbug": attackDialogue = "Glimmerbug zips around, leaving a blinding trail of sparkling light!"; break; case "Thornet": attackDialogue = "Thornet buzzes furiously, diving at you with its venomous stinger!"; break; case "Shadewisp": attackDialogue = "Shadewisp flickers and vanishes, then lashes out from the shadows!"; break; case "Queen Lily": attackDialogue = "Queen Lily summons a swirling storm of razor-sharp petals your way!"; break; default: attackDialogue = monster.name + " attacks fiercely!"; break; } dialogueBox.setText(attackDialogue); heart.visible = true; heart.x = 2048 / 2; heart.y = 1800; attackSlashes = []; attackTimer = 0; attackDuration = 90 + Math.floor(Math.random() * 30); gameState = STATE.BATTLE_DODGE; showJoystick(true); } // --- Endings --- function showEnding() { hideAllMenus(); heart.visible = false; monster.visible = false; dialogueBox.visible = false; if (monster.state === "spared") { resultText.setText("You made a friend: " + monster.name + ".\nA gentle path lies ahead."); } else if (monster.hp <= 0) { resultText.setText("You defeated " + monster.name + ".\nBut at what cost?"); } else { resultText.setText("The story continues..."); } resultText.visible = true; LK.setTimeout(function () { LK.showYouWin(); }, 1800); } // --- Input handling --- // Virtual joystick for heart movement (touch-optimized) // Ava: This joystick appears during enemy attack phase and is fully touch/mouse compatible. var joystick = { active: false, baseX: 0, baseY: 0, stickX: 0, stickY: 0, radius: 120, stickRadius: 60, visible: false, baseAsset: null, stickAsset: null }; // Create joystick assets (will be positioned below the heart dynamically) joystick.baseAsset = LK.getAsset('btn_bg', { anchorX: 0.5, anchorY: 0.5, x: 0, // will be set dynamically y: 0, // will be set dynamically width: joystick.radius * 2, height: joystick.radius * 2 }); joystick.baseAsset.alpha = 0.25; joystick.baseAsset.visible = false; game.addChild(joystick.baseAsset); // Use a simple circle for joystick stick instead of a heart to avoid duplicate heart visuals joystick.stickAsset = LK.getAsset('btn_hl', { anchorX: 0.5, anchorY: 0.5, x: 0, // will be set dynamically y: 0, // will be set dynamically width: joystick.stickRadius * 2, height: joystick.stickRadius * 2 }); joystick.stickAsset.alpha = 0.7; joystick.stickAsset.visible = false; game.addChild(joystick.stickAsset); // Add a "MOVE" button styled like the menu buttons, and make it moveable joystick.moveButton = new MenuButton(); joystick.moveButton.setText("MOVE"); joystick.moveButton.x = 0; // will be set dynamically joystick.moveButton.y = 0; // will be set dynamically joystick.moveButton.visible = false; joystick.moveButton.setHighlight(false); game.addChild(joystick.moveButton); // --- Show joystick at start of game (story phase) --- function showJoystick(show) { joystick.visible = show; joystick.baseAsset.visible = show; joystick.stickAsset.visible = show; joystick.moveButton.visible = false; if (show) { // Place joystick below the heart if heart is visible, otherwise below hero (start of game) var centerX, baseY; if (heart && heart.visible) { centerX = heart.x; baseY = heart.y + heart.height / 2; } else if (hero && hero.visible) { centerX = hero.x; baseY = hero.y + hero.height / 2; } else { centerX = 2048 / 2; baseY = 2732 / 2 + 400 + 60; // fallback } var joystickOffset = 120 + joystick.radius; var joyX = centerX; var joyY = baseY + joystickOffset; var minJoyY = baseY + 100; var maxJoyY = 2732 - joystick.radius - 40; if (joyY > maxJoyY) joyY = maxJoyY; if (joyY < minJoyY) joyY = minJoyY; joystick.baseAsset.x = joyX; joystick.baseAsset.y = joyY; joystick.stickAsset.x = joyX; joystick.stickAsset.y = joyY; joystick.baseX = joyX; joystick.baseY = joyY; joystick.stickX = joyX; joystick.stickY = joyY; } else { joystick.active = false; joystick.moveButton.visible = false; } } // Show joystick at the start of the game (story phase) showJoystick(true); // Returns true if (x, y) is inside joystick base function isInJoystick(x, y) { var dx = x - joystick.baseAsset.x; var dy = y - joystick.baseAsset.y; return dx * dx + dy * dy <= joystick.radius * joystick.radius; } var draggingHeart = false; game.down = function (x, y, obj) { if (gameState === STATE.BATTLE_DODGE) { // If touch is inside joystick, activate joystick if (isInJoystick(x, y)) { joystick.active = true; joystick.baseX = joystick.baseAsset.x; joystick.baseY = joystick.baseAsset.y; joystick.stickX = x; joystick.stickY = y; joystick.stickAsset.x = x; joystick.stickAsset.y = y; } } else if (gameState === STATE.BATTLE_MENU || gameState === STATE.BATTLE_ACT || gameState === STATE.BATTLE_ITEM || gameState === STATE.BATTLE_RESULT || gameState === STATE.BATTLE_INTRO || gameState === STATE.STORY) { handleMenuTouch(x, y); } }; game.up = function (x, y, obj) { joystick.active = false; joystick.stickAsset.x = joystick.baseAsset.x; joystick.stickAsset.y = joystick.baseAsset.y; lastTouchY = null; }; // Unified move handler for joystick and menu navigation (NO mouse/drag/hover/Enter) game.move = function (x, y, obj) { if (gameState === STATE.BATTLE_DODGE) { // Joystick movement only if (joystick.active) { var dx = x - joystick.baseX; var dy = y - joystick.baseY; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > joystick.radius) { dx = dx * joystick.radius / dist; dy = dy * joystick.radius / dist; } joystick.stickX = joystick.baseX + dx; joystick.stickY = joystick.baseY + dy; joystick.stickAsset.x = joystick.stickX; joystick.stickAsset.y = joystick.stickY; // Move heart var moveSpeed = heart.speed; var moveX = dx / joystick.radius; var moveY = dy / joystick.radius; var minX = 524, maxX = 2048 - 524, minY = 1500, maxY = 2100; heart.x = clamp(heart.x + moveX * moveSpeed, minX, maxX); heart.y = clamp(heart.y + moveY * moveSpeed, minY, maxY); } } }; // --- Main update loop --- game.update = function () { // Heart update if (gameState === STATE.BATTLE_DODGE) { // Keep joystick locked below the heart every frame var heartCenterX = heart.x; var heartBottomY = heart.y + heart.height / 2; var joystickOffset = 120 + joystick.radius; var joyX = heartCenterX; var joyY = heartBottomY + joystickOffset; var minJoyY = heart.y + 100; var maxJoyY = 2732 - joystick.radius - 40; if (joyY > maxJoyY) joyY = maxJoyY; if (joyY < minJoyY) joyY = minJoyY; joystick.baseAsset.x = joyX; joystick.baseAsset.y = joyY; if (!joystick.active) { joystick.stickAsset.x = joyX; joystick.stickAsset.y = joyY; joystick.stickX = joyX; joystick.stickY = joyY; } joystick.baseX = joyX; joystick.baseY = joyY; // Spawn slashes if (attackTimer % 24 === 0 && attackSlashes.length < 5) { var slash = new AttackSlash(); slash.y = 1600 + Math.random() * 400; slash.x = Math.random() < 0.5 ? 2048 - 200 : 200; slash.direction = slash.x < 1024 ? 1 : -1; attackSlashes.push(slash); game.addChild(slash); } // Update slashes for (var i = attackSlashes.length - 1; i >= 0; i--) { var s = attackSlashes[i]; s.update(); // Collision with heart var dx = s.x - heart.x, dy = s.y - heart.y; if (s.active && dx * dx + dy * dy < 60 * 60) { s.active = false; playerHP -= 4; updateHPText(); LK.effects.flashObject(heart, 0xff0000, 400); if (playerHP <= 0) { playerHP = 0; updateHPText(); LK.effects.flashScreen(0xff0000, 1200); LK.showGameOver(); return; } } if (!s.active) { s.destroy(); attackSlashes.splice(i, 1); } } attackTimer++; if (attackTimer > attackDuration) { // End attack phase for (var i = 0; i < attackSlashes.length; i++) attackSlashes[i].destroy(); attackSlashes = []; heart.visible = false; showJoystick(false); gameState = STATE.BATTLE_MENU; showBattleMenu(); } } }; // --- Touch navigation for menu (swipe up/down to change selection) --- var lastTouchY = null; // --- End of file ---
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Attack slash (enemy attack)
var AttackSlash = Container.expand(function () {
var self = Container.call(this);
var slash = self.attachAsset('attack_slash', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 22 + Math.random() * 8;
self.direction = Math.random() < 0.5 ? 1 : -1;
self.active = true;
self.update = function () {
self.x += self.speed * self.direction;
if (self.x < 200 || self.x > 1848) {
self.active = false;
}
};
return self;
});
// Dialogue box
var DialogueBox = Container.expand(function () {
var self = Container.call(this);
self.bg = self.attachAsset('dialogue_box', {
anchorX: 0.5,
anchorY: 0.5
});
self.txt = new Text2('', {
size: 54,
fill: "#fff"
});
self.txt.anchor.set(0.5, 0.5);
self.txt.x = 0;
self.txt.y = 0;
self.addChild(self.txt);
self.setText = function (t) {
self.txt.setText(t);
};
return self;
});
// Hero soul in battle (the heart you move to dodge attacks)
var Heart = Container.expand(function () {
var self = Container.call(this);
var heart = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 30;
self.speed = 18;
self.update = function () {
// Movement handled in game.move
};
return self;
});
// Button for menu
var MenuButton = Container.expand(function () {
var self = Container.call(this);
self.bg = self.attachAsset('btn_bg', {
anchorX: 0.5,
anchorY: 0.5
});
self.hl = self.attachAsset('btn_hl', {
anchorX: 0.5,
anchorY: 0.5
});
self.hl.visible = false;
self.txt = new Text2('', {
size: 60,
fill: "#fff"
});
self.txt.anchor.set(0.5, 0.5);
self.addChild(self.txt);
self.setText = function (t) {
self.txt.setText(t);
};
self.setHighlight = function (v) {
self.hl.visible = v;
};
return self;
});
// Monster class (for battle)
var Monster = Container.expand(function () {
var self = Container.call(this);
var monster = self.attachAsset('monster1', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 12;
self.maxHp = 12;
self.name = "Froggle";
self.state = "neutral"; // can be "neutral", "spared", "angry"
self.dialogue = ["Ribbit... A human? Down here?", "Froggle seems nervous.", "Froggle croaks softly.", "Froggle is watching you closely."];
self.dialogueIndex = 0;
self.getDialogue = function () {
if (self.state === "spared") return self.name + " looks relieved.";
if (self.state === "angry") return self.name + " is furious!";
return self.dialogue[self.dialogueIndex % self.dialogue.length];
};
self.nextDialogue = function () {
self.dialogueIndex++;
};
self.update = function () {
// No movement for monster
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181824
});
/****
* Game Code
****/
// Mercy yellow
// Dialogue box
// Button highlight
// Button backgrounds
// Heart (player soul in battle)
// Attack indicator (red slash)
// Enemy monster (first encounter, "Froggit" inspired, but original)
// Main character (the "child")
// --- Game State ---
var STATE = {
STORY: 0,
BATTLE_INTRO: 1,
BATTLE_MENU: 2,
BATTLE_ACT: 3,
BATTLE_ATTACK: 4,
BATTLE_DODGE: 5,
BATTLE_RESULT: 6,
ENDING: 7
};
var gameState = STATE.STORY;
// --- Main variables ---
var hero, heart, dialogueBox;
var monsters = [];
var currentMonsterIndex = 0;
var monster = null;
var menuButtons = [];
var selectedMenu = 0;
var actButtons = [];
var selectedAct = 0;
var mercyAvailable = false;
var playerHP = 20;
var playerMaxHP = 20;
var playerInventory = [{
name: "Candy",
heal: 10,
qty: 1
}];
var selectedItem = 0;
var itemButtons = [];
var mercyButton;
var attackSlashes = [];
var attackTimer = 0;
var attackDuration = 90;
var battleResultText = '';
var storyStep = 0;
var storyTexts = ["You wake up in a dark, unfamiliar place.", "A soft croak echoes in the distance...", "A strange creature approaches!", "You survived the first battle. But the journey continues...", "A new monster blocks your path!", "You brace yourself for another fight."];
// Add more monsters for a longer game
function createMonsters() {
// 1. Froggle
var m1 = new Monster();
m1.x = 2048 / 2;
m1.y = 900;
m1.hp = 12;
m1.maxHp = 12;
m1.name = "Froggle";
m1.state = "neutral";
m1.dialogue = ["Ribbit... A human? Down here?", "Froggle seems nervous.", "Froggle croaks softly.", "Froggle is watching you closely."];
m1.dialogueIndex = 0;
// 2. Hopster
var m2 = new Monster();
m2.x = 2048 / 2;
m2.y = 900;
m2.hp = 18;
m2.maxHp = 18;
m2.name = "Hopster";
m2.state = "neutral";
m2.dialogue = ["Hopster bounces in!", "Hopster looks at you with big eyes.", "Hopster is ready to pounce.", "Hopster wiggles its ears."];
m2.dialogueIndex = 0;
// 3. Croaknight
var m3 = new Monster();
m3.x = 2048 / 2;
m3.y = 900;
m3.hp = 25;
m3.maxHp = 25;
m3.name = "Croaknight";
m3.state = "neutral";
m3.dialogue = ["Croaknight blocks your way!", "Croaknight polishes its armor.", "Croaknight croaks with authority.", "Croaknight stands tall."];
m3.dialogueIndex = 0;
// 4. Salamandra
var m4 = new Monster();
m4.x = 2048 / 2;
m4.y = 900;
m4.hp = 22;
m4.maxHp = 22;
m4.name = "Salamandra";
m4.state = "neutral";
m4.dialogue = ["Salamandra flicks its tail.", "Salamandra's eyes glow in the dark.", "Salamandra hisses quietly.", "Salamandra is sizing you up."];
m4.dialogueIndex = 0;
// 5. Turtloid
var m5 = new Monster();
m5.x = 2048 / 2;
m5.y = 900;
m5.hp = 30;
m5.maxHp = 30;
m5.name = "Turtloid";
m5.state = "neutral";
m5.dialogue = ["Turtloid withdraws into its shell.", "Turtloid peeks out cautiously.", "Turtloid spins slowly.", "Turtloid seems unhurried."];
m5.dialogueIndex = 0;
// 6. Mothmire
var m6 = new Monster();
m6.x = 2048 / 2;
m6.y = 900;
m6.hp = 16;
m6.maxHp = 16;
m6.name = "Mothmire";
m6.state = "neutral";
m6.dialogue = ["Mothmire flutters its wings.", "Mothmire is drawn to the light.", "Mothmire hovers silently.", "Mothmire's dust sparkles."];
m6.dialogueIndex = 0;
// 7. Spindle
var m7 = new Monster();
m7.x = 2048 / 2;
m7.y = 900;
m7.hp = 20;
m7.maxHp = 20;
m7.name = "Spindle";
m7.state = "neutral";
m7.dialogue = ["Spindle weaves a web.", "Spindle's eyes glint.", "Spindle scuttles around.", "Spindle is alert."];
m7.dialogueIndex = 0;
// 8. Barkbark
var m8 = new Monster();
m8.x = 2048 / 2;
m8.y = 900;
m8.hp = 24;
m8.maxHp = 24;
m8.name = "Barkbark";
m8.state = "neutral";
m8.dialogue = ["Barkbark wags its tail.", "Barkbark barks excitedly.", "Barkbark circles you.", "Barkbark pants happily."];
m8.dialogueIndex = 0;
// 9. Glimmerbug
var m9 = new Monster();
m9.x = 2048 / 2;
m9.y = 900;
m9.hp = 15;
m9.maxHp = 15;
m9.name = "Glimmerbug";
m9.state = "neutral";
m9.dialogue = ["Glimmerbug glows softly.", "Glimmerbug zips around.", "Glimmerbug leaves a trail of light.", "Glimmerbug hums a tune."];
m9.dialogueIndex = 0;
// 10. Thornet
var m10 = new Monster();
m10.x = 2048 / 2;
m10.y = 900;
m10.hp = 28;
m10.maxHp = 28;
m10.name = "Thornet";
m10.state = "neutral";
m10.dialogue = ["Thornet buzzes menacingly.", "Thornet sharpens its stinger.", "Thornet hovers above.", "Thornet's wings vibrate."];
m10.dialogueIndex = 0;
// 11. Shadewisp
var m11 = new Monster();
m11.x = 2048 / 2;
m11.y = 900;
m11.hp = 35;
m11.maxHp = 35;
m11.name = "Shadewisp";
m11.state = "neutral";
m11.dialogue = ["Shadewisp flickers in and out.", "Shadewisp's form is hard to see.", "Shadewisp whispers your name.", "Shadewisp chills the air."];
m11.dialogueIndex = 0;
// 12. Queen Lily
var m12 = new Monster();
m12.x = 2048 / 2;
m12.y = 900;
m12.hp = 40;
m12.maxHp = 40;
m12.name = "Queen Lily";
m12.state = "neutral";
m12.dialogue = ["Queen Lily gazes at you regally.", "Queen Lily's petals shimmer.", "Queen Lily radiates calm.", "Queen Lily stands tall."];
m12.dialogueIndex = 0;
monsters = [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12];
}
createMonsters();
monster = monsters[0];
// --- Utility functions ---
function showDialogue(text) {
dialogueBox.setText(text);
dialogueBox.visible = true;
}
function hideDialogue() {
dialogueBox.visible = false;
}
function updateHPText() {
hpText.setText("HP: " + playerHP + "/" + playerMaxHP);
}
function clamp(val, min, max) {
return val < min ? min : val > max ? max : val;
}
// --- UI Elements ---
var hpText = new Text2("HP: 20/20", {
size: 60,
fill: 0xFF6666
});
hpText.anchor.set(0, 0);
LK.gui.top.addChild(hpText);
hpText.x = 120;
hpText.y = 20;
// Dialogue box
dialogueBox = new DialogueBox();
dialogueBox.x = 2048 / 2;
dialogueBox.y = 2732 - 220;
game.addChild(dialogueBox);
// --- Main character (not visible in battle) ---
hero = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 + 400
});
game.addChild(hero);
// --- Monster (battle only) ---
monster = new Monster();
monster.x = 2048 / 2;
monster.y = 900;
monster.visible = false;
game.addChild(monster);
// --- Heart (player soul in battle) ---
heart = new Heart();
heart.x = 2048 / 2;
heart.y = 1800;
heart.visible = false;
game.addChild(heart);
// --- Menu buttons (Fight, Act, Item, Mercy) ---
// Now with clear button backgrounds, NO "ENTER" or "CLICK" label, tap only
var menuNames = ["Fight", "Act", "Item", "Mercy"];
for (var i = 0; i < 4; i++) {
var btn = new MenuButton();
btn.x = 524 + i * 350;
btn.y = 2732 - 500;
btn.setText(menuNames[i]);
btn.visible = false;
menuButtons.push(btn);
game.addChild(btn);
}
menuButtons[0].setHighlight(true);
// --- Act buttons (contextual) ---
var actNames = ["Check", "Compliment", "Threaten"];
for (var i = 0; i < 3; i++) {
var btn = new MenuButton();
btn.x = 524 + i * 500;
btn.y = 2732 - 500;
btn.setText(actNames[i]);
btn.visible = false;
actButtons.push(btn);
game.addChild(btn);
}
actButtons[0].setHighlight(true);
// --- Item buttons (contextual) ---
for (var i = 0; i < 2; i++) {
var btn = new MenuButton();
btn.x = 524 + i * 500;
btn.y = 2732 - 500;
btn.setText('');
btn.visible = false;
itemButtons.push(btn);
game.addChild(btn);
}
itemButtons[0].setHighlight(true);
// --- Mercy button (yellow if available) ---
mercyButton = menuButtons[3];
// --- Battle result text ---
var resultText = new Text2('', {
size: 80,
fill: "#fff"
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 2048 / 2;
resultText.y = 2732 / 2;
resultText.visible = false;
game.addChild(resultText);
// --- Story flow ---
function nextStory() {
if (storyStep < storyTexts.length) {
showDialogue(storyTexts[storyStep]);
storyStep++;
// If we just finished the first story arc, and there are more monsters, prep next monster
if (storyStep === 3 && currentMonsterIndex === 0) {
// After first monster, show next monster
LK.setTimeout(function () {
currentMonsterIndex = 1;
monster.visible = false;
monster = monsters[currentMonsterIndex];
monster.visible = false;
game.addChild(monster);
}, 1200);
}
if (storyStep === 5 && currentMonsterIndex === 1) {
LK.setTimeout(function () {
currentMonsterIndex = 2;
monster.visible = false;
monster = monsters[currentMonsterIndex];
monster.visible = false;
game.addChild(monster);
}, 1200);
}
} else {
// Start battle with current monster
gameState = STATE.BATTLE_INTRO;
hero.visible = false;
monster.visible = true;
showDialogue(monster.getDialogue());
LK.setTimeout(function () {
gameState = STATE.BATTLE_MENU;
showBattleMenu();
}, 1200);
}
}
nextStory();
// --- Battle menu logic ---
function showBattleMenu() {
for (var i = 0; i < menuButtons.length; i++) {
menuButtons[i].visible = true;
menuButtons[i].setHighlight(i === selectedMenu);
}
for (var i = 0; i < actButtons.length; i++) actButtons[i].visible = false;
for (var i = 0; i < itemButtons.length; i++) itemButtons[i].visible = false;
dialogueBox.visible = true;
dialogueBox.setText(monster.name + ": " + monster.getDialogue());
mercyButton.bg.visible = !mercyAvailable;
if (mercyAvailable) {
mercyButton.bg.visible = false;
if (!mercyButton.mercyAsset) {
mercyButton.mercyAsset = mercyButton.attachAsset('mercy_yellow', {
anchorX: 0.5,
anchorY: 0.5
});
}
mercyButton.mercyAsset.visible = true;
} else if (mercyButton.mercyAsset) {
mercyButton.mercyAsset.visible = false;
}
}
// --- Act menu logic ---
function showActMenu() {
for (var i = 0; i < menuButtons.length; i++) menuButtons[i].visible = false;
for (var i = 0; i < actButtons.length; i++) {
actButtons[i].visible = true;
actButtons[i].setHighlight(i === selectedAct);
}
for (var i = 0; i < itemButtons.length; i++) itemButtons[i].visible = false;
dialogueBox.visible = true;
dialogueBox.setText("What will you do?");
}
// --- Item menu logic ---
function showItemMenu() {
for (var i = 0; i < menuButtons.length; i++) menuButtons[i].visible = false;
for (var i = 0; i < actButtons.length; i++) actButtons[i].visible = false;
for (var i = 0; i < itemButtons.length; i++) {
var item = playerInventory[i];
if (item && item.qty > 0) {
itemButtons[i].setText(item.name + " (" + item.qty + ")");
itemButtons[i].visible = true;
itemButtons[i].setHighlight(i === selectedItem);
} else {
itemButtons[i].visible = false;
}
}
dialogueBox.visible = true;
dialogueBox.setText("Use which item?");
}
// --- Hide all menus ---
function hideAllMenus() {
for (var i = 0; i < menuButtons.length; i++) {
menuButtons[i].visible = false;
}
for (var i = 0; i < actButtons.length; i++) actButtons[i].visible = false;
for (var i = 0; i < itemButtons.length; i++) itemButtons[i].visible = false;
}
// --- Handle menu selection (touch) ---
function handleMenuTouch(x, y) {
if (gameState === STATE.BATTLE_MENU) {
for (var i = 0; i < menuButtons.length; i++) {
var btn = menuButtons[i];
if (btn.visible && x > btn.x - 200 && x < btn.x + 200 && y > btn.y - 60 && y < btn.y + 60) {
selectedMenu = i;
for (var j = 0; j < menuButtons.length; j++) menuButtons[j].setHighlight(j === i);
selectMenuOption(i);
return;
}
}
} else if (gameState === STATE.BATTLE_ACT) {
for (var i = 0; i < actButtons.length; i++) {
var btn = actButtons[i];
if (btn.visible && x > btn.x - 200 && x < btn.x + 200 && y > btn.y - 60 && y < btn.y + 60) {
selectedAct = i;
for (var j = 0; j < actButtons.length; j++) actButtons[j].setHighlight(j === i);
selectActOption(i);
return;
}
}
} else if (gameState === STATE.BATTLE_ATTACK) {
// No touch input
} else if (gameState === STATE.BATTLE_DODGE) {
// Heart movement handled in move
} else if (gameState === STATE.BATTLE_RESULT) {
// Tap to continue
resultText.visible = false;
if (monster.hp <= 0 || monster.state === "spared") {
// If there are more monsters, continue to next battle
if (currentMonsterIndex < monsters.length - 1) {
currentMonsterIndex++;
monster.visible = false;
monster = monsters[currentMonsterIndex];
monster.visible = false;
game.addChild(monster);
// Show next story segment before next battle
storyStep++;
nextStory();
} else {
gameState = STATE.ENDING;
showEnding();
}
} else {
gameState = STATE.BATTLE_MENU;
showBattleMenu();
}
} else if (gameState === STATE.BATTLE_INTRO) {
// skip intro
} else if (gameState === STATE.STORY) {
nextStory();
} else if (gameState === STATE.BATTLE_ITEM) {
for (var i = 0; i < itemButtons.length; i++) {
var btn = itemButtons[i];
if (btn.visible && x > btn.x - 200 && x < btn.x + 200 && y > btn.y - 60 && y < btn.y + 60) {
selectedItem = i;
for (var j = 0; j < itemButtons.length; j++) itemButtons[j].setHighlight(j === i);
selectItemOption(i);
return;
}
}
}
}
// --- Menu option selection ---
function selectMenuOption(idx) {
if (idx === 0) {
// Fight
hideAllMenus();
dialogueBox.setText("You attack!");
gameState = STATE.BATTLE_ATTACK;
LK.setTimeout(function () {
// Simple attack: always hit for 5
monster.hp -= 5;
if (monster.hp < 0) monster.hp = 0;
dialogueBox.setText("You hit " + monster.name + " for 5 damage!");
monster.state = "angry";
monster.nextDialogue();
LK.setTimeout(function () {
if (monster.hp <= 0) {
battleResultText = "You defeated " + monster.name + "!";
resultText.setText(battleResultText);
resultText.visible = true;
gameState = STATE.BATTLE_RESULT;
} else {
startEnemyAttack();
}
}, 900);
}, 600);
} else if (idx === 1) {
// Act
hideAllMenus();
gameState = STATE.BATTLE_ACT;
showActMenu();
} else if (idx === 2) {
// Item
hideAllMenus();
gameState = STATE.BATTLE_ITEM;
showItemMenu();
} else if (idx === 3) {
// Mercy
if (mercyAvailable) {
hideAllMenus();
dialogueBox.setText("You spare " + monster.name + ".");
monster.state = "spared";
LK.setTimeout(function () {
battleResultText = "You showed mercy to " + monster.name + "!";
resultText.setText(battleResultText);
resultText.visible = true;
gameState = STATE.BATTLE_RESULT;
}, 900);
} else {
dialogueBox.setText("You tried to spare, but Froggle isn't ready.");
LK.setTimeout(function () {
showBattleMenu();
}, 900);
}
}
}
// --- Act option selection ---
function selectActOption(idx) {
hideAllMenus();
if (idx === 0) {
// Check
dialogueBox.setText(monster.name + " - ATK 4 DEF 2\nA nervous little monster.");
LK.setTimeout(function () {
showBattleMenu();
}, 1200);
} else if (idx === 1) {
// Compliment
dialogueBox.setText("You compliment " + monster.name + ".\n" + monster.name + " blushes.");
monster.state = "neutral";
mercyAvailable = true;
LK.setTimeout(function () {
showBattleMenu();
}, 1200);
} else if (idx === 2) {
// Threaten
dialogueBox.setText("You threaten " + monster.name + ".\n" + monster.name + " looks scared.");
monster.state = "angry";
mercyAvailable = false;
LK.setTimeout(function () {
showBattleMenu();
}, 1200);
}
}
// --- Item option selection ---
function selectItemOption(idx) {
var item = playerInventory[idx];
if (item && item.qty > 0) {
playerHP += item.heal;
if (playerHP > playerMaxHP) playerHP = playerMaxHP;
item.qty--;
updateHPText();
dialogueBox.setText("You used " + item.name + ".\nRecovered " + item.heal + " HP!");
LK.setTimeout(function () {
showBattleMenu();
}, 1200);
}
}
// --- Enemy attack phase ---
function startEnemyAttack() {
hideAllMenus();
// Show unique, attentive attack dialogue for each monster
var attackDialogue = "";
switch (monster.name) {
case "Froggle":
attackDialogue = "Froggle leaps at you, eyes wide with fear and determination!";
break;
case "Hopster":
attackDialogue = "Hopster springs high, aiming to land right on your head!";
break;
case "Croaknight":
attackDialogue = "Croaknight draws his sword and charges with a thunderous croak!";
break;
case "Salamandra":
attackDialogue = "Salamandra spits a sizzling stream of fire in your direction!";
break;
case "Turtloid":
attackDialogue = "Turtloid spins in its shell, barreling toward you with surprising speed!";
break;
case "Mothmire":
attackDialogue = "Mothmire flutters above, scattering a cloud of dazzling, stinging dust!";
break;
case "Spindle":
attackDialogue = "Spindle weaves a sticky web and flings it at you!";
break;
case "Barkbark":
attackDialogue = "Barkbark barks wildly and dashes in circles, trying to trip you!";
break;
case "Glimmerbug":
attackDialogue = "Glimmerbug zips around, leaving a blinding trail of sparkling light!";
break;
case "Thornet":
attackDialogue = "Thornet buzzes furiously, diving at you with its venomous stinger!";
break;
case "Shadewisp":
attackDialogue = "Shadewisp flickers and vanishes, then lashes out from the shadows!";
break;
case "Queen Lily":
attackDialogue = "Queen Lily summons a swirling storm of razor-sharp petals your way!";
break;
default:
attackDialogue = monster.name + " attacks fiercely!";
break;
}
dialogueBox.setText(attackDialogue);
heart.visible = true;
heart.x = 2048 / 2;
heart.y = 1800;
attackSlashes = [];
attackTimer = 0;
attackDuration = 90 + Math.floor(Math.random() * 30);
gameState = STATE.BATTLE_DODGE;
showJoystick(true);
}
// --- Endings ---
function showEnding() {
hideAllMenus();
heart.visible = false;
monster.visible = false;
dialogueBox.visible = false;
if (monster.state === "spared") {
resultText.setText("You made a friend: " + monster.name + ".\nA gentle path lies ahead.");
} else if (monster.hp <= 0) {
resultText.setText("You defeated " + monster.name + ".\nBut at what cost?");
} else {
resultText.setText("The story continues...");
}
resultText.visible = true;
LK.setTimeout(function () {
LK.showYouWin();
}, 1800);
}
// --- Input handling ---
// Virtual joystick for heart movement (touch-optimized)
// Ava: This joystick appears during enemy attack phase and is fully touch/mouse compatible.
var joystick = {
active: false,
baseX: 0,
baseY: 0,
stickX: 0,
stickY: 0,
radius: 120,
stickRadius: 60,
visible: false,
baseAsset: null,
stickAsset: null
};
// Create joystick assets (will be positioned below the heart dynamically)
joystick.baseAsset = LK.getAsset('btn_bg', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
// will be set dynamically
y: 0,
// will be set dynamically
width: joystick.radius * 2,
height: joystick.radius * 2
});
joystick.baseAsset.alpha = 0.25;
joystick.baseAsset.visible = false;
game.addChild(joystick.baseAsset);
// Use a simple circle for joystick stick instead of a heart to avoid duplicate heart visuals
joystick.stickAsset = LK.getAsset('btn_hl', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
// will be set dynamically
y: 0,
// will be set dynamically
width: joystick.stickRadius * 2,
height: joystick.stickRadius * 2
});
joystick.stickAsset.alpha = 0.7;
joystick.stickAsset.visible = false;
game.addChild(joystick.stickAsset);
// Add a "MOVE" button styled like the menu buttons, and make it moveable
joystick.moveButton = new MenuButton();
joystick.moveButton.setText("MOVE");
joystick.moveButton.x = 0; // will be set dynamically
joystick.moveButton.y = 0; // will be set dynamically
joystick.moveButton.visible = false;
joystick.moveButton.setHighlight(false);
game.addChild(joystick.moveButton);
// --- Show joystick at start of game (story phase) ---
function showJoystick(show) {
joystick.visible = show;
joystick.baseAsset.visible = show;
joystick.stickAsset.visible = show;
joystick.moveButton.visible = false;
if (show) {
// Place joystick below the heart if heart is visible, otherwise below hero (start of game)
var centerX, baseY;
if (heart && heart.visible) {
centerX = heart.x;
baseY = heart.y + heart.height / 2;
} else if (hero && hero.visible) {
centerX = hero.x;
baseY = hero.y + hero.height / 2;
} else {
centerX = 2048 / 2;
baseY = 2732 / 2 + 400 + 60; // fallback
}
var joystickOffset = 120 + joystick.radius;
var joyX = centerX;
var joyY = baseY + joystickOffset;
var minJoyY = baseY + 100;
var maxJoyY = 2732 - joystick.radius - 40;
if (joyY > maxJoyY) joyY = maxJoyY;
if (joyY < minJoyY) joyY = minJoyY;
joystick.baseAsset.x = joyX;
joystick.baseAsset.y = joyY;
joystick.stickAsset.x = joyX;
joystick.stickAsset.y = joyY;
joystick.baseX = joyX;
joystick.baseY = joyY;
joystick.stickX = joyX;
joystick.stickY = joyY;
} else {
joystick.active = false;
joystick.moveButton.visible = false;
}
}
// Show joystick at the start of the game (story phase)
showJoystick(true);
// Returns true if (x, y) is inside joystick base
function isInJoystick(x, y) {
var dx = x - joystick.baseAsset.x;
var dy = y - joystick.baseAsset.y;
return dx * dx + dy * dy <= joystick.radius * joystick.radius;
}
var draggingHeart = false;
game.down = function (x, y, obj) {
if (gameState === STATE.BATTLE_DODGE) {
// If touch is inside joystick, activate joystick
if (isInJoystick(x, y)) {
joystick.active = true;
joystick.baseX = joystick.baseAsset.x;
joystick.baseY = joystick.baseAsset.y;
joystick.stickX = x;
joystick.stickY = y;
joystick.stickAsset.x = x;
joystick.stickAsset.y = y;
}
} else if (gameState === STATE.BATTLE_MENU || gameState === STATE.BATTLE_ACT || gameState === STATE.BATTLE_ITEM || gameState === STATE.BATTLE_RESULT || gameState === STATE.BATTLE_INTRO || gameState === STATE.STORY) {
handleMenuTouch(x, y);
}
};
game.up = function (x, y, obj) {
joystick.active = false;
joystick.stickAsset.x = joystick.baseAsset.x;
joystick.stickAsset.y = joystick.baseAsset.y;
lastTouchY = null;
};
// Unified move handler for joystick and menu navigation (NO mouse/drag/hover/Enter)
game.move = function (x, y, obj) {
if (gameState === STATE.BATTLE_DODGE) {
// Joystick movement only
if (joystick.active) {
var dx = x - joystick.baseX;
var dy = y - joystick.baseY;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > joystick.radius) {
dx = dx * joystick.radius / dist;
dy = dy * joystick.radius / dist;
}
joystick.stickX = joystick.baseX + dx;
joystick.stickY = joystick.baseY + dy;
joystick.stickAsset.x = joystick.stickX;
joystick.stickAsset.y = joystick.stickY;
// Move heart
var moveSpeed = heart.speed;
var moveX = dx / joystick.radius;
var moveY = dy / joystick.radius;
var minX = 524,
maxX = 2048 - 524,
minY = 1500,
maxY = 2100;
heart.x = clamp(heart.x + moveX * moveSpeed, minX, maxX);
heart.y = clamp(heart.y + moveY * moveSpeed, minY, maxY);
}
}
};
// --- Main update loop ---
game.update = function () {
// Heart update
if (gameState === STATE.BATTLE_DODGE) {
// Keep joystick locked below the heart every frame
var heartCenterX = heart.x;
var heartBottomY = heart.y + heart.height / 2;
var joystickOffset = 120 + joystick.radius;
var joyX = heartCenterX;
var joyY = heartBottomY + joystickOffset;
var minJoyY = heart.y + 100;
var maxJoyY = 2732 - joystick.radius - 40;
if (joyY > maxJoyY) joyY = maxJoyY;
if (joyY < minJoyY) joyY = minJoyY;
joystick.baseAsset.x = joyX;
joystick.baseAsset.y = joyY;
if (!joystick.active) {
joystick.stickAsset.x = joyX;
joystick.stickAsset.y = joyY;
joystick.stickX = joyX;
joystick.stickY = joyY;
}
joystick.baseX = joyX;
joystick.baseY = joyY;
// Spawn slashes
if (attackTimer % 24 === 0 && attackSlashes.length < 5) {
var slash = new AttackSlash();
slash.y = 1600 + Math.random() * 400;
slash.x = Math.random() < 0.5 ? 2048 - 200 : 200;
slash.direction = slash.x < 1024 ? 1 : -1;
attackSlashes.push(slash);
game.addChild(slash);
}
// Update slashes
for (var i = attackSlashes.length - 1; i >= 0; i--) {
var s = attackSlashes[i];
s.update();
// Collision with heart
var dx = s.x - heart.x,
dy = s.y - heart.y;
if (s.active && dx * dx + dy * dy < 60 * 60) {
s.active = false;
playerHP -= 4;
updateHPText();
LK.effects.flashObject(heart, 0xff0000, 400);
if (playerHP <= 0) {
playerHP = 0;
updateHPText();
LK.effects.flashScreen(0xff0000, 1200);
LK.showGameOver();
return;
}
}
if (!s.active) {
s.destroy();
attackSlashes.splice(i, 1);
}
}
attackTimer++;
if (attackTimer > attackDuration) {
// End attack phase
for (var i = 0; i < attackSlashes.length; i++) attackSlashes[i].destroy();
attackSlashes = [];
heart.visible = false;
showJoystick(false);
gameState = STATE.BATTLE_MENU;
showBattleMenu();
}
}
};
// --- Touch navigation for menu (swipe up/down to change selection) ---
var lastTouchY = null;
// --- End of file ---
make chara from underatle. In-Game asset. 2d. High contrast. No shadows
make undertale heart. In-Game asset. 2d. High contrast. No shadows
make it undertale mercy button. In-Game asset. 2d. High contrast. No shadows
make sans bones. In-Game asset. 2d. High contrast. No shadows
make bones like undartale sans but just one bone. In-Game asset. 2d. High contrast. No shadows
black rectangle. In-Game asset. 2d. High contrast. No shadows
make funny thing. In-Game asset. 2d. High contrast. No shadows
asgore undeertale. In-Game asset. 2d. High contrast. No shadows
black rectangle. In-Game asset. 2d. High contrast. No shadows