โ˜• My First Java Game ยท Episode 6 of 7 ยท See All Episodes
๐Ÿฐ Episode 6 ยท Big Project ยท RPG Part 2 of 3

RPG Quest
Part 2: Monsters & Combat

Your world gets dangerous. Add a Monster class, roaming enemies, and a full turn-based battle system, attack, defend, flee, plus XP and level-ups when you win.

๐Ÿ‘ถ Ages 11+ โฑ๏ธ ~2 Hours โ˜• Java โœ“ Free
๐Ÿ‘น Monster class ๐ŸŽฒ Random encounters โš”๏ธ Turn-based combat ๐Ÿ›ก๏ธ Defend action โœจ XP & level-ups ๐ŸŽฌ Battle state
โญ
0 XP
Level 1
๐Ÿ”ฅ0
Your Progress 0 / 6 steps
1
๐Ÿ‘น
The Monster Class
Reuse the Hero pattern for goblins, wolves and slimes
Active
๐ŸŽฏ
Goal for this step

Create a Monster class and a factory method that builds different enemy types.

  • 1Continue in your Part 1 project, everything builds on it.
  • 2Monster looks like a mini Hero: name, hp, maxHp, attack, plus xpReward and goldReward.
  • 3Add a static Monster create(String type) factory: "slime" is weak, "wolf" is medium, "goblin" hits hard.
  • 4Notice how similar Monster and Hero are, spotting shared patterns is a real developer skill (Part 3 will exploit it).
Monster.java
public class Monster {
    String name;
    int hp, maxHp, attack, xpReward, goldReward;

    public Monster(String name, int hp, int attack, int xp, int gold) {
        this.name = name; this.maxHp = hp; this.hp = hp;
        this.attack = attack; this.xpReward = xp; this.goldReward = gold;
    }

    public static Monster create(String type) {
        switch (type) {
            case "slime":  return new Monster("Slime", 12, 3, 8, 5);
            case "wolf":   return new Monster("Wolf", 20, 5, 15, 12);
            case "goblin": return new Monster("Goblin", 28, 7, 25, 20);
        }
        return create("slime");
    }

    public void takeDamage(int amount) { hp = Math.max(0, hp - amount); }
    public boolean isAlive() { return hp > 0; }
}
โœ๏ธ
Fill in the Blanks
+15 XP
Monster.create("wolf") is a factory method, we call it on the class itself without using outside. Each monster carries an xpReward and a for when it is defeated.
๐Ÿง 
Knowledge Check
+15 XP
What is handy about the create(type) factory pattern?
AIt is the only way to make objects
BAll monster stats live in ONE place, balancing the game means editing one method
CStatic methods run faster
2
๐ŸŽฒ
Random Encounters
Each step in the wild might start a battle
Locked
๐ŸŽฏ
Goal for this step

Trigger battles as the hero explores, with smart odds that feel fair.

  • 1Add game state to GamePanel: String state = "EXPLORE"; and a Monster foe; field.
  • 2After every successful step onto grass, roll the dice: Math.random() < 0.15 โ†’ 15% encounter chance.
  • 3On an encounter: pick a random monster type (slimes common, goblins rare), set state = "BATTLE".
  • 4While state is "BATTLE", arrow keys must NOT move the hero, the state check does that.
  • 5Fairness trick: guarantee 3 safe steps after each battle with a small safeSteps counter.
GamePanel.java
    String state = "EXPLORE";
    Monster foe;
    int safeSteps = 0;

    // after a successful move in keyPressed:
    if (state.equals("EXPLORE") && map[heroRow][heroCol] == 0) {
        if (safeSteps > 0) safeSteps--;
        else if (Math.random() < 0.15) {
            double roll = Math.random();
            String type = roll < 0.5 ? "slime"
                        : roll < 0.85 ? "wolf" : "goblin";
            foe = Monster.create(type);
            state = "BATTLE";
        }
    }
โŒจ๏ธ
Code Challenge
+20 XP
Set the encounter odds: 50% slime, 35% wolf, 15% goblin.
GamePanel.java
String type = roll <  ? "slime"
            : roll <  ? "wolf" : "goblin";
