Java Knight ⚔️
A turn-based, roguelike-inspired RPG that runs entirely in the terminal, written in Java for the fourth Advanced Programming assignment.
For centuries, the land of Javanest lived in peace, until a magical Dragon plunged it into darkness, cursing its people into monsters and scattering the three keys to its lair among them. Battle the cursed creatures, recover the Goblin, Skeleton and Vampire keys, grow strong, and slay the Dragon to break the curse and restore peace to Javanest!
Table of Contents
- How to Compile & Run
- How to Play
- Classes, Enemies & Stats
- Project Structure
- OOP Principles Used
- Bonus Features
How to Compile & Run
The project is a standard Maven project. You need JDK 21+ installed.
Using Maven (recommended)
cd Java-Knight
mvn compile
mvn exec:java -Dexec.mainClass=org.project.Main
Using plain javac / java
cd Java-Knight
# compile every source file into the target/classes folder
find src/main/java -name "*.java" > sources.txt
javac -d target/classes @sources.txt
# run it
java -cp target/classes org.project.Main
The game uses ANSI colour codes, so run it in a real terminal (macOS Terminal, Linux shell, Windows Terminal) for the intended colourful output.
How to Play
-
Name your hero and pick a class (Knight, Wizard, or Assassin).
-
You wander into a random location and a random standard enemy appears. For each encounter you choose:
- 1. Fight – enter turn-based combat.
- 2. Move – skip this enemy and travel somewhere new.
- 3. Visit the Merchant – spend coins on potions, a weapon, or repairs.
- 4. Go to the Castle – only appears once you hold all 3 keys – fight the Dragon.
-
Combat is turn-based. On your turn you pick one of five actions. Only Light Attack is free; the rest cost Mana/Stamina, and the class Special costs the most. If you run out of Mana, you can still Light Attack.
# Action Cost Effect 1 Light Attack 0 Moderate damage 2 Heavy Attack 8 Double damage 3 Defend 6 Blocks ~75% of the next hit 4 Heal 12 Restores 40% of max HP 5 Special 15 Unique class ultimate 6 Use Item – Drink a potion from your inventory -
Winning a fight grants XP (scaled to the enemy's power), coins, and a chance to drop that species' key. Your HP and Mana are then fully restored.
-
Leveling up automatically raises your Max HP, Max Mana and base damage.
-
Collect all 3 keys, go to the Castle, and defeat the Dragon to win. Dying at any point is Game Over.
Classes, Enemies & Stats
Player Classes
Each class has deliberately different starting stats, so they play differently.
| Class | HP | Mana | Base Dmg | Weapon | Special ability |
|---|---|---|---|---|---|
| Knight ⚔️ | 50 | 40 | 8 (highest) | Iron Sword | Shield Bash – heavy damage + stuns the enemy (skips its next turn) |
| Wizard 🧙 | 65 (highest) | 45 | 5 | Oak Staff | Arcane Blast – heavy damage AND heals the caster |
| Assassin 🗡️ | 45 | 55 (highest) | 6 | Twin Dagger | Vanish – dodges the next attack + guarantees a crit on the next strike |
Enemies
| Enemy | HP | Ability | Key |
|---|---|---|---|
| Goblin 👹 | 30 | 40% chance to land a critical hit (double damage) | Goblin Key |
| Skeleton ☠️ | 40 | Resurrects once per battle at 50% HP | Skeleton Key |
| Vampire 🦇 | 48 | Lifesteal – heals for 50% of the damage it deals | Vampire Key |
| Dragon 🐉 | 160 | True damage – fiery breath ignores armor and the Defend stance | — |
Standard enemies each have a 25% chance to drop their key, and only one key per species can ever drop (once you have the Goblin Key, no other Goblin will drop one).
Project Structure
org.project
├── Main.java // entry point – builds the world, starts the game
├── Game.java // the game controller / main loop
├── Merchant.java // bonus shop system
├── entity
│ ├── Entity.java // interface every combatant implements
│ ├── players
│ │ ├── ICombatActions.java // interface: the 5 required actions
│ │ ├── Player.java // abstract base for all heroes
│ │ ├── Knight.java / Wizard.java / Assassin.java
│ └── enemies
│ ├── Enemy.java // abstract base for all monsters
│ ├── KeyType.java // enum of the three keys
│ ├── Goblin.java / Skeleton.java / Vampire.java / Dragon.java
├── item
│ ├── Item.java // interface for anything ownable/sellable
│ ├── weapons (Weapon abstract → Sword, Dagger, Staff, GreatAxe)
│ ├── armors (Armor abstract → KnightArmor, LeatherArmor, Robe)
│ └── consumables (Consumable abstract → Flask, ManaPotion)
├── location
│ └── Location.java // a place that spawns random enemies
└── util
├── ConsoleColors.java // ANSI colour helpers
└── Dice.java // random-number helpers
OOP Principles Used
This project was designed around the seven principles the assignment asks for:
-
Encapsulation – all fields are
private/protectedwith getters. HP, Mana and XP can only be changed through methods liketakeDamage,healandgainXP, which enforce their own rules (clamping to max, capping at zero, leveling up). -
Inheritance – shared logic lives in abstract base classes so it isn't duplicated.
Playerholds the HP/Mana bars, leveling, inventory and four of the five actions;Knight,WizardandAssassinonly add what's unique. Same idea forEnemy→ its four monster subclasses, and forWeapon/Armor/Consumable→ their concrete items. -
Interfaces –
Entityis the contract every combatant obeys, which is what lets the combat loop treat a Knight and a Dragon identically.ICombatActionsguarantees every player class implements exactly the five required actions.Itemunifies weapons, armor and consumables so the merchant can sell any of them. -
Abstract classes –
Player,Enemy,Weapon,ArmorandConsumableare all abstract: they provide shared state/behaviour but can't be instantiated on their own, forcing subclasses to fill in the specifics (e.g.Player.specialAbilityis abstract). -
Polymorphism – the whole game loop is polymorphic.
Gameonly ever callsPlayer/Enemy/Entitymethods; the correctspecialAbility,attackortakeDamageruns depending on the real object. Each enemy overridesattackto add its signature move. -
Overriding – subclasses override base behaviour, e.g.
SkeletonoverridestakeDamageto resurrect,Vampire/Goblin/Dragonoverrideattack, and every player class overridesspecialAbility. -
Overloading –
takeDamage(int)vstakeDamage(int, boolean)inPlayer. The second, overloaded version ignores defenses and is how the Dragon's breath bypasses armor and shields. TheSwordalso overloads its constructor.
Bonus Features
- Coins & Merchant system – enemies drop coins; the Merchant sells Health Flasks, Mana Potions, a stronger Great Axe, and can repair armor.
- Inventory & consumables – potions can be bought, stored and used mid-combat (action 6).
- Durable armor – armor loses durability as it absorbs hits and eventually breaks, until repaired at the merchant.
- Colourful narrative console – ANSI colours (red for damage, blue for mana, green for healing) make the combat log easy to read.
Born of God and Void. You shall seal the blinding light that plagues their dreams. You are the Vessel. You are the Java Knight.