Unlike real-time action games, Undertale fights are turn-based but with a twist:

let keys = ArrowUp: false, ArrowDown: false... ; function updateSoul() if (keys.ArrowUp) soul.y -= 5; // Add boundaries

let dialogQueue = [];

function pressMercy() if (mercyState === "MERCY") mercyState = "SPARE"; showText("* Spare - Show mercy."); else if (mercyState === "SPARE") if (boss.spareProgress >= 100) endBattle("SPARED"); else showText("* You can't spare yet. Keep acting."); mercyState = "MERCY";

Whether you are coding in GML for a fangame, Lua for a CYF mod, or Python for a fan remake, remember Toby Fox’s golden rule:

// In enemy take-damage script if (attacking_sans) if (random(100) < 90) // 90% dodge rate damage = 0; show_text("* miss");

# Define Sans's phases sans_phases = [ 'name': 'Phase 1', 'attacks': [sans_attacks[0], sans_attacks[1]], 'name': 'Phase 2', 'attacks': [sans_attacks[1], sans_attacks[2]] ]

def defend(self): self.defense = random.randint(1, 10) return self.defense

Once you understand the basics, you can start modifying existing scripts or creating entirely new bosses.

Toriel starts the fight by trying to guide you. When you continue to act, her dialogue shifts to pleading.

Undyne's Emotional Resonance: [Agitated]

Undertale Boss Battles Script: A Guide to the Most Iconic Encounters

import pygame