Java Knight
This commit is contained in:
+2
-2
@@ -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 = "[0m";
|
||||
public static final String RED = "[31m";
|
||||
public static final String GREEN = "[32m";
|
||||
public static final String YELLOW = "[33m";
|
||||
public static final String BLUE = "[34m";
|
||||
public static final String PURPLE = "[35m";
|
||||
public static final String CYAN = "[36m";
|
||||
public static final String BOLD = "[1m";
|
||||
|
||||
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31
@@ -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
|
||||
+31
@@ -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
|
||||
Reference in New Issue
Block a user