๐Ÿ’ก Hint: roll is between 0 and 1. Slime claims the first half; wolf claims up to 0.85 (0.5 + 0.35); goblin gets the rest.
๐Ÿง 
Knowledge Check
+15 XP
Why guarantee a few safe steps after each battle?
AMonsters need time to respawn
BBack-to-back random battles feel unfair and exhausting, pacing is part of game design
CJava random needs to reset
3
โš”๏ธ
The Battle Screen
Draw the combat UI: two fighters, HP bars, action menu
Locked
๐ŸŽฏ
Goal for this step

When a battle starts, replace the map with a proper battle screen.

  • 1In paintComponent: if state is "BATTLE", draw the battle screen INSTEAD of the map, dark backdrop, hero bottom-left, monster top-right.
  • 2Both fighters get name + HP bar (you wrote that bar code in Part 1, reuse it!).
  • 3Action menu along the bottom: [A]ttack   [D]efend   [P]otion   [F]lee.
  • 4Add a String battleLog, one line describing what just happened ("Wolf bites you for 5!"). Draw it centre-screen.
  • 5No logic yet, just the visuals, next step brings it to life.
GamePanel.java (paintComponent)
    if (state.equals("BATTLE")) {
        g.setColor(new Color(20, 12, 28));
        g.fillRect(0, 0, 700, 560);

        g.setColor(Color.WHITE);
        g.setFont(new Font("Arial", Font.BOLD, 22));
        g.drawString(hero.getName(), 60, 380);
        g.drawString(foe.name, 470, 140);

        drawBar(g, 60, 392, hero.getHp(), hero.getMaxHp());
        drawBar(g, 470, 152, foe.hp, foe.maxHp);

        g.drawString(battleLog, 60, 280);
        g.drawString("[A]ttack   [D]efend   [P]otion   [F]lee", 60, 500);
        return;   // skip drawing the map
    }

    void drawBar(Graphics g, int x, int y, int val, int max) {
        g.setColor(Color.RED);           g.fillRect(x, y, 150, 14);
        g.setColor(new Color(6,214,160)); g.fillRect(x, y, 150 * val / max, 14);
    }
โœ๏ธ
Fill in the Blanks
+15 XP
We reused the HP bar by pulling it into a method called . The early stops the map being drawn under the battle screen.
๐Ÿง 
Knowledge Check
+15 XP
The battle screen and map are both drawn by paintComponent. What chooses between them?
AThe Timer speed
BThe state variable, one screen per state, checked at the top
CThe window size
4
๐Ÿ›ก๏ธ
Turn Logic: Attack, Defend, Flee
You act, then the monster acts, the full turn cycle
Locked
๐ŸŽฏ
Goal for this step

Implement the complete turn: player action, then the monster hits back.

  • 1In keyPressed, when state is "BATTLE": A attacks (damage = hero attack ยฑ a little randomness), D defends, P uses a potion, F tries to flee (60% chance).
  • 2Damage variance keeps it spicy: hero.getAttack() + (int)(Math.random()*4) - 1 โ†’ attack-1 up to attack+2.
  • 3After YOUR action, if the foe still lives, it strikes back, defending halves that hit.
  • 4Update battleLog after both actions so the player can follow the fight.
  • 5If hero HP hits 0 โ†’ state = "GAME_OVER". Fleeing successfully โ†’ back to "EXPLORE" with safeSteps = 3.
GamePanel.java
    boolean defending = false;

    void battleAction(char key) {
        defending = false;
        if (key == 'a') {
            int dmg = hero.getAttack() + (int)(Math.random()*4) - 1;
            foe.takeDamage(dmg);
            battleLog = "You hit " + foe.name + " for " + dmg + "!";
        } else if (key == 'd') {
            defending = true;
            battleLog = "You brace behind your shield.";
        } else if (key == 'p') {
            battleLog = hero.useItem("Potion") ? "Glug! Potion heals 12." : "No potions left!";
        } else if (key == 'f') {
            if (Math.random() < 0.6) { state = "EXPLORE"; safeSteps = 3;
                battleLog = ""; return; }
            battleLog = "Could not escape!";
        } else return;

        if (foe.isAlive()) {
            int dmg = foe.attack + (int)(Math.random()*3);
            if (defending) dmg /= 2;
            hero.takeDamage(dmg);
            battleLog += "  " + foe.name + " hits for " + dmg + "!";
            if (!hero.isAlive()) state = "GAME_OVER";
        }
    }
