User prompt
3. Delete All “Enter” Buttons or Labels. Replace them with clean, labeled on-screen buttons (like “ACT”, “ITEM”, etc.). These must be large, spaced out, and easily tappable. Show a little visual feedback when pressed — highlight or animate the button.
User prompt
1. Joystick Placement: Put the virtual joystick directly below the player’s soul (heart) — centered, fixed in place, and never drifting. It needs to respond instantly. No lag, no weird movement. Use proper positioning: for example, position: fixed; bottom: 20%; left: 50%; transform: translateX(-50%).
User prompt
Don’t use the same asset or image for all bosses. That’s lazy and wrong. Each boss must have a unique image/sprite. Store them separately — for example: boss1.png boss2.png boss3.png And when loading each boss, load the correct image asset based on the boss ID or name. DO NOT override or reuse the same image for all bosses. Here’s a basic idea in code: js Kopyala Düzenle let bossAssets = { "sans": "assets/boss_sans.png", "papyrus": "assets/boss_papyrus.png", "undyne": "assets/boss_undyne.png" }; let currentBoss = "papyrus"; let bossImage = loadImage(bossAssets[currentBoss]); You MUST treat each boss as a unique entity. No more clone armies. Fix it.
User prompt
DO NOT reuse the same asset or image for all bosses. I will manually provide a different image asset for each boss, and you need to load the correct one depending on which boss is being used. Example: js Kopyala Düzenle if (bossName === "sans") { loadBossImage("boss_sans.png"); } else if (bossName === "papyrus") { loadBossImage("boss_papyrus.png"); } else if (bossName === "undyne") { loadBossImage("boss_undyne.png"); } You are NOT allowed to override boss assets automatically. You should only use the specific image I assign per boss. Don’t force the same image for every boss. That ruins the game. Wait for me to provide the correct asset, then use that.
User prompt
sans dissepeares sometimes fix that
User prompt
make it to every boss you amde
User prompt
make sans 20 hearts
User prompt
not make sans different color make hearts
User prompt
make his hp 20
User prompt
make it upper
User prompt
make it more bigger and increase bar
User prompt
make sans health bar
User prompt
make every boss another color
User prompt
it says froggle fix it
User prompt
make sanss attacks more hard
User prompt
make the hopsters name sans and make dialagoes about sans
User prompt
when we click cesur in act make the title Sezaryen yumurtaları
User prompt
make health 50
User prompt
dont make bosses easy to spare make it harder we can try 5 6 times and we spare
User prompt
when we press cesur there is a dialagoue cesurun sezaryan yumurtaları text make it
User prompt
add act button a cesur
User prompt
make funny dialagoues when we press mercy *SOMETİMES*
User prompt
make item box *leave item box* button
User prompt
make skip tutorial button
User prompt
THERE İS NO EGEGOKALP İTEM İN İTEM BO
/****
* 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 = "Sans";
self.state = "neutral"; // can be "neutral", "spared", "angry"
self.dialogue = ["Sans appears with a grin. \"hey kiddo.\"", "Sans shrugs. \"you look like you've seen a ghost.\"", "Sans winks. \"get dunked on? nah, just kidding.\"", "Sans tells a pun. \"what do you call a lazy skeleton? bone-idle.\""];
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
****/
// Add round joystick assets
// 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 = 50;
var playerMaxHP = 50;
// Add more items to the inventory!
var playerInventory = [{
name: "Candy",
heal: 10,
qty: 1
}, {
name: "Donut",
heal: 15,
qty: 1
}, {
name: "Pie",
heal: 20,
qty: 1
}, {
name: "Soda",
heal: 8,
qty: 2
}, {
name: "Bandage",
heal: 5,
qty: 3
}, {
name: "egegokalp",
type: "special",
effect: "sezaryen_yumurtasi",
qty: 1
}];
var sezaryenYumurtasi = 0; // Track extra sezaryen yumurtası
var selectedItem = 0;
// Support up to 5 item buttons for more items
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;
m1.mercyAvailable = false;
if (m1.children && m1.children[0]) m1.children[0].tint = 0x4caf50; // Green
// 2. Sans
var m2 = new Monster();
m2.x = 2048 / 2;
m2.y = 900;
m2.hp = 18;
m2.maxHp = 18;
m2.name = "Sans";
m2.state = "neutral";
m2.dialogue = ["Sans appears with a grin. \"hey kiddo.\"", "Sans shrugs. \"you look like you've seen a ghost.\"", "Sans winks. \"get dunked on? nah, just kidding.\"", "Sans tells a pun. \"what do you call a lazy skeleton? bone-idle.\""];
m2.dialogueIndex = 0;
m2.mercyAvailable = false;
if (m2.children && m2.children[0]) m2.children[0].tint = 0x00bfff; // Blue
// 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;
m3.mercyAvailable = false;
if (m3.children && m3.children[0]) m3.children[0].tint = 0x9c27b0; // Purple
// 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;
m4.mercyAvailable = false;
if (m4.children && m4.children[0]) m4.children[0].tint = 0xff9800; // Orange
// 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;
m5.mercyAvailable = false;
if (m5.children && m5.children[0]) m5.children[0].tint = 0x795548; // Brown
// 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;
m6.mercyAvailable = false;
if (m6.children && m6.children[0]) m6.children[0].tint = 0xffeb3b; // Yellow
// 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;
m7.mercyAvailable = false;
if (m7.children && m7.children[0]) m7.children[0].tint = 0xffffff; // White
// 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;
m8.mercyAvailable = false;
if (m8.children && m8.children[0]) m8.children[0].tint = 0xffc107; // Amber
// 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;
m9.mercyAvailable = false;
if (m9.children && m9.children[0]) m9.children[0].tint = 0x00ffea; // Cyan
// 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;
m10.mercyAvailable = false;
if (m10.children && m10.children[0]) m10.children[0].tint = 0xe91e63; // Pink
// 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;
m11.mercyAvailable = false;
if (m11.children && m11.children[0]) m11.children[0].tint = 0x607d8b; // Blue Grey
// 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;
m12.mercyAvailable = false;
if (m12.children && m12.children[0]) m12.children[0].tint = 0xff69b4; // Hot Pink
m1.actCount = 0;
m1.mercyAvailable = false;
m2.actCount = 0;
m2.mercyAvailable = false;
m3.actCount = 0;
m3.mercyAvailable = false;
m4.actCount = 0;
m4.mercyAvailable = false;
m5.actCount = 0;
m5.mercyAvailable = false;
m6.actCount = 0;
m6.mercyAvailable = false;
m7.actCount = 0;
m7.mercyAvailable = false;
m8.actCount = 0;
m8.mercyAvailable = false;
m9.actCount = 0;
m9.mercyAvailable = false;
m10.actCount = 0;
m10.mercyAvailable = false;
m11.actCount = 0;
m11.mercyAvailable = false;
m12.actCount = 0;
m12.mercyAvailable = false;
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: 50/50", {
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);
// --- Skip Tutorial Button ---
var skipTutorialBtn = new MenuButton();
skipTutorialBtn.setText("Skip Tutorial");
skipTutorialBtn.x = 2048 - 350;
skipTutorialBtn.y = 180;
skipTutorialBtn.visible = false;
skipTutorialBtn.setHighlight(false);
game.addChild(skipTutorialBtn);
function showSkipTutorial(show) {
skipTutorialBtn.visible = !!show;
}
// --- 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);
// --- Monster Health Bar ---
var monsterHpBarBg = LK.getAsset('dialogue_box', {
anchorX: 0.5,
anchorY: 0.5,
width: 520,
height: 38,
x: 2048 / 2,
y: 820
});
monsterHpBarBg.tint = 0x222222;
monsterHpBarBg.alpha = 0.7;
monsterHpBarBg.visible = false;
game.addChild(monsterHpBarBg);
var monsterHpBar = LK.getAsset('dialogue_box', {
anchorX: 0.5,
anchorY: 0.5,
width: 500,
height: 24,
x: 2048 / 2,
y: 820
});
monsterHpBar.tint = 0x00e676;
monsterHpBar.alpha = 0.95;
monsterHpBar.visible = false;
game.addChild(monsterHpBar);
var monsterHpText = new Text2('', {
size: 36,
fill: "#fff"
});
monsterHpText.anchor.set(0.5, 0.5);
monsterHpText.x = 2048 / 2;
monsterHpText.y = 820;
monsterHpText.visible = false;
game.addChild(monsterHpText);
function updateMonsterHpBar() {
if (!monster || typeof monster.hp === "undefined" || typeof monster.maxHp === "undefined") {
monsterHpBarBg.visible = false;
monsterHpBar.visible = false;
monsterHpText.visible = false;
return;
}
// Only show during battle
if (gameState === STATE.BATTLE_INTRO || gameState === STATE.BATTLE_MENU || gameState === STATE.BATTLE_ACT || gameState === STATE.BATTLE_ATTACK || gameState === STATE.BATTLE_DODGE) {
monsterHpBarBg.visible = true;
monsterHpBar.visible = true;
monsterHpText.visible = true;
// Clamp values
var hp = Math.max(0, Math.min(monster.hp, monster.maxHp));
var maxHp = Math.max(1, monster.maxHp);
var percent = hp / maxHp;
monsterHpBar.width = 500 * percent;
monsterHpBar.x = 2048 / 2 - (1 - percent) * 250;
monsterHpText.setText(monster.name + " HP: " + hp + "/" + maxHp);
} else {
monsterHpBarBg.visible = false;
monsterHpBar.visible = false;
monsterHpText.visible = false;
}
}
// --- Heart (player soul in battle) ---
heart = new Heart();
heart.x = 2048 / 2;
heart.y = 1800;
// Place heart in the center of the even larger arena at start of battle
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", "Cesur"];
for (var i = 0; i < 4; 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) ---
// Support up to 5 item buttons for more items
for (var i = 0; i < 5; i++) {
var btn = new MenuButton();
btn.x = 524 + i % 3 * 500;
btn.y = 2732 - 500 - Math.floor(i / 3) * 160;
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++;
// Show skip tutorial button only during story phase, before battle starts
if (gameState === STATE.STORY && storyStep < storyTexts.length) {
showSkipTutorial(true);
} else {
showSkipTutorial(false);
}
// 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;
monster.actCount = 0;
monster.mercyAvailable = false;
game.addChild(monster);
updateMonsterHpBar();
}, 1200);
}
if (storyStep === 5 && currentMonsterIndex === 1) {
LK.setTimeout(function () {
currentMonsterIndex = 2;
monster.visible = false;
monster = monsters[currentMonsterIndex];
monster.visible = false;
monster.actCount = 0;
monster.mercyAvailable = false;
game.addChild(monster);
}, 1200);
}
} else {
showSkipTutorial(false);
// 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());
var mercyState = monster.mercyAvailable === true;
mercyButton.bg.visible = !mercyState;
if (mercyState) {
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;
// Always show egegokalp next to Bandage in the item menu
// Find Bandage and egegokalp indices in inventory
var bandageIdx = -1,
egegokalpIdx = -1;
for (var i = 0; i < playerInventory.length; i++) {
if (playerInventory[i].name === "Bandage") bandageIdx = i;
if (playerInventory[i].name === "egegokalp") egegokalpIdx = i;
}
// Build a display order: all items up to Bandage, then Bandage, then egegokalp (if present), then the rest
var displayOrder = [];
for (var i = 0; i < playerInventory.length; i++) {
if (i === bandageIdx) {
displayOrder.push(i);
if (egegokalpIdx !== -1) displayOrder.push(egegokalpIdx);
} else if (i !== egegokalpIdx) {
displayOrder.push(i);
}
}
for (var i = 0; i < itemButtons.length; i++) {
var invIdx = displayOrder[i];
var item = playerInventory[invIdx];
if (item && item.qty > 0) {
itemButtons[i].setText(item.name + " (" + item.qty + ")");
itemButtons[i].visible = true;
itemButtons[i].setHighlight(invIdx === selectedItem);
} else {
itemButtons[i].visible = false;
}
}
// Add a "Leave Item Box" button below the item buttons
if (!window.leaveItemBoxBtn) {
window.leaveItemBoxBtn = new MenuButton();
window.leaveItemBoxBtn.setText("Leave Item Box");
window.leaveItemBoxBtn.x = 2048 / 2;
window.leaveItemBoxBtn.y = 2732 - 200;
window.leaveItemBoxBtn.visible = false;
window.leaveItemBoxBtn.setHighlight(false);
game.addChild(window.leaveItemBoxBtn);
}
window.leaveItemBoxBtn.visible = true;
window.leaveItemBoxBtn.setHighlight(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;
if (window.leaveItemBoxBtn) window.leaveItemBoxBtn.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;
monster.actCount = 0;
monster.mercyAvailable = false;
game.addChild(monster);
updateMonsterHpBar();
// 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) {
// Check leave item box button first
if (window.leaveItemBoxBtn && window.leaveItemBoxBtn.visible && x > window.leaveItemBoxBtn.x - 200 && x < window.leaveItemBoxBtn.x + 200 && y > window.leaveItemBoxBtn.y - 60 && y < window.leaveItemBoxBtn.y + 60) {
// Hide item menu, return to battle menu
window.leaveItemBoxBtn.visible = false;
hideAllMenus();
gameState = STATE.BATTLE_MENU;
showBattleMenu();
return;
}
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;
}
}
// If tap is not on any button, do nothing
}
}
// --- 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;
updateMonsterHpBar();
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 (monster.mercyAvailable === true) {
hideAllMenus();
// 1 in 3 chance to show a funny dialogue
var funnyMercyLines = ["You spare " + monster.name + ".\n" + monster.name + " gives you a confused thumbs up.", "You spare " + monster.name + ".\n" + monster.name + " wonders if this is a dating sim.", "You spare " + monster.name + ".\n" + monster.name + " is now legally your roommate.", "You spare " + monster.name + ".\n" + monster.name + " croaks in relief... or is it laughter?", "You spare " + monster.name + ".\n" + monster.name + " offers you a coupon for free flies.", "You spare " + monster.name + ".\n" + monster.name + " does a little dance.", "You spare " + monster.name + ".\n" + monster.name + " is writing about this on its blog.", "You spare " + monster.name + ".\n" + monster.name + " is now following you on RibbitBook.", "You spare " + monster.name + ".\n" + monster.name + " will remember this.", "You spare " + monster.name + ".\n" + monster.name + " is already planning your birthday party."];
var showFunny = Math.random() < 0.33;
if (showFunny) {
var idx = Math.floor(Math.random() * funnyMercyLines.length);
dialogueBox.setText(funnyMercyLines[idx]);
} else {
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 " + monster.name + " isn't ready.");
LK.setTimeout(function () {
showBattleMenu();
}, 900);
}
}
}
// --- Act option selection ---
function selectActOption(idx) {
hideAllMenus();
// Always restore menu input after Act phase, regardless of which Act was chosen
function restoreMenu() {
gameState = STATE.BATTLE_MENU;
showBattleMenu();
for (var i = 0; i < menuButtons.length; i++) {
menuButtons[i].visible = true;
}
}
if (idx === 0) {
// Check
dialogueBox.setText(monster.name + " - ATK 4 DEF 2\nA nervous little monster.");
LK.setTimeout(restoreMenu, 1200);
} else if (idx === 1) {
// Compliment
dialogueBox.setText("You compliment " + monster.name + ".\n" + monster.name + " blushes.");
monster.state = "neutral";
// --- Mercy Progression: require 5-6 successful Acts before mercy is available ---
if (typeof monster.actCount === "undefined") monster.actCount = 0;
monster.actCount++;
if (monster.actCount >= 5 + Math.floor(Math.random() * 2)) {
monster.mercyAvailable = true;
} else {
monster.mercyAvailable = false;
}
LK.setTimeout(restoreMenu, 1200);
} else if (idx === 2) {
// Threaten
dialogueBox.setText("You threaten " + monster.name + ".\n" + monster.name + " looks scared.");
monster.state = "angry";
if (typeof monster.actCount === "undefined") monster.actCount = 0;
monster.actCount++;
if (monster.actCount >= 5 + Math.floor(Math.random() * 2)) {
monster.mercyAvailable = true;
} else {
monster.mercyAvailable = false;
}
LK.setTimeout(restoreMenu, 1200);
} else if (idx === 3) {
// Cesur
dialogueBox.setText("Sezaryen yumurtaları");
monster.state = "neutral";
if (typeof monster.actCount === "undefined") monster.actCount = 0;
monster.actCount++;
if (monster.actCount >= 5 + Math.floor(Math.random() * 2)) {
monster.mercyAvailable = true;
} else {
monster.mercyAvailable = false;
}
LK.setTimeout(restoreMenu, 1200);
}
}
// --- Item option selection ---
function selectItemOption(idx) {
// Use the same displayOrder as in showItemMenu to map button index to inventory index
var bandageIdx = -1,
egegokalpIdx = -1;
for (var i = 0; i < playerInventory.length; i++) {
if (playerInventory[i].name === "Bandage") bandageIdx = i;
if (playerInventory[i].name === "egegokalp") egegokalpIdx = i;
}
var displayOrder = [];
for (var i = 0; i < playerInventory.length; i++) {
if (i === bandageIdx) {
displayOrder.push(i);
if (egegokalpIdx !== -1) displayOrder.push(egegokalpIdx);
} else if (i !== egegokalpIdx) {
displayOrder.push(i);
}
}
var invIdx = displayOrder[idx];
var item = playerInventory[invIdx];
if (item && item.qty > 0) {
if (item.name === "egegokalp") {
sezaryenYumurtasi += 10;
item.qty--;
dialogueBox.setText("You used egegokalp!\nKazandın: 10 sezaryen yumurtası!\nToplam: " + sezaryenYumurtasi);
} else {
playerHP += item.heal;
if (playerHP > playerMaxHP) playerHP = playerMaxHP;
item.qty--;
updateHPText();
dialogueBox.setText("You used " + item.name + ".\nRecovered " + item.heal + " HP!");
}
LK.setTimeout(function () {
// Always restore menu input after Item phase, just like Act
gameState = STATE.BATTLE_MENU;
showBattleMenu();
for (var i = 0; i < menuButtons.length; i++) {
menuButtons[i].visible = true;
}
}, 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 "Sans":
attackDialogue = "Sans gives you a lazy grin. \"let's see if you can dodge this, kid.\"";
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;
// Unique special attacks for each boss
switch (monster.name) {
case "Froggle":
// Standard horizontal slashes, easy pattern
attackDuration = 90 + Math.floor(Math.random() * 20);
break;
case "Sans":
// Sans: Harder attack - more slashes, faster, and less predictable patterns
attackDuration = 170;
LK.setTimeout(function () {
for (var i = 0; i < 10; i++) {
LK.setTimeout(function (j) {
var slash = new AttackSlash();
var pattern = Math.random();
if (pattern < 0.4) {
// Horizontal bone, faster
slash.y = 1200 + Math.random() * 1100;
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.speed = 28 + Math.random() * 12;
slash.update = function () {
this.x += this.speed * this.direction;
if (this.x < 0 || this.x > 2048) this.active = false;
};
} else if (pattern < 0.7) {
// Vertical bone, faster
slash.x = 400 + Math.random() * (2048 - 800);
slash.y = 1050;
slash.direction = 1;
slash.speed = 24 + Math.random() * 10;
slash.update = function () {
this.y += this.speed * this.direction;
if (this.y > 2550) this.active = false;
};
} else {
// Diagonal bone (hard to dodge)
var fromLeft = Math.random() < 0.5;
slash.x = fromLeft ? 200 : 2048 - 200;
slash.y = 1050 + Math.random() * 800;
slash.direction = fromLeft ? 1 : -1;
slash.speed = 22 + Math.random() * 10;
var diag = Math.random() * 0.8 + 0.6;
slash.update = function () {
this.x += this.speed * this.direction;
this.y += this.speed * diag;
if (this.x < 0 || this.x > 2048 || this.y > 2550) this.active = false;
};
}
attackSlashes.push(slash);
game.addChild(slash);
}, i * 110, i);
}
}, 300);
break;
case "Croaknight":
// Croaknight: Sword combo - vertical sword, then two fast horizontal slashes, then a cross slash
attackDuration = 130;
// First, a vertical sword slash down the center
LK.setTimeout(function () {
var slash = new AttackSlash();
slash.x = 1024;
slash.y = 1500;
slash.direction = 1; // Down
slash.speed = 36;
// Vertical slash: moves down only
slash.update = function () {
this.y += this.speed * this.direction;
if (this.y > 2100) this.active = false;
};
attackSlashes.push(slash);
game.addChild(slash);
}, 200);
// Then, two fast horizontal slashes from both sides (sword swings)
LK.setTimeout(function () {
for (var i = 0; i < 2; i++) {
var slash = new AttackSlash();
slash.y = 1700 + i * 200;
slash.x = i === 0 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.speed = 32 + Math.random() * 6;
// Horizontal sword slash: moves horizontally only
slash.update = function () {
this.x += this.speed * this.direction;
if (this.x < 0 || this.x > 2048) this.active = false;
};
attackSlashes.push(slash);
game.addChild(slash);
}
}, 700);
// Then, a cross slash from a random corner (diagonal sword slash)
LK.setTimeout(function () {
var slash = new AttackSlash();
var fromLeft = Math.random() < 0.5;
slash.x = fromLeft ? 200 : 2048 - 200;
slash.y = 1500;
slash.direction = fromLeft ? 1 : -1;
slash.speed = 26 + Math.random() * 8;
// Crosses diagonally
slash.update = function () {
this.x += this.speed * this.direction;
this.y += this.speed * 0.7;
if (this.x < 0 || this.x > 2048 || this.y > 2200) this.active = false;
};
attackSlashes.push(slash);
game.addChild(slash);
}, 1100);
break;
case "Salamandra":
// Rapid fire slashes, more frequent
attackDuration = 80;
LK.setTimeout(function () {
for (var i = 0; i < 3; i++) {
LK.setTimeout(function () {
var slash = new AttackSlash();
slash.y = 1600 + Math.random() * 400;
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.speed = 28 + Math.random() * 8;
attackSlashes.push(slash);
game.addChild(slash);
}, i * 200);
}
}, 200);
break;
case "Turtloid":
// Slow, heavy slashes, fewer but larger
attackDuration = 110;
LK.setTimeout(function () {
for (var i = 0; i < 2; i++) {
var slash = new AttackSlash();
slash.y = 1700 + i * 200;
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.speed = 14 + Math.random() * 4;
attackSlashes.push(slash);
game.addChild(slash);
}
}, 500);
break;
case "Mothmire":
// Slashes appear in a wave pattern (y changes)
attackDuration = 100;
LK.setTimeout(function () {
for (var i = 0; i < 4; i++) {
LK.setTimeout(function (j) {
var slash = new AttackSlash();
slash.y = 1600 + j * 100;
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
attackSlashes.push(slash);
game.addChild(slash);
}.bind(null, i), i * 180);
}
}, 200);
break;
case "Spindle":
// Many fast, thin slashes (webs)
attackDuration = 90;
LK.setTimeout(function () {
for (var i = 0; i < 6; i++) {
LK.setTimeout(function () {
var slash = new AttackSlash();
slash.y = 1600 + Math.random() * 400;
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.speed = 30 + Math.random() * 10;
attackSlashes.push(slash);
game.addChild(slash);
}, i * 100);
}
}, 100);
break;
case "Barkbark":
// Slashes come in zig-zag (alternate y)
attackDuration = 100;
LK.setTimeout(function () {
for (var i = 0; i < 4; i++) {
var slash = new AttackSlash();
slash.y = 1600 + i % 2 * 300;
slash.x = i % 2 === 0 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
attackSlashes.push(slash);
game.addChild(slash);
}
}, 300);
break;
case "Glimmerbug":
// Slashes are invisible for a moment, then appear
attackDuration = 100;
LK.setTimeout(function () {
for (var i = 0; i < 3; i++) {
var slash = new AttackSlash();
slash.y = 1600 + Math.random() * 400;
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.alpha = 0;
attackSlashes.push(slash);
game.addChild(slash);
LK.setTimeout(function (s) {
s.alpha = 1;
}, 400, slash);
}
}, 200);
break;
case "Thornet":
// Very fast, many slashes, short duration
attackDuration = 70;
LK.setTimeout(function () {
for (var i = 0; i < 7; i++) {
LK.setTimeout(function () {
var slash = new AttackSlash();
slash.y = 1600 + Math.random() * 400;
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.speed = 36 + Math.random() * 10;
attackSlashes.push(slash);
game.addChild(slash);
}, i * 60);
}
}, 100);
break;
case "Shadewisp":
// Slashes appear at random positions, sometimes from center
attackDuration = 110;
LK.setTimeout(function () {
for (var i = 0; i < 4; i++) {
var slash = new AttackSlash();
if (i % 2 === 0) {
slash.x = 1024;
slash.direction = Math.random() < 0.5 ? 1 : -1;
} else {
slash.x = Math.random() < 0.5 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
}
slash.y = 1600 + Math.random() * 400;
attackSlashes.push(slash);
game.addChild(slash);
}
}, 400);
break;
case "Queen Lily":
// Slashes from all sides, in a spiral pattern (simulate by offsetting y)
attackDuration = 130;
LK.setTimeout(function () {
for (var i = 0; i < 6; i++) {
LK.setTimeout(function (j) {
var slash = new AttackSlash();
slash.y = 1600 + j % 3 * 200;
slash.x = j % 2 === 0 ? 200 : 2048 - 200;
slash.direction = slash.x < 1024 ? 1 : -1;
slash.speed = 24 + j * 2;
attackSlashes.push(slash);
game.addChild(slash);
}.bind(null, i), i * 120);
}
}, 200);
break;
default:
attackDuration = 90 + Math.floor(Math.random() * 30);
break;
}
gameState = STATE.BATTLE_DODGE;
showJoystick(true);
}
// --- Endings ---
function showEnding() {
hideAllMenus();
heart.visible = false;
monster.visible = false;
dialogueBox.visible = false;
monsterHpBarBg.visible = false;
monsterHpBar.visible = false;
monsterHpText.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('joystick_base', {
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('joystick_stick', {
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;
// Adjust joystick Y placement for new arena
var minJoyY = Math.max(baseY + 100, 1050 + 100);
var maxJoyY = Math.min(2732 - joystick.radius - 40, 2550 + 100);
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.STORY && skipTutorialBtn.visible && x > skipTutorialBtn.x - 200 && x < skipTutorialBtn.x + 200 && y > skipTutorialBtn.y - 60 && y < skipTutorialBtn.y + 60) {
// Skip tutorial button pressed
// Fast-forward story to battle
storyStep = storyTexts.length;
showSkipTutorial(false);
nextStory();
return;
} 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
// Make joystick move the heart even faster by increasing the multiplier
var moveSpeed = heart.speed * 2.3; // Increased from 1.7x to 2.3x for even faster movement
var moveX = dx / joystick.radius;
var moveY = dy / joystick.radius;
// Even further expanded arena for maximum space to dodge
var minX = 40,
maxX = 2048 - 40,
minY = 1050,
maxY = 2550;
heart.x = clamp(heart.x + moveX * moveSpeed, minX, maxX);
heart.y = clamp(heart.y + moveY * moveSpeed, minY, maxY);
}
}
};
// --- Main update loop ---
game.update = function () {
// Update monster health bar every frame
updateMonsterHpBar();
// 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;
// Adjust joystick Y placement for new arena
var minJoyY = Math.max(heart.y + 100, 1050 + 100);
var maxJoyY = Math.min(2732 - joystick.radius - 40, 2550 + 100);
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();
// Expanded vertical range for slashes to match even larger arena
slash.y = 1050 + Math.random() * 1500;
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
@@ -418,8 +418,67 @@
monster.x = 2048 / 2;
monster.y = 900;
monster.visible = false;
game.addChild(monster);
+// --- Monster Health Bar ---
+var monsterHpBarBg = LK.getAsset('dialogue_box', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 520,
+ height: 38,
+ x: 2048 / 2,
+ y: 820
+});
+monsterHpBarBg.tint = 0x222222;
+monsterHpBarBg.alpha = 0.7;
+monsterHpBarBg.visible = false;
+game.addChild(monsterHpBarBg);
+var monsterHpBar = LK.getAsset('dialogue_box', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 500,
+ height: 24,
+ x: 2048 / 2,
+ y: 820
+});
+monsterHpBar.tint = 0x00e676;
+monsterHpBar.alpha = 0.95;
+monsterHpBar.visible = false;
+game.addChild(monsterHpBar);
+var monsterHpText = new Text2('', {
+ size: 36,
+ fill: "#fff"
+});
+monsterHpText.anchor.set(0.5, 0.5);
+monsterHpText.x = 2048 / 2;
+monsterHpText.y = 820;
+monsterHpText.visible = false;
+game.addChild(monsterHpText);
+function updateMonsterHpBar() {
+ if (!monster || typeof monster.hp === "undefined" || typeof monster.maxHp === "undefined") {
+ monsterHpBarBg.visible = false;
+ monsterHpBar.visible = false;
+ monsterHpText.visible = false;
+ return;
+ }
+ // Only show during battle
+ if (gameState === STATE.BATTLE_INTRO || gameState === STATE.BATTLE_MENU || gameState === STATE.BATTLE_ACT || gameState === STATE.BATTLE_ATTACK || gameState === STATE.BATTLE_DODGE) {
+ monsterHpBarBg.visible = true;
+ monsterHpBar.visible = true;
+ monsterHpText.visible = true;
+ // Clamp values
+ var hp = Math.max(0, Math.min(monster.hp, monster.maxHp));
+ var maxHp = Math.max(1, monster.maxHp);
+ var percent = hp / maxHp;
+ monsterHpBar.width = 500 * percent;
+ monsterHpBar.x = 2048 / 2 - (1 - percent) * 250;
+ monsterHpText.setText(monster.name + " HP: " + hp + "/" + maxHp);
+ } else {
+ monsterHpBarBg.visible = false;
+ monsterHpBar.visible = false;
+ monsterHpText.visible = false;
+ }
+}
// --- Heart (player soul in battle) ---
heart = new Heart();
heart.x = 2048 / 2;
heart.y = 1800;
@@ -496,8 +555,9 @@
monster.visible = false;
monster.actCount = 0;
monster.mercyAvailable = false;
game.addChild(monster);
+ updateMonsterHpBar();
}, 1200);
}
if (storyStep === 5 && currentMonsterIndex === 1) {
LK.setTimeout(function () {
@@ -655,8 +715,9 @@
monster.visible = false;
monster.actCount = 0;
monster.mercyAvailable = false;
game.addChild(monster);
+ updateMonsterHpBar();
// Show next story segment before next battle
storyStep++;
nextStory();
} else {
@@ -703,8 +764,9 @@
LK.setTimeout(function () {
// Simple attack: always hit for 5
monster.hp -= 5;
if (monster.hp < 0) monster.hp = 0;
+ updateMonsterHpBar();
dialogueBox.setText("You hit " + monster.name + " for 5 damage!");
monster.state = "angry";
monster.nextDialogue();
LK.setTimeout(function () {
@@ -1173,8 +1235,11 @@
hideAllMenus();
heart.visible = false;
monster.visible = false;
dialogueBox.visible = false;
+ monsterHpBarBg.visible = false;
+ monsterHpBar.visible = false;
+ monsterHpText.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?");
@@ -1347,8 +1412,10 @@
}
};
// --- Main update loop ---
game.update = function () {
+ // Update monster health bar every frame
+ updateMonsterHpBar();
// Heart update
if (gameState === STATE.BATTLE_DODGE) {
// Keep joystick locked below the heart every frame
var heartCenterX = heart.x;
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