User prompt
make every bosses own attack and make it attentive
User prompt
add 10 bosses or more and make them own dialagoes
User prompt
dont make *froggle atack* make every characthers own dialagoues
User prompt
there is croaknight:froggle is furious! this is typo fix it croaknight is not froggle
User prompt
there is just 2 heart make it 1
User prompt
make the typos fix
User prompt
undo
User prompt
make move button moveable with that fight act item and mercy buttons
User prompt
add 1 joystick too to start of the game
User prompt
make game more long
User prompt
Put the virtual joystick directly below the player’s heart (soul) sprite on the screen. It should always stay centered and responsive, so the player can move smoothly during enemy attacks. Right now, the UI feels like it’s falling apart — everything’s floating in random places like a busted mess. Fix the layout so that it’s clean, balanced, and locked in place. No more wobbly, disconnected bullshit. This game should feel tight and deliberate, like a real fight — not some amateur spaghetti code. Center the joystick, keep it stable, and make sure it doesn’t slide around or bug out. Clean it up. Lock it in. Make it solid.
User prompt
Listen — stop trying to make the game use the mouse. Mouse controls suck for this. It’s slow, clunky, and annoying as hell. EVERYTHING — and I mean EVERY. FUCKING. THING. — should be controlled with the virtual joystick and on-screen buttons. No clicking, no hovering, no pointing — NONE of that crap. Delete anything that needs the Enter key or mouse input. I don’t want to see a single “Enter” label or have to click anything to play. This game needs to feel clean, responsive, and actually fun to play — not like I’m wrestling with a goddamn UI from 2005. So fix it, and do it right. No mouse. No Enter. Just joystick and button taps. Got it? Good. Now get to work.
User prompt
The current combat system is hard to interact with using just the mouse. Clicking on text-based options like “Fight”, “Act”, “Item”, and “Mercy” every time is slow and frustrating — especially during tense moments. To fix this, I want you to add clear, clickable buttons above or around each of those options. Each button should look like a real UI button, not just text. Also, please add a small label (like “ENTER” or “CLICK”) on each button so it's obvious that the player can just press it. This will make it more intuitive and easier to play, especially for users who don’t want to use a keyboard. On top of that, add a virtual joystick (like a circle pad) on the screen that lets the player dodge enemy attacks during the enemy phase — similar to the bullet-dodging sections in Undertale. Everything should be optimized for mouse or touch controls, so it's smooth and responsive even without a keyboard.
User prompt
The current combat system is hard to interact with using just the mouse. Clicking on text-based options like “Fight”, “Act”, “Item”, and “Mercy” every time is slow and frustrating — especially during tense moments. To fix this, I want you to add clear, clickable buttons above or around each of those options. Each button should look like a real UI button, not just text. Also, please add a small label (like “ENTER” or “CLICK”) on each button so it's obvious that the player can just press it. This will make it more intuitive and easier to play, especially for users who don’t want to use a keyboard. On top of that, add a virtual joystick (like a circle pad) on the screen that lets the player dodge enemy attacks during the enemy phase — similar to the bullet-dodging sections in Undertale. Everything should be optimized for mouse or touch controls, so it's smooth and responsive even without a keyboard.
User prompt
Make the combat interface easy to use. Add on-screen buttons for the “Fight”, “Act”, “Item”, and “Mercy” options, so the player can simply tap them instead of using keyboard keys. Also, add a virtual joystick that allows the player to move and dodge incoming enemy projectiles during the enemy's turn — just like in Undertale’s bullet-dodging system.
User prompt
add enter buttons on that buttons
User prompt
make fight act item mercy buttons touchable
Code edit (1 edits merged)
Please save this source code
User prompt
Heartstrings: Echoes of the Underworld
Initial prompt
I want you to create a game inspired by Undertale. It should have a similar atmosphere, story-driven mechanics, and emotional depth. The gameplay should include turn-based combat with options like "Fight", "Act", "Item", and "Mercy", and every choice should affect the story and endings. The art style can be pixel-based, and the characters should be original but feel like they belong in the Undertale universe. I want it to have multiple endings, secrets, and a moral system that responds to the player's actions. Start by outlining the game concept: setting, main character, gameplay loop, and tone. Then you can move on to designing characters, writing sample dialogue, and building story branches. Make sure it captures the heart and soul of Undertale without directly copying it.
/**** * 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 "Froggle looks relieved."; if (self.state === "angry") return "Froggle 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 ****/ // --- Game State --- // Main character (the "child") // Enemy monster (first encounter, "Froggit" inspired, but original) // Attack indicator (red slash) // Heart (player soul in battle) // Button backgrounds // Button highlight // Dialogue box // Mercy yellow 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() { 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; 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; 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; monsters = [m1, m2, m3]; } 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(); dialogueBox.setText("Froggle attacks!"); 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); joystick.stickAsset = LK.getAsset('heart', { 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" label to the joystick base for clarity joystick.moveLabel = new Text2("MOVE", { size: 36, fill: 0xFFE066 }); joystick.moveLabel.anchor.set(0.5, 0.5); joystick.moveLabel.x = 0; // will be set dynamically joystick.moveLabel.y = 0; // will be set dynamically joystick.moveLabel.visible = false; game.addChild(joystick.moveLabel); function showJoystick(show) { joystick.visible = show; joystick.baseAsset.visible = show; joystick.stickAsset.visible = show; joystick.moveLabel.visible = show; if (show) { // Place joystick directly below the heart, centered horizontally // Heart is always visible at this point var heartCenterX = heart.x; var heartBottomY = heart.y + heart.height / 2; var joystickOffset = 120 + joystick.radius; // 120px below heart, plus joystick radius var joyX = heartCenterX; var joyY = heartBottomY + joystickOffset; // Clamp joystick to stay inside game area 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; joystick.stickAsset.x = joyX; joystick.stickAsset.y = joyY; joystick.moveLabel.x = joyX; joystick.moveLabel.y = joyY; joystick.baseX = joyX; joystick.baseY = joyY; joystick.stickX = joyX; joystick.stickY = joyY; } else { joystick.active = false; } } // 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.moveLabel.x = joyX; joystick.moveLabel.y = 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 ---
===================================================================
--- original.js
+++ change.js
@@ -141,9 +141,12 @@
ENDING: 7
};
var gameState = STATE.STORY;
// --- Main variables ---
-var hero, monster, heart, dialogueBox;
+var hero, heart, dialogueBox;
+var monsters = [];
+var currentMonsterIndex = 0;
+var monster = null;
var menuButtons = [];
var selectedMenu = 0;
var actButtons = [];
var selectedAct = 0;
@@ -162,9 +165,42 @@
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!"];
+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() {
+ 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;
+ 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;
+ 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;
+ monsters = [m1, m2, m3];
+}
+createMonsters();
+monster = monsters[0];
// --- Utility functions ---
function showDialogue(text) {
dialogueBox.setText(text);
dialogueBox.visible = true;
@@ -264,10 +300,30 @@
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
+ // Start battle with current monster
gameState = STATE.BATTLE_INTRO;
hero.visible = false;
monster.visible = true;
showDialogue(monster.getDialogue());
@@ -286,9 +342,9 @@
}
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.getDialogue());
+ dialogueBox.setText(monster.name + ": " + monster.getDialogue());
mercyButton.bg.visible = !mercyAvailable;
if (mercyAvailable) {
mercyButton.bg.visible = false;
if (!mercyButton.mercyAsset) {
@@ -367,10 +423,22 @@
} else if (gameState === STATE.BATTLE_RESULT) {
// Tap to continue
resultText.visible = false;
if (monster.hp <= 0 || monster.state === "spared") {
- gameState = STATE.ENDING;
- showEnding();
+ // 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();
}
@@ -400,14 +468,14 @@
LK.setTimeout(function () {
// Simple attack: always hit for 5
monster.hp -= 5;
if (monster.hp < 0) monster.hp = 0;
- dialogueBox.setText("You hit Froggle for 5 damage!");
+ dialogueBox.setText("You hit " + monster.name + " for 5 damage!");
monster.state = "angry";
monster.nextDialogue();
LK.setTimeout(function () {
if (monster.hp <= 0) {
- battleResultText = "You won!";
+ battleResultText = "You defeated " + monster.name + "!";
resultText.setText(battleResultText);
resultText.visible = true;
gameState = STATE.BATTLE_RESULT;
} else {
@@ -428,12 +496,12 @@
} else if (idx === 3) {
// Mercy
if (mercyAvailable) {
hideAllMenus();
- dialogueBox.setText("You spare Froggle.");
+ dialogueBox.setText("You spare " + monster.name + ".");
monster.state = "spared";
LK.setTimeout(function () {
- battleResultText = "You showed mercy!";
+ battleResultText = "You showed mercy to " + monster.name + "!";
resultText.setText(battleResultText);
resultText.visible = true;
gameState = STATE.BATTLE_RESULT;
}, 900);
@@ -449,23 +517,23 @@
function selectActOption(idx) {
hideAllMenus();
if (idx === 0) {
// Check
- dialogueBox.setText("Froggle - ATK 4 DEF 2\nA nervous little monster.");
+ 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 Froggle.\nFroggle blushes.");
+ 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 Froggle.\nFroggle looks scared.");
+ dialogueBox.setText("You threaten " + monster.name + ".\n" + monster.name + " looks scared.");
monster.state = "angry";
mercyAvailable = false;
LK.setTimeout(function () {
showBattleMenu();
@@ -505,11 +573,11 @@
heart.visible = false;
monster.visible = false;
dialogueBox.visible = false;
if (monster.state === "spared") {
- resultText.setText("You made a friend.\nA gentle path lies ahead.");
+ resultText.setText("You made a friend: " + monster.name + ".\nA gentle path lies ahead.");
} else if (monster.hp <= 0) {
- resultText.setText("You defeated Froggle.\nBut at what cost?");
+ resultText.setText("You defeated " + monster.name + ".\nBut at what cost?");
} else {
resultText.setText("The story continues...");
}
resultText.visible = true;
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