My Little Mortal Combat (MLMC) is a browser-based single-player arena fighter. It blends My Little Pony flavor with rock-paper-scissors combat and light RPG progression. The stack is vanilla HTML/CSS plus Alpine.js, with game state saved to localStorage.
| Aspect | Detail |
|---|---|
| Genre | Turn-based RPS fighter + RPG progression |
| Platform | Static web app (no backend) |
| Save system | One slot in localStorage (pcPony) |
| Tagline | "Friendship is fatal" |
The game loop is: name your pony → train skills → pick an opponent → fight → earn gold/XP → repeat.
- Intro — Create a pony (name required) or load a saved game.
- Main (Arena) — View stats, train skills, choose 1 of 3 opponents, start a fight.
- Battle — Turn-based RPS until one side reaches 0 HP.
There is no path back to the intro screen after entering the arena.
Intro (name / continue)
↓
Arena Dashboard (train + pick foe)
↓
Battle (RPS rounds)
↓
Result dialog → back to Arena
Each pony tracks:
| Stat | Starting value | Notes |
|---|---|---|
| HP | 50 | Scales with level: level × 50 |
| Attack / Defend / Vogue | 1 each | Trainable with gold |
| Gold | 0 | Earned from fights |
| XP | 0 | Same amount as gold earned; drives level |
Level is derived from XP, not stored directly:
function xpToLevel(xp) {
if (xp <= 0) return 1;
return Math.floor(Math.pow(xp / 100, 1 / 1.5)) + 1;
}Approximate level thresholds:
| Level | XP needed (approx.) |
|---|---|
| 1 | 0 |
| 2 | 100 |
| 3 | ~283 |
| 4 | ~520 |
| 5 | ~800 |
After each fight (win or lose), HP is fully restored to level × 50 before returning to the arena.
The three moves form a cycle (shown in the battle UI):
| Your move | Beats | Loses to |
|---|---|---|
| Attack | Vogue | Defend |
| Defend | Attack | Vogue |
| Vogue (Strike a Pose) | Defend | Attack |
| Same move | — | Draw (no damage) |
Each round:
- Player picks Attack, Defend, or Vogue.
- The NPC picks randomly (equal chance among all three).
- Outcome is resolved via the RPS table.
- Only the winner deals damage — draws deal none.
let pcdmg = 10 * this.pcPony[type];
let npcdmg = 10 * this.selectedOpponent[npcMove];Damage = 10 × skill level of the move used. Example: Attack 3 → 30 damage on a winning exchange.
Because the NPC move is random, higher skills help when you win, but do not change RPS outcomes.
Three challengers are generated whenever you enter the arena or finish a fight.
let level = Math.max(1, this.level + getRandomIntInclusive(-1, 1))
let opp = {
name: name + ' ' + getRandomArr(suffixes),
level,
currentHp: HP_PER_LEVEL * level,
hp: HP_PER_LEVEL * level,
hpPercentage: 100,
bio,
attack: Math.max(1, getRandomIntInclusive(level-2, level+2)),
defend: Math.max(1, getRandomIntInclusive(level-2, level+2)),
vogue: Math.max(1, getRandomIntInclusive(level-2, level+2)),
}| Property | How it's set |
|---|---|
| Name | Random MLP-style name + violent suffix (e.g. "Rainbow Dash the Mane Ripper") |
| Bio | Three random passive-aggressive flavor lines |
| Level | Player level ± 1 (minimum 1) |
| HP | level × 50 |
| Skills | Each skill random in [level−2, level+2], minimum 1 |
The readme says opponent level should be ±2 around the player level:
* ~~Make opponents have a level -2 to +2 around player level (min 1)~~The code uses ±1 for level (line 70 in script.js):
/*
my level is -1 to +1 of my current level
*/
let level = Math.max(1, this.level + getRandomIntInclusive(-1, 1))The -2 to +2 range on lines 78–80 applies to attack/defend/vogue stats, not opponent level. Either line 70 should be getRandomIntInclusive(-2, 2), or the readme is stale (the inline comment explicitly documents ±1).
| Outcome | Gold & XP |
|---|---|
| Victory | opponent.level × 100 |
| Defeat | 10% of that (floor(level × 10)) |
Gold and XP are always equal — the readme notes this may be tweaked later.
train(skill) {
if (this.pcPony.gold < this.pcPony[skill] * 10) {
alert('Not enough gold to train!');
return;
}
this.pcPony.gold -= this.pcPony[skill] * 10;
this.pcPony[skill]++;
this.persist();
}| Skill level | Training cost |
|---|---|
| 1 → 2 | 10 gold |
| 2 → 3 | 20 gold |
| 3 → 4 | 30 gold |
| n → n+1 | n × 10 gold |
Training is the main gold sink and the primary way to increase damage output.
- Key:
localStorage→pcPony - When saved: After training and after each fight ends
- Single slot: Starting a new game with an existing save prompts for confirmation
- Continue: Intro shows saved pony name and level
The readme describes saves as "hackable as hell" — stats can be edited directly in browser dev tools.
- Names (
names.js): ~50 MLP character names - Suffixes (
suffixes.js): Dark/violent titles ("the Dream Crusher", "Hoof Smasher", etc.) - Bios (
bios.js): Humorous petty-evil traits ("double-dips at every dip", "replies 'k.' to paragraphs")
Bios are flavor only and do not affect combat.
- Simple, readable loop: fight → earn → train → fight harder opponents
- RPS is easy to learn; random NPC moves add variance
- Opponent scaling keeps fights relevant as you level
- Full HP restore between fights avoids death-spiral attrition
- No NPC intelligence — Pure random moves; no adaptation or level-based behavior
- Skill investment is one-dimensional — Training any skill helps only when that move wins
- XP = gold — No separate progression tuning
- Defeat still pays — 10% consolation reward reduces loss penalty
- Draws are common — ~33% of exchanges (when moves match); no damage, longer fights
- New game edge case — Starting fresh after a page load resets stats, but the flow doesn't explicitly reset
pcPonyif state was mutated in-session (minor; no return-to-intro path exists)
The main outstanding item is Cutie Marks — not implemented yet. Core systems (XP, leveling, scaled opponents, damage formula, local save) are marked complete.
MLMC is a lightweight RPG arena game built around a three-move RPS system. Progression comes from gold-funded skill training and XP-driven leveling, which increases HP and opponent difficulty. Combat is deterministic given move choices, with randomness only from the NPC's move selection. The MLP parody theme (cute ponies, violent suffixes, passive-aggressive bios) is the main personality layer on top of straightforward mechanics.