Java Knight

This commit is contained in:
2026-07-12 19:20:33 +03:30
parent 78d4bedb08
commit a527c5bcfa
67 changed files with 1742 additions and 254 deletions
+2 -2
View File
@@ -9,8 +9,8 @@
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@@ -0,0 +1,345 @@
package org.project;
import org.project.entity.enemies.Dragon;
import org.project.entity.enemies.Enemy;
import org.project.entity.enemies.KeyType;
import org.project.entity.players.Assassin;
import org.project.entity.players.Knight;
import org.project.entity.players.Player;
import org.project.entity.players.Wizard;
import org.project.item.consumables.Consumable;
import org.project.location.Location;
import org.project.util.ConsoleColors;
import org.project.util.Dice;
import java.util.List;
import java.util.Scanner;
/**
* The main game controller. It owns the player, the world's locations and the
* merchant, and drives the whole flow: class selection, the exploration menu,
* turn-based combat, progression, and the final Dragon fight.
*
* All the polymorphism set up in the class hierarchy pays off here — the combat
* loop only ever calls methods on {@code Player} and {@code Enemy}, and each
* concrete class fills in its own behaviour.
*/
public class Game {
private final Scanner scanner = new Scanner(System.in);
private final List<Location> locations;
private final Merchant merchant = new Merchant(scanner);
private Player player;
private boolean running = true;
public Game(List<Location> locations) {
this.locations = locations;
}
// ------------------------------------------------------------------
// Top-level flow
// ------------------------------------------------------------------
public void start() {
printPrologue();
chooseClass();
explorationLoop();
}
private void printPrologue() {
System.out.println(ConsoleColors.bold(ConsoleColors.CYAN + "\n=== JAVA KNIGHT ===" + ConsoleColors.RESET));
System.out.println(ConsoleColors.cyan(
"For centuries, the land of Javanest lived in peace, until a magical Dragon\n"
+ "plunged it into darkness, cursing its people into monsters. It split the three\n"
+ "keys to its lair among them. Collect all three keys, grow strong, and slay the\n"
+ "Dragon to break the curse!\n"));
}
private void chooseClass() {
System.out.print("Name your hero: ");
String name = scanner.hasNextLine() ? scanner.nextLine().trim() : "Hero";
if (name.isEmpty()) {
name = "Hero";
}
System.out.println("\nChoose your class:");
System.out.println("1. " + ConsoleColors.cyan("Knight")
+ " ⚔️ Highest base damage, sturdy armor. Special: Shield Bash (stun).");
System.out.println("2. " + ConsoleColors.purple("Wizard")
+ " 🧙 Highest HP. Special: Arcane Blast (damage + self-heal).");
System.out.println("3. " + ConsoleColors.yellow("Assassin")
+ " 🗡️ Highest mana. Special: Vanish (dodge + guaranteed crit).");
int choice = readChoice(1, 3);
player = switch (choice) {
case 1 -> new Knight(name);
case 2 -> new Wizard(name);
default -> new Assassin(name);
};
System.out.println(ConsoleColors.green("\nWelcome, " + player.getName() + " the " + player.getClassName() + "!"));
}
// ------------------------------------------------------------------
// Exploration menu
// ------------------------------------------------------------------
private void explorationLoop() {
while (running && player.isAlive()) {
Location location = locations.get(Dice.between(0, locations.size() - 1));
Enemy enemy = location.spawnEnemy();
System.out.println(ConsoleColors.bold("\n────────────────────────────────────────"));
System.out.println("You arrive at " + ConsoleColors.cyan(location.getName())
+ ". A wild " + ConsoleColors.red(enemy.getName()) + " appears!");
printKeyProgress();
// Decide what to do about this particular enemy.
boolean encounterOngoing = true;
while (encounterOngoing && running && player.isAlive()) {
System.out.println("\nWhat will you do?");
System.out.println("1. Fight the " + enemy.getName());
System.out.println("2. Move to another location");
System.out.println("3. Visit the Merchant");
if (player.hasAllKeys()) {
System.out.println("4. " + ConsoleColors.bold("Go to the Castle to face the Dragon 🐉"));
}
int max = player.hasAllKeys() ? 4 : 3;
int choice = readChoice(1, max);
switch (choice) {
case 1 -> {
boolean won = combat(enemy);
if (!won) {
gameOver();
return;
}
afterVictory(enemy);
encounterOngoing = false; // move on to a new encounter
}
case 2 -> {
System.out.println("You slip away to find another path...");
encounterOngoing = false;
}
case 3 -> merchant.visit(player);
case 4 -> {
castleFight();
return;
}
}
}
}
}
// ------------------------------------------------------------------
// Combat
// ------------------------------------------------------------------
/** Runs a full turn-based battle. Returns true if the player survives. */
private boolean combat(Enemy enemy) {
System.out.println(ConsoleColors.bold("\nYou chose to FIGHT!"));
while (player.isAlive() && enemy.isAlive()) {
printCombatStatus(enemy);
playerTurn(enemy);
if (enemy.isAlive()) {
enemyTurn(enemy);
}
}
return player.isAlive();
}
private void printCombatStatus(Enemy enemy) {
System.out.println();
System.out.println(ConsoleColors.green("[" + player.getName() + " (" + player.getClassName() + ") - "
+ player.getHp() + "/" + player.getMaxHP() + " HP | "
+ player.getMp() + "/" + player.getMaxMP() + " Mana] Lv." + player.getLevel()));
System.out.println(ConsoleColors.red("[" + enemy.getName() + " - "
+ enemy.getHp() + "/" + enemy.getMaxHP() + " HP]"));
System.out.println("---");
}
/** Handle exactly one player action (loops until a valid one is chosen). */
private void playerTurn(Enemy enemy) {
System.out.println(ConsoleColors.bold("\nYour Turn:"));
System.out.println("1. Light Attack | 2. Heavy Attack " + ConsoleColors.blue("(-" + Player.HEAVY_COST + " Mana)")
+ " | 3. Defend " + ConsoleColors.blue("(-" + Player.DEFEND_COST + " Mana)")
+ " | 4. Heal " + ConsoleColors.blue("(-" + Player.HEAL_COST + " Mana)")
+ " | 5. " + player.getSpecialName() + " " + ConsoleColors.blue("(-" + Player.SPECIAL_COST + " Mana)")
+ " | 6. Use Item");
while (true) {
int choice = readChoice(1, 6);
switch (choice) {
case 1 -> {
player.lightAttack(enemy);
return;
}
case 2 -> {
if (requireMana(Player.HEAVY_COST)) {
player.heavyAttack(enemy);
return;
}
}
case 3 -> {
if (requireMana(Player.DEFEND_COST)) {
player.defend();
return;
}
}
case 4 -> {
if (requireMana(Player.HEAL_COST)) {
player.healAction();
return;
}
}
case 5 -> {
if (requireMana(Player.SPECIAL_COST)) {
player.specialAbility(enemy);
return;
}
}
case 6 -> {
if (useItemMenu()) {
return;
}
}
}
}
}
private boolean requireMana(int cost) {
if (player.canAfford(cost)) {
return true;
}
System.out.println(ConsoleColors.blue("Not enough Mana! Pick another action."));
return false;
}
/**
* Show the inventory and use a chosen consumable. Returns true if an item was
* used (which consumes the turn), false if the player had nothing / cancelled.
*/
private boolean useItemMenu() {
List<Consumable> inv = player.getInventory();
if (inv.isEmpty()) {
System.out.println("Your inventory is empty!");
return false;
}
System.out.println("Choose an item to use (0 to cancel):");
for (int i = 0; i < inv.size(); i++) {
System.out.println((i + 1) + ". " + inv.get(i).getName());
}
int choice = readChoice(0, inv.size());
if (choice == 0) {
return false;
}
player.useConsumable(choice - 1);
return true;
}
private void enemyTurn(Enemy enemy) {
System.out.println(ConsoleColors.bold("\n" + enemy.getName() + "'s Turn:"));
if (enemy.isStunned()) {
enemy.clearStun();
System.out.println(ConsoleColors.yellow("😵 " + enemy.getName() + " is stunned and skips its turn!"));
return;
}
enemy.attack(player);
}
// ------------------------------------------------------------------
// Post-combat rewards & progression
// ------------------------------------------------------------------
private void afterVictory(Enemy enemy) {
System.out.println(ConsoleColors.bold(ConsoleColors.GREEN
+ "\n🏆 The " + enemy.getName() + " is defeated!" + ConsoleColors.RESET));
player.gainXP(enemy.getXpReward());
int coins = Math.max(0, Dice.between(enemy.getCoinReward() - 4, enemy.getCoinReward() + 4));
player.addCoins(coins);
System.out.println(ConsoleColors.yellow("💰 Found " + coins + " coins (total: " + player.getCoins() + ")."));
tryKeyDrop(enemy);
player.restoreAfterBattle();
System.out.println(ConsoleColors.green("You catch your breath — HP and Mana fully restored."));
}
/** Roll for the enemy's unique key, respecting the one-key-per-species rule. */
private void tryKeyDrop(Enemy enemy) {
KeyType key = enemy.getKeyType();
if (key == null || player.hasKey(key)) {
return; // Dragon has no key, or we already own this one
}
if (Dice.chance(enemy.getKeyDropChance())) {
player.addKey(key);
System.out.println(ConsoleColors.bold(ConsoleColors.PURPLE
+ "🗝️ The " + enemy.getName() + " dropped the " + key.getDisplayName() + "!" + ConsoleColors.RESET));
printKeyProgress();
}
}
private void printKeyProgress() {
StringBuilder sb = new StringBuilder("Keys: ");
for (KeyType key : KeyType.values()) {
sb.append(player.hasKey(key) ? ConsoleColors.green("[" + key.getDisplayName() + "] ")
: ConsoleColors.red("[???] "));
}
System.out.println(sb.toString().trim());
}
// ------------------------------------------------------------------
// Endgame
// ------------------------------------------------------------------
private void castleFight() {
System.out.println(ConsoleColors.bold(ConsoleColors.RED
+ "\nYou march to the Castle. The ground trembles as the Dragon awakens...\n" + ConsoleColors.RESET));
Dragon dragon = new Dragon();
boolean won = combat(dragon);
if (won) {
System.out.println(ConsoleColors.bold(ConsoleColors.GREEN
+ "\n🎉 VICTORY! The Dragon falls, the curse breaks, and peace returns to Javanest!"
+ ConsoleColors.RESET));
System.out.println(ConsoleColors.green("You are the Java Knight. 🛡️"));
} else {
gameOver();
}
running = false;
}
private void gameOver() {
System.out.println(ConsoleColors.bold(ConsoleColors.RED
+ "\n💀 GAME OVER — " + player.getName() + " has fallen. Darkness consumes Javanest..."
+ ConsoleColors.RESET));
running = false;
}
// ------------------------------------------------------------------
// Input helper
// ------------------------------------------------------------------
/** Read an integer choice in [min, max], re-prompting on bad input. */
private int readChoice(int min, int max) {
while (true) {
System.out.print(ConsoleColors.bold("> "));
if (!scanner.hasNextLine()) {
return min; // no more input: pick the safe default so we don't loop forever
}
try {
int value = Integer.parseInt(scanner.nextLine().trim());
if (value >= min && value <= max) {
return value;
}
} catch (NumberFormatException ignored) {
// fall through to re-prompt
}
System.out.println("Please enter a number between " + min + " and " + max + ".");
}
}
}
@@ -5,11 +5,19 @@ import org.project.location.Location;
import java.util.ArrayList;
import java.util.List;
/**
* Entry point. Builds the world (a handful of locations) and starts the game.
*/
public class Main {
public static void main(String[] args) {
// TODO: ADD LOCATIONS TO YOUR GAME
List<Location> locations = new ArrayList<>();
locations.add(new Location("Whispering Woods"));
locations.add(new Location("Forgotten Crypt"));
locations.add(new Location("Bleak Marsh"));
locations.add(new Location("Ruined Village"));
locations.add(new Location("Frostpeak Pass"));
// TODO: IMPLEMENT GAMEPLAY
Game game = new Game(locations);
game.start();
}
}
}
@@ -0,0 +1,100 @@
package org.project;
import org.project.entity.players.Player;
import org.project.item.consumables.Flask;
import org.project.item.consumables.ManaPotion;
import org.project.item.weapons.GreatAxe;
import org.project.item.weapons.Weapon;
import org.project.util.ConsoleColors;
import java.util.Scanner;
/**
* Bonus feature: a shop where the player spends coins earned in battle on
* consumables, a stronger weapon, or armor repairs.
*/
public class Merchant {
private static final int REPAIR_COST = 15;
private final Scanner scanner;
public Merchant(Scanner scanner) {
this.scanner = scanner;
}
public void visit(Player player) {
System.out.println(ConsoleColors.yellow("\n🧙 Merchant: \"Welcome, traveler. Coin buys survival out here.\""));
boolean shopping = true;
while (shopping) {
System.out.println(ConsoleColors.bold("\n--- Merchant's Wares ---"));
System.out.println("You have " + ConsoleColors.yellow(player.getCoins() + " coins") + ".");
System.out.println("1. Health Flask (heals 25 HP) - 20 coins");
System.out.println("2. Mana Potion (restores 20 Mana) - 20 coins");
System.out.println("3. Great Axe (12 damage weapon) - 80 coins");
System.out.println("4. Repair Armor - " + REPAIR_COST + " coins");
System.out.println("5. Leave");
int choice = readChoice(1, 5);
switch (choice) {
case 1 -> buyConsumable(player, new Flask(), 20);
case 2 -> buyConsumable(player, new ManaPotion(), 20);
case 3 -> buyWeapon(player, new GreatAxe());
case 4 -> repairArmor(player);
case 5 -> shopping = false;
}
}
System.out.println(ConsoleColors.yellow("🧙 Merchant: \"Safe travels, hero.\"\n"));
}
private void buyConsumable(Player player, org.project.item.consumables.Consumable item, int price) {
if (player.spendCoins(price)) {
player.addConsumable(item);
System.out.println(ConsoleColors.green("Bought a " + item.getName() + "! Added to your inventory."));
} else {
notEnough();
}
}
private void buyWeapon(Player player, Weapon weapon) {
if (player.spendCoins(weapon.getPrice())) {
player.setWeapon(weapon);
System.out.println(ConsoleColors.green("You equip the " + weapon.getName()
+ " (" + weapon.getDamage() + " damage)!"));
} else {
notEnough();
}
}
private void repairArmor(Player player) {
if (player.spendCoins(REPAIR_COST)) {
player.getArmor().repair();
System.out.println(ConsoleColors.green("Your " + player.getArmor().getName() + " is fully repaired."));
} else {
notEnough();
}
}
private void notEnough() {
System.out.println(ConsoleColors.red("Not enough coins!"));
}
private int readChoice(int min, int max) {
while (true) {
System.out.print("> ");
if (!scanner.hasNextLine()) {
return max; // treat end-of-input as "Leave"
}
try {
int value = Integer.parseInt(scanner.nextLine().trim());
if (value >= min && value <= max) {
return value;
}
} catch (NumberFormatException ignored) {
// fall through and re-prompt
}
System.out.println("Please enter a number between " + min + " and " + max + ".");
}
}
}
@@ -1,21 +1,39 @@
package org.project.entity;
/**
* The common contract that everything able to fight in the game must follow.
*
* Both {@code Player} and {@code Enemy} implement this interface, which is what
* lets the combat loop treat a Knight and a Dragon the same way (polymorphism):
* it only ever talks to an {@code Entity}.
*/
public interface Entity {
/** Perform this entity's basic attack against a target. */
void attack(Entity target);
/** Put the entity into a defensive stance for the next incoming hit. */
void defend();
/** Restore some health (capped at the maximum). */
void heal(int health);
/** Restore some mana/stamina (capped at the maximum). */
void fillMana(int mana);
/** Apply incoming damage to this entity. */
void takeDamage(int damage);
String getName();
int getHp();
int getMaxHP();
int getMp();
int getMaxMP();
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
/** @return true while the entity still has health left. */
boolean isAlive();
}
@@ -0,0 +1,30 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.entity.players.Player;
import org.project.util.ConsoleColors;
/**
* Dragon 🐉 — the final boss. Enormous HP and damage. Its fiery breath is "true
* damage": it bypasses armor and the player's defend stance entirely by calling
* the overloaded {@link Player#takeDamage(int, boolean)} with {@code true}.
*
* Even though it is the boss, it still extends {@link Enemy} so it reuses all
* the common combat plumbing — exactly what the assignment asks for.
*/
public class Dragon extends Enemy {
public Dragon() {
super("Dragon", 160, 26, 120, 100, null, 0);
}
@Override
public void attack(Entity target) {
System.out.println(ConsoleColors.red("🐉🔥 The Dragon unleashes a torrent of fire that bypasses all defenses!"));
if (target instanceof Player player) {
player.takeDamage(damage, true); // true = ignore armor and shields
} else {
target.takeDamage(damage);
}
}
}
@@ -1,34 +1,137 @@
package org.project.entity.enemies;
import org.project.item.weapons.Weapon;
import org.project.entity.Entity;
import org.project.util.ConsoleColors;
// TODO: UPDATE IMPLEMENTATION
public abstract class Enemy {
Weapon weapon;
/**
* Abstract base for every monster, including the Dragon boss.
*
* It holds the stats and rewards shared by all enemies and provides a plain
* default attack. Each subclass overrides {@link #attack(Entity)} (and sometimes
* {@link #takeDamage(int)}) to add its signature ability — a crit, a
* resurrection, lifesteal, or armor-piercing fire.
*/
public abstract class Enemy implements Entity {
protected String name;
private int hp;
private int mp;
private final int maxHP;
protected int damage;
public Enemy(int hp, int mp, Weapon weapon) {
this.hp = hp;
this.mp = mp;
private final int xpReward;
private final int coinReward;
private final KeyType keyType; // null for the Dragon (it drops no key)
private final int keyDropChance; // percent chance to drop its key
this.weapon = weapon;
private boolean stunned = false;
protected Enemy(String name, int maxHP, int damage,
int xpReward, int coinReward, KeyType keyType, int keyDropChance) {
this.name = name;
this.maxHP = maxHP;
this.hp = maxHP;
this.damage = damage;
this.xpReward = xpReward;
this.coinReward = coinReward;
this.keyType = keyType;
this.keyDropChance = keyDropChance;
}
/** Default attack: hit the target for this enemy's damage. */
@Override
public void attack(Entity target) {
System.out.println(ConsoleColors.red("👾 " + name + " attacks!"));
target.takeDamage(damage);
}
@Override
public void takeDamage(int damage) {
hp -= damage;
if (hp < 0) {
hp = 0;
}
// Note: the attacker prints the damage/HP lines, so this stays quiet.
}
/** Enemies don't have a defensive stance in this game. */
@Override
public void defend() {
// no-op
}
@Override
public void heal(int health) {
hp += health;
if (hp > maxHP) {
hp = maxHP;
}
}
/** Enemies use no mana, so this does nothing. */
@Override
public void fillMana(int mana) {
// no-op
}
// --- Stun handling (used by the Knight's Shield Bash) ---
public void stun() {
stunned = true;
}
public boolean isStunned() {
return stunned;
}
public void clearStun() {
stunned = false;
}
// --- Getters ---
@Override
public String getName() {
return name;
}
@Override
public int getHp() {
return hp;
}
public int getMp() {
return mp;
@Override
public int getMaxHP() {
return maxHP;
}
public Weapon getWeapon() {
return weapon;
@Override
public int getMp() {
return 0;
}
@Override
public int getMaxMP() {
return 0;
}
@Override
public boolean isAlive() {
return hp > 0;
}
public int getXpReward() {
return xpReward;
}
public int getCoinReward() {
return coinReward;
}
public KeyType getKeyType() {
return keyType;
}
public int getKeyDropChance() {
return keyDropChance;
}
}
@@ -0,0 +1,31 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.util.ConsoleColors;
import org.project.util.Dice;
/**
* Goblin 👹 — low health, but a high chance to land a devastating critical hit.
* Drops the Goblin Key.
*/
public class Goblin extends Enemy {
private static final int CRIT_CHANCE = 40; // percent
public Goblin() {
super("Goblin", 30, 8, 20, 12, KeyType.GOBLIN, 25);
}
@Override
public void attack(Entity target) {
if (Dice.chance(CRIT_CHANCE)) {
int critDamage = damage * 2;
System.out.println(ConsoleColors.red("👹 Goblin used Critical Strike!"));
System.out.println(ConsoleColors.red("💥 Critical hit!"));
target.takeDamage(critDamage);
} else {
System.out.println(ConsoleColors.red("👹 Goblin lunges with a rusty blade!"));
target.takeDamage(damage);
}
}
}
@@ -0,0 +1,22 @@
package org.project.entity.enemies;
/**
* The three keys that the Dragon scattered among the cursed monsters.
* Each standard enemy species is tied to exactly one key type, and the player
* needs all three to unlock the castle.
*/
public enum KeyType {
GOBLIN("Goblin Key"),
SKELETON("Skeleton Key"),
VAMPIRE("Vampire Key");
private final String displayName;
KeyType(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
@@ -1,6 +1,31 @@
package org.project.entity.enemies;
// TODO: UPDATE IMPLEMENTATION
public class Skeleton {
// TODO: DESIGN ENEMY AND IMPLEMENT THE CONSTRUCTOR
import org.project.util.ConsoleColors;
/**
* Skeleton ☠️ — a bag of bones that can pull itself back together once per
* battle, returning to 50% HP the first time it would die. Drops the Skeleton
* Key.
*/
public class Skeleton extends Enemy {
private boolean hasResurrected = false;
public Skeleton() {
super("Skeleton", 40, 10, 25, 14, KeyType.SKELETON, 25);
}
@Override
public void takeDamage(int damage) {
super.takeDamage(damage);
// First time it drops to 0, it revives at half health.
if (getHp() <= 0 && !hasResurrected) {
hasResurrected = true;
int revived = getMaxHP() / 2;
heal(revived); // hp was 0, so this sets it to 50% of max
System.out.println(ConsoleColors.purple(
"☠️ The Skeleton collapses into a pile of bones... then reassembles with " + getHp() + " HP!"));
}
}
}
@@ -0,0 +1,28 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.util.ConsoleColors;
/**
* Vampire 🦇 — heals itself for a portion of the damage it deals (lifesteal),
* making it a war of attrition. Drops the Vampire Key.
*/
public class Vampire extends Enemy {
private static final double LIFESTEAL = 0.5; // heals for 50% of damage dealt
public Vampire() {
super("Vampire", 48, 11, 30, 16, KeyType.VAMPIRE, 25);
}
@Override
public void attack(Entity target) {
System.out.println(ConsoleColors.red("🦇 Vampire sinks its fangs in!"));
target.takeDamage(damage);
int stolen = (int) (damage * LIFESTEAL);
heal(stolen);
System.out.println(ConsoleColors.green(
"🩸 Vampire drains " + stolen + " HP and now has " + getHp() + "/" + getMaxHP() + " HP."));
}
}
@@ -0,0 +1,42 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.item.armors.LeatherArmor;
import org.project.item.weapons.Dagger;
import org.project.util.ConsoleColors;
/**
* The Assassin 🗡️ — the nimble skirmisher. Highest max mana/stamina so it can
* chain abilities, with average damage and low HP.
*
* Special: Vanish — turns invisible, dodging the enemy's next attack entirely
* and guaranteeing a critical hit on the Assassin's next strike.
*/
public class Assassin extends Player {
public Assassin(String name) {
// Highest Mana/Stamina (55); low HP (45).
super(name, 45, 55, 6, new Dagger(), new LeatherArmor());
}
@Override
public String getClassName() {
return "Assassin";
}
@Override
public String getSpecialName() {
return "Vanish";
}
@Override
public void specialAbility(Entity target) {
spendMana(SPECIAL_COST);
// Flip the two combat-state flags the base class already knows about.
dodgeNext = true;
guaranteedCrit = true;
System.out.println(ConsoleColors.purple("🌫️ " + name + " VANISHES into the shadows! "
+ ConsoleColors.blue("(-" + SPECIAL_COST + " Mana)")));
System.out.println(ConsoleColors.purple(" The next enemy attack will miss, and the next strike is a guaranteed crit!"));
}
}
@@ -0,0 +1,24 @@
package org.project.entity.players;
import org.project.entity.Entity;
/**
* "The Rule of Five" every player class must offer exactly these five actions
* on its turn. Declaring them in an interface guarantees each class implements
* all of them, and lets the game loop drive any player class the same way.
*
* Light Attack is free; every other action costs mana/stamina, and the Special
* Ability always costs the most.
*/
public interface ICombatActions {
void lightAttack(Entity target);
void heavyAttack(Entity target);
void defend();
void healAction();
void specialAbility(Entity target);
}
@@ -1,6 +1,46 @@
package org.project.entity.players;
// TODO: UPDATE IMPLEMENTATION
public class Knight {
// TODO: DESIGN KNIGHT'S WEAPON AND ARMOR AND IMPLEMENT THE CONSTRUCTOR
import org.project.entity.Entity;
import org.project.entity.enemies.Enemy;
import org.project.item.armors.KnightArmor;
import org.project.item.weapons.Sword;
import org.project.util.ConsoleColors;
/**
* The Knight ⚔️ — the front-line bruiser. Highest base damage of the three
* classes and sturdy plate armor, but an average health and mana pool.
*
* Special: Shield Bash — deals heavy damage and stuns the enemy so it skips its
* next turn.
*/
public class Knight extends Player {
public Knight(String name) {
// Highest base damage (8); average HP/Mana.
super(name, 50, 40, 8, new Sword(), new KnightArmor());
}
@Override
public String getClassName() {
return "Knight";
}
@Override
public String getSpecialName() {
return "Shield Bash";
}
@Override
public void specialAbility(Entity target) {
spendMana(SPECIAL_COST);
System.out.println(ConsoleColors.purple("🛡️💥 " + name + " performs a mighty SHIELD BASH! "
+ ConsoleColors.blue("(-" + SPECIAL_COST + " Mana)")));
dealDamage(target, (int) (getAttackPower() * 2.2));
// Stun only makes sense against an actual enemy.
if (target instanceof Enemy enemy) {
enemy.stun();
System.out.println(ConsoleColors.yellow("😵 " + enemy.getName() + " is stunned and will skip its next turn!"));
}
}
}
@@ -1,42 +1,203 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.entity.enemies.KeyType;
import org.project.item.armors.Armor;
import org.project.item.consumables.Consumable;
import org.project.item.weapons.Weapon;
import org.project.util.ConsoleColors;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
/**
* Abstract base for all playable heroes (Knight, Wizard, Assassin).
*
* This class holds everything the three classes share: health/mana bars, the
* weapon and armor, the level/XP system, coins, the inventory of consumables,
* and the collected keys. It also implements four of the five combat actions,
* because Light/Heavy Attack, Defend and Heal work the same for every class.
* Only {@link #specialAbility(Entity)} is left abstract so each subclass can
* provide its own unique ultimate (polymorphism).
*/
public abstract class Player implements Entity, ICombatActions {
// Mana/stamina cost of each action. Special is deliberately the highest.
public static final int HEAVY_COST = 8;
public static final int DEFEND_COST = 6;
public static final int HEAL_COST = 12;
public static final int SPECIAL_COST = 15;
// TODO: UPDATE IMPLEMENTATION
public abstract class Player {
protected String name;
Weapon weapon;
Armor armor;
private int hp;
private int maxHP;
private int mp;
private int maxMP;
private int baseDamage;
public Player(String name, int hp, int mp, Weapon weapon, Armor armor) {
protected Weapon weapon;
protected Armor armor;
// Progression
private int level = 1;
private int xp = 0;
private int xpToNext = 50;
private int coins = 0;
private final List<Consumable> inventory = new ArrayList<>();
private final Set<KeyType> keys = EnumSet.noneOf(KeyType.class);
// Temporary combat states, all triggered by actions and consumed on the
// next relevant hit. Kept in the base class so takeDamage/dealDamage stay
// generic while subclasses just flip the flags.
protected boolean defending = false;
protected boolean dodgeNext = false;
protected boolean guaranteedCrit = false;
protected Player(String name, int maxHP, int maxMP, int baseDamage, Weapon weapon, Armor armor) {
this.name = name;
this.hp = hp;
this.mp = mp;
this.maxHP = maxHP;
this.hp = maxHP;
this.maxMP = maxMP;
this.mp = maxMP;
this.baseDamage = baseDamage;
this.weapon = weapon;
this.armor = armor;
}
/** The class name shown in menus, e.g. "Knight". */
public abstract String getClassName();
/** The display name of the special ability, used to build the turn menu. */
public abstract String getSpecialName();
// ------------------------------------------------------------------
// Combat actions (ICombatActions)
// ------------------------------------------------------------------
/** Total raw damage of a basic hit = class base damage + weapon damage. */
public int getAttackPower() {
return baseDamage + weapon.getDamage();
}
/** The generic Entity attack maps to the free Light Attack. */
@Override
public void attack(Entity target) {
target.takeDamage(weapon.getDamage());
lightAttack(target);
}
@Override
public void lightAttack(Entity target) {
System.out.println(ConsoleColors.cyan("⚔️ " + name + " (" + getClassName() + ") used Light Attack! (0 Mana)"));
dealDamage(target, getAttackPower());
}
@Override
public void heavyAttack(Entity target) {
spendMana(HEAVY_COST);
System.out.println(ConsoleColors.cyan("⚔️ " + name + " used Heavy Attack! " + manaTag(HEAVY_COST)));
dealDamage(target, getAttackPower() * 2);
}
@Override
public void defend() {
// TODO
spendMana(DEFEND_COST);
defending = true;
System.out.println(ConsoleColors.yellow("🛡️ " + name + " braces for the next hit! " + manaTag(DEFEND_COST)));
}
@Override
public void healAction() {
spendMana(HEAL_COST);
int amount = (int) (maxHP * 0.4);
heal(amount);
System.out.println(ConsoleColors.green("" + name + " heals for " + amount + " HP! " + manaTag(HEAL_COST)));
System.out.println(ConsoleColors.green(name + " now has " + hp + "/" + maxHP + " HP."));
}
// specialAbility is abstract -> each class supplies its own ultimate.
/**
* Shared damage routine for the player's attacks. Applies a guaranteed crit
* if the Assassin set one up, then reports the result.
*/
protected void dealDamage(Entity target, int amount) {
if (guaranteedCrit) {
amount *= 2;
guaranteedCrit = false;
System.out.println(ConsoleColors.purple("🌙 Critical hit from the shadows!"));
}
target.takeDamage(amount);
System.out.println(ConsoleColors.red(target.getName() + " took " + amount + " damage!"));
System.out.println(target.getName() + " has " + target.getHp() + "/" + target.getMaxHP() + " HP remaining.");
System.out.println(ConsoleColors.blue(name + " Mana: " + mp + "/" + maxMP));
}
// ------------------------------------------------------------------
// Taking damage (with overloading for the Dragon's true damage)
// ------------------------------------------------------------------
@Override
public void takeDamage(int damage) {
hp -= damage - armor.getDefense();
// Normal hit: goes through armor and any defensive stance.
takeDamage(damage, false);
}
/**
* Overloaded version. When {@code ignoreDefenses} is true the hit ignores
* armor and the defend stance this is how the Dragon's fiery breath
* bypasses shields.
*/
public void takeDamage(int damage, boolean ignoreDefenses) {
if (dodgeNext) {
dodgeNext = false;
System.out.println(ConsoleColors.purple("💨 " + name + " was invisible and dodged the attack completely!"));
return;
}
int incoming = damage;
if (!ignoreDefenses) {
if (defending) {
defending = false;
incoming = incoming / 4; // brace reduces the blow to 25%
System.out.println(ConsoleColors.yellow("🛡️ " + name + " blocks most of the blow!"));
}
incoming -= armor.getDefense();
armor.absorbHit();
}
if (incoming < 1) {
incoming = 1; // a hit always stings at least a little
}
hp -= incoming;
if (hp < 0) {
hp = 0;
}
System.out.println(ConsoleColors.red(name + " took " + incoming + " damage!"));
System.out.println(name + " has " + hp + "/" + maxHP + " HP remaining.");
}
// ------------------------------------------------------------------
// Resource helpers
// ------------------------------------------------------------------
/** Spend mana if affordable. Returns false when there is not enough. */
protected boolean spendMana(int cost) {
if (mp < cost) {
return false;
}
mp -= cost;
return true;
}
public boolean canAfford(int cost) {
return mp >= cost;
}
private String manaTag(int cost) {
return ConsoleColors.blue("(-" + cost + " Mana)");
}
@Override
@@ -55,11 +216,100 @@ public abstract class Player {
}
}
/** Refill HP and mana fully after a won battle. */
public void restoreAfterBattle() {
hp = maxHP;
mp = maxMP;
}
// ------------------------------------------------------------------
// Leveling
// ------------------------------------------------------------------
/** Award XP and level up as many times as the total allows. */
public void gainXP(int amount) {
xp += amount;
System.out.println(ConsoleColors.purple("✨ Gained " + amount + " XP! (" + xp + "/" + xpToNext + ")"));
while (xp >= xpToNext) {
xp -= xpToNext;
levelUp();
}
}
private void levelUp() {
level++;
xpToNext = (int) (xpToNext * 1.5);
maxHP += 10;
maxMP += 5;
baseDamage += 2;
hp = maxHP;
mp = maxMP;
System.out.println(ConsoleColors.bold(ConsoleColors.GREEN
+ "⬆️ LEVEL UP! " + name + " is now level " + level + "!" + ConsoleColors.RESET));
System.out.println(ConsoleColors.green(
" Max HP -> " + maxHP + " | Max Mana -> " + maxMP + " | Base Damage -> " + baseDamage));
}
// ------------------------------------------------------------------
// Inventory, coins and keys
// ------------------------------------------------------------------
public void addConsumable(Consumable c) {
inventory.add(c);
}
public List<Consumable> getInventory() {
return inventory;
}
/** Use (and remove) the consumable at the given index on this player. */
public void useConsumable(int index) {
Consumable c = inventory.remove(index);
c.use(this);
}
public void addCoins(int amount) {
coins += amount;
}
public boolean spendCoins(int amount) {
if (coins < amount) {
return false;
}
coins -= amount;
return true;
}
public int getCoins() {
return coins;
}
public void addKey(KeyType key) {
keys.add(key);
}
public boolean hasKey(KeyType key) {
return keys.contains(key);
}
public boolean hasAllKeys() {
return keys.size() == KeyType.values().length;
}
public Set<KeyType> getKeys() {
return keys;
}
// ------------------------------------------------------------------
// Simple getters
// ------------------------------------------------------------------
@Override
public String getName() {
return name;
}
@Override
public int getHp() {
return hp;
}
@@ -69,6 +319,7 @@ public abstract class Player {
return maxHP;
}
@Override
public int getMp() {
return mp;
}
@@ -78,12 +329,24 @@ public abstract class Player {
return maxMP;
}
@Override
public boolean isAlive() {
return hp > 0;
}
public int getLevel() {
return level;
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public Armor getArmor() {
return armor;
}
}
@@ -0,0 +1,44 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.item.armors.Robe;
import org.project.item.weapons.Staff;
import org.project.util.ConsoleColors;
/**
* The Wizard 🧙 — the glass cannon caster. Highest max HP so it can survive long
* fights, but weak basic attacks and flimsy armor.
*
* Special: Arcane Blast — blasts the enemy for heavy damage AND heals the Wizard
* at the same time.
*/
public class Wizard extends Player {
public Wizard(String name) {
// Highest HP (65); low base damage (5).
super(name, 65, 45, 5, new Staff(), new Robe());
}
@Override
public String getClassName() {
return "Wizard";
}
@Override
public String getSpecialName() {
return "Arcane Blast";
}
@Override
public void specialAbility(Entity target) {
spendMana(SPECIAL_COST);
System.out.println(ConsoleColors.purple("🔮 " + name + " channels an ARCANE BLAST! "
+ ConsoleColors.blue("(-" + SPECIAL_COST + " Mana)")));
dealDamage(target, (int) (getAttackPower() * 2.5));
int lifeReturn = (int) (getMaxHP() * 0.25);
heal(lifeReturn);
System.out.println(ConsoleColors.green("🌿 The spell's energy restores " + lifeReturn + " HP to " + name
+ " (" + getHp() + "/" + getMaxHP() + ")."));
}
}
@@ -2,10 +2,19 @@ package org.project.item;
import org.project.entity.Entity;
/**
* Anything that can live in the player's inventory or be sold by the merchant.
*
* Weapons, Armor and Consumables are the three families of items; they all
* implement this interface so the merchant can list and sell them uniformly.
*/
public interface Item {
/** Use / apply the item on a target (deal damage, heal, equip, ...). */
void use(Entity target);
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
String getName();
/** Cost in coins at the merchant. */
int getPrice();
}
@@ -1,17 +1,48 @@
package org.project.item.armors;
// TODO: UPDATE IMPLEMENTATION
public abstract class Armor {
import org.project.entity.Entity;
import org.project.item.Item;
/**
* Base class for every piece of armor. Armor lowers the damage the wearer takes
* through its {@code defense} value, and slowly wears out through
* {@code durability}: once durability hits zero the armor breaks and stops
* protecting until it is repaired at the merchant.
*/
public abstract class Armor implements Item {
private final String name;
private final int price;
private int defense;
private int maxDefense;
private final int maxDefense;
private int durability;
private int maxDurability;
private final int maxDurability;
private boolean isBroke;
public Armor(int defense, int durability) {
protected Armor(String name, int defense, int durability, int price) {
this.name = name;
this.defense = defense;
this.maxDefense = defense;
this.durability = durability;
this.maxDurability = durability;
this.price = price;
}
/** Equipping armor has no direct effect on a target, so this is a no-op. */
@Override
public void use(Entity target) {
// Armor is worn, not "used" on someone. Nothing to do here.
}
/** Every hit chips away one point of durability; may break the armor. */
public void absorbHit() {
if (isBroke) {
return;
}
durability--;
checkBreak();
}
public void checkBreak() {
@@ -21,13 +52,23 @@ public abstract class Armor {
}
}
// TODO: (BONUS) UPDATE THE REPAIR METHOD
/** Bonus: merchants can repair worn/broken armor back to full. */
public void repair() {
isBroke = false;
defense = maxDefense;
durability = maxDurability;
}
@Override
public String getName() {
return name;
}
@Override
public int getPrice() {
return price;
}
public int getDefense() {
return defense;
}
@@ -1,6 +1,12 @@
package org.project.item.armors;
// TODO: UPDATE IMPLEMENTATION
public class KnightArmor {
// TODO: DESIGN ARMOR'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
}
/**
* Heavy plate armor worn by the Knight. High defense to match the Knight's
* front-line, tanky role.
*/
public class KnightArmor extends Armor {
public KnightArmor() {
super("Knight Plate", 4, 25, 40);
}
}
@@ -0,0 +1,12 @@
package org.project.item.armors;
/**
* Light armor worn by the Assassin. Modest defense, but does not slow the
* nimble assassin down.
*/
public class LeatherArmor extends Armor {
public LeatherArmor() {
super("Leather Vest", 2, 20, 30);
}
}
@@ -0,0 +1,12 @@
package org.project.item.armors;
/**
* A simple cloth robe worn by the Wizard. Very little protection — the Wizard is
* squishy and relies on high HP and spells instead.
*/
public class Robe extends Armor {
public Robe() {
super("Mage Robe", 1, 18, 25);
}
}
@@ -1,8 +1,37 @@
package org.project.item.consumables;
// TODO: UPDATE IMPLEMENTATION
public abstract class Consumable {
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
import org.project.entity.Entity;
import org.project.item.Item;
/**
* Base class for one-shot items the player can carry and use in battle
* (potions, flasks, ...). Each consumable knows its name, its shop price and how
* potent its effect is; the actual effect is defined by each subclass in
* {@link #use(Entity)}.
*/
public abstract class Consumable implements Item {
private final String name;
private final int price;
protected final int potency;
protected Consumable(String name, int potency, int price) {
this.name = name;
this.potency = potency;
this.price = price;
}
/** Subclasses decide what the consumable actually does. */
@Override
public abstract void use(Entity target);
@Override
public String getName() {
return name;
}
@Override
public int getPrice() {
return price;
}
}
@@ -1,16 +1,21 @@
package org.project.item.consumables;
import org.project.entity.Entity;
import org.project.util.ConsoleColors;
// TODO: UPDATE IMPLEMENTATION
public class Flask {
/*
THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN.
*/
/**
* A healing flask. Restores a flat amount of HP to whoever drinks it.
*/
public class Flask extends Consumable {
public Flask() {
super("Health Flask", 25, 20);
}
// TODO: UPDATE USE METHOD
@Override
public void use(Entity target) {
target.heal(target.getMaxHP() / 10);
target.heal(potency);
System.out.println(ConsoleColors.green(
"🧪 " + target.getName() + " drinks a Health Flask and recovers " + potency + " HP!"));
}
}
@@ -0,0 +1,22 @@
package org.project.item.consumables;
import org.project.entity.Entity;
import org.project.util.ConsoleColors;
/**
* A mana/stamina potion. Restores a flat amount of the resource the player uses
* to power their abilities.
*/
public class ManaPotion extends Consumable {
public ManaPotion() {
super("Mana Potion", 20, 20);
}
@Override
public void use(Entity target) {
target.fillMana(potency);
System.out.println(ConsoleColors.blue(
"🔷 " + target.getName() + " drinks a Mana Potion and restores " + potency + " Mana!"));
}
}
@@ -0,0 +1,12 @@
package org.project.item.weapons;
/**
* The Assassin's starting weapon. Lower raw damage than the sword but cheap on
* stamina, which fits the Assassin's fast, resource-hungry play style.
*/
public class Dagger extends Weapon {
public Dagger() {
super("Twin Dagger", 6, 6, 30);
}
}
@@ -0,0 +1,12 @@
package org.project.item.weapons;
/**
* A heavy weapon only available from the merchant. Big damage for players who
* have saved up enough coins.
*/
public class GreatAxe extends Weapon {
public GreatAxe() {
super("Great Axe", 12, 10, 80);
}
}
@@ -0,0 +1,12 @@
package org.project.item.weapons;
/**
* The Wizard's starting weapon. Weak on basic hits, but the Wizard makes up for
* it with a powerful special ability.
*/
public class Staff extends Weapon {
public Staff() {
super("Oak Staff", 5, 10, 30);
}
}
@@ -2,25 +2,32 @@ package org.project.item.weapons;
import org.project.entity.Entity;
import java.util.ArrayList;
import java.util.List;
// TODO: UPDATE IMPLEMENTATION
public class Sword {
/*
THIS IS AN EXAMPLE OF A WEAPON DESIGN.
*/
/**
* The Knight's starting weapon. A reliable, high-damage blade.
*
* Also keeps the example "unique ability" idea from the skeleton: a sweeping
* slash that can hit several targets at once (useful in the bonus party mode).
*/
public class Sword extends Weapon {
int abilityCharge;
private int abilityCharge;
public Sword() {
// TODO: DESIGN SWORD'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
super("Iron Sword", 6, 8, 30);
}
// TODO: (BONUS) UPDATE THE UNIQUE ABILITY
public void uniqueAbility(ArrayList<Entity> targets) {
/** Constructor overloading: lets the merchant sell fancier swords. */
public Sword(String name, int damage, int price) {
super(name, damage, 8, price);
}
/** Bonus: a charged sweep that damages every target passed in. */
public void uniqueAbility(List<Entity> targets) {
abilityCharge += 2;
for (Entity target : targets) {
target.takeDamage(getDamage());
target.takeDamage(getDamage() + abilityCharge);
}
}
}
@@ -1,26 +1,43 @@
package org.project.item.weapons;
import org.project.entity.Entity;
import org.project.item.Item;
// TODO: UPDATE IMPLEMENTATION
public abstract class Weapon {
private int damage;
private int manaCost;
/**
* Base class shared by every weapon. It holds the numbers common to all weapons
* (damage, the mana it takes to swing hard with it, and its shop price) so the
* concrete weapons only have to fill in those values in their constructor.
*/
public abstract class Weapon implements Item {
/*
TODO: ADD OTHER REQUIRED AND BONUS ATTRIBUTES
*/
private final String name;
private final int damage;
private final int manaCost;
private final int price;
public Weapon(int damage, int manaCost) {
protected Weapon(String name, int damage, int manaCost, int price) {
this.name = name;
this.damage = damage;
this.manaCost = manaCost;
this.price = price;
}
/** Default weapon use = strike the target for the weapon's damage. */
@Override
public void use(Entity target) {
target.takeDamage(damage);
}
@Override
public String getName() {
return name;
}
@Override
public int getPrice() {
return price;
}
public int getDamage() {
return damage;
}
@@ -28,8 +45,4 @@ public abstract class Weapon {
public int getManaCost() {
return manaCost;
}
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
}
@@ -1,28 +1,33 @@
package org.project.location;
import org.project.entity.enemies.Enemy;
import org.project.entity.enemies.Goblin;
import org.project.entity.enemies.Skeleton;
import org.project.entity.enemies.Vampire;
import org.project.util.Dice;
import java.util.ArrayList;
/**
* A place in Javanest the player can wander into. Each visit to a location
* spawns a fresh, random standard enemy (Goblin, Skeleton or Vampire).
*/
public class Location {
private String name;
private ArrayList<Enemy> enemies;
private final String name;
public Location(ArrayList<Location> locations, ArrayList<Enemy> enemies) {
this.locations = locations;
this.enemies = enemies;
public Location(String name) {
this.name = name;
}
public String getName() {
return name;
}
public ArrayList<Location> getLocations() {
return locations;
}
public ArrayList<Enemy> getEnemies() {
return enemies;
/** Spawn one of the three standard enemies at random. */
public Enemy spawnEnemy() {
return switch (Dice.between(1, 3)) {
case 1 -> new Goblin();
case 2 -> new Skeleton();
default -> new Vampire();
};
}
}
@@ -0,0 +1,55 @@
package org.project.util;
/**
* Small helper that wraps text in ANSI escape codes so the combat log is
* colourful in the terminal. Keeping all the codes in one place means the rest
* of the game never has to remember the raw "[..m" strings.
*
* Colour convention used across the game:
* red -> damage taken / dealt
* blue -> mana / stamina usage
* green -> healing and good news
*/
public final class ConsoleColors {
private ConsoleColors() {
// utility class, never instantiated
}
public static final String RESET = "";
public static final String RED = "";
public static final String GREEN = "";
public static final String YELLOW = "";
public static final String BLUE = "";
public static final String PURPLE = "";
public static final String CYAN = "";
public static final String BOLD = "";
public static String red(String text) {
return RED + text + RESET;
}
public static String green(String text) {
return GREEN + text + RESET;
}
public static String blue(String text) {
return BLUE + text + RESET;
}
public static String yellow(String text) {
return YELLOW + text + RESET;
}
public static String purple(String text) {
return PURPLE + text + RESET;
}
public static String cyan(String text) {
return CYAN + text + RESET;
}
public static String bold(String text) {
return BOLD + text + RESET;
}
}
@@ -0,0 +1,26 @@
package org.project.util;
import java.util.concurrent.ThreadLocalRandom;
/**
* Tiny wrapper around the random number generator so the rest of the code reads
* nicely (e.g. {@code Dice.chance(20)} instead of raw Random calls everywhere).
*/
public final class Dice {
private Dice() {
}
/**
* @param percent a value from 0 to 100
* @return true with the given percentage probability
*/
public static boolean chance(int percent) {
return ThreadLocalRandom.current().nextInt(100) < percent;
}
/** @return a random integer in the inclusive range [min, max]. */
public static int between(int min, int max) {
return ThreadLocalRandom.current().nextInt(min, max + 1);
}
}
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,31 @@
org/project/Game.class
org/project/entity/enemies/Enemy.class
org/project/entity/enemies/Vampire.class
org/project/item/armors/Robe.class
org/project/entity/players/ICombatActions.class
org/project/item/consumables/Flask.class
org/project/item/weapons/Dagger.class
org/project/Main.class
org/project/entity/players/Wizard.class
org/project/Merchant.class
org/project/entity/players/Player.class
org/project/location/Location.class
org/project/entity/enemies/Dragon.class
org/project/entity/enemies/Goblin.class
org/project/util/ConsoleColors.class
org/project/item/weapons/Staff.class
org/project/entity/enemies/Skeleton.class
org/project/entity/Entity.class
org/project/item/armors/LeatherArmor.class
org/project/item/weapons/Sword.class
org/project/entity/players/Assassin.class
org/project/util/Dice.class
org/project/item/armors/KnightArmor.class
org/project/entity/enemies/KeyType.class
org/project/item/weapons/Weapon.class
org/project/item/weapons/GreatAxe.class
org/project/item/armors/Armor.class
org/project/entity/players/Knight.class
org/project/item/consumables/Consumable.class
org/project/item/consumables/ManaPotion.class
org/project/item/Item.class
@@ -0,0 +1,31 @@
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/Game.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/Main.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/Merchant.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/Entity.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/enemies/Dragon.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/enemies/Enemy.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/enemies/Goblin.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/enemies/KeyType.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/enemies/Skeleton.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/enemies/Vampire.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/players/Assassin.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/players/ICombatActions.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/players/Knight.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/players/Player.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/entity/players/Wizard.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/Item.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/armors/Armor.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/armors/KnightArmor.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/armors/LeatherArmor.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/armors/Robe.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/consumables/Consumable.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/consumables/Flask.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/consumables/ManaPotion.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/weapons/Dagger.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/weapons/GreatAxe.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/weapons/Staff.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/weapons/Sword.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/item/weapons/Weapon.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/location/Location.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/util/ConsoleColors.java
/Users/faraz/Documents/SBU/Term 6/AP/HW-04-JAVA-KNIGHT/Java-Knight/src/main/java/org/project/util/Dice.java