โŒจ๏ธ
Code Challenge
+20 XP
Make defending halve the monsterโ€™s counter-attack:
GamePanel.java
int dmg = foe.attack + (int)(Math.random()*3);
if () dmg  2;
hero.takeDamage(dmg);
๐Ÿ’ก Hint: Check the boolean set by the D action; integer divide-and-assign halves the value.
๐Ÿง 
Knowledge Check
+15 XP
Why does the monster only counter-attack if (foe.isAlive())?
ADead monsters hitting you back would be absurd, always check state after dealing damage
BIt saves memory
CisAlive() heals the monster
5
โœจ
Victory, XP & Level-Ups
Win the fight, claim rewards, grow stronger
Locked
๐ŸŽฏ
Goal for this step

Reward victory with XP and gold, and level the hero up at thresholds.

  • 1After your attack, if !foe.isAlive(): victory! Add foe.xpReward and foe.goldReward to the hero, log it, return to EXPLORE.
  • 2Give Hero a gainXp(int) method: add XP, and while XP โ‰ฅ level*25, subtract that cost, level++, maxHp += 6, attack += 2, and refill HP.
  • 3A full heal on level-up feels GREAT, reward the grind.
  • 4Show level and XP progress in the HUD: Lv 3 ยท XP 12/75.
  • 5Balance check: a slime should take ~3 hits at level 1 and 1-2 at level 3. Feel the power curve!
Hero.java
    public void gainXp(int amount) {
        xp += amount;
        while (xp >= level * 25) {
            xp -= level * 25;
            level++;
            maxHp += 6;
            attack += 2;
            hp = maxHp;          // level-up full heal!
        }
    }
    public int getLevel() { return level; }
    public void addGold(int g) { gold += g; }
โœ๏ธ
Fill in the Blanks
+15 XP
Levelling costs level ร— XP. On level-up the hero gains 6 max HP, attack, and is healed to .
๐Ÿง 
Knowledge Check
+15 XP
Why is gainXp a while loop, not an if?
AWhile loops are safer
BA huge XP reward (like a boss) might grant several level-ups at once
Cif statements cannot change fields
6
๐Ÿ’€
Defeat, Respawn & Balance Pass
Game over screen, respawning, and tuning the numbers
Locked
๐ŸŽฏ
Goal for this step

Handle losing gracefully and tune combat until it feels fair but tense.

  • 1GAME_OVER state: dark screen, "YOU FELLโ€ฆ", stats reached, and [R] Try Again.
  • 2On R: respawn at the start tile with half max HP and half gold (a sting, not a wipe) and state = "EXPLORE".
  • 3Balance testing is a real job, check: Can you survive 3 slimes in a row at level 1? Does a goblin feel scary but beatable at level 2?
  • 4Tune: encounter rate (0.15), potion power (12), defend halving, flee odds (0.6). Small numbers, huge feel changes.
  • 5Part 2 done! Your world is alive and dangerous. Part 3: NPCs, a quest, and the Dragon boss finale.
GamePanel.java
    if (state.equals("GAME_OVER") && e.getKeyChar() == 'r') {
        heroRow = 1; heroCol = 1;
        hero.heal(hero.getMaxHp() / 2);      // half HP respawn
        state = "EXPLORE";
        safeSteps = 5;
    }
๐Ÿ’ก
Losing should cost something, but never everything. Players quit games that wipe them, and quit games with no danger. Sit in between.
โŒจ๏ธ
Code Challenge
+20 XP
Give the fallen hero a fair respawn:
GamePanel.java
heroRow = 1; heroCol = ;             // back to the start
hero.heal(hero.getMaxHp() / );       // half health
safeSteps = ;                        // breathing room
๐Ÿ’ก Hint: Start tile was (1,1). Respawn at half of max HP, with five guaranteed-safe steps.
๐Ÿง 
Knowledge Check
+15 XP
Which is the best-designed death penalty for this game?
ADelete the save file
BRespawn at start with half HP/gold, losing stings but you keep your levels and continue
CNothing happens at all
๐ŸŽ‰๐Ÿ†๐ŸŽฎโœจ๐ŸŽ‰
Workshop Complete!
Turn-based combat, XP and level-ups, the beating heart of every RPG, built by you. Part 3 finishes the quest with bosses and the final goal!
0
Total XP
1
Level
0
Best Streak
0%
Accuracy
โ–ถ Part 3: Quests & The Boss โ†’
โญ View My Progress & Certificates

This workshop was free and took many hours to build. If it helped you learn something new, consider supporting the project.

☕ Support on Ko-fi