Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81cd45b475 | ||
|
|
971c9d7ed3 | ||
|
|
7edb9ecee9 |
@@ -1,15 +1,627 @@
|
||||
package org.project;
|
||||
|
||||
import org.project.entity.players.*;
|
||||
import org.project.entity.enemies.*;
|
||||
import org.project.location.Location;
|
||||
import org.project.item.armors.*;
|
||||
import org.project.item.weapons.*;
|
||||
import org.project.item.Item;
|
||||
import org.project.item.consumables.Flask;
|
||||
import org.project.item.consumables.StaminaPotion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
// TODO: ADD LOCATIONS TO YOUR GAME
|
||||
List<Location> locations = new ArrayList<>();
|
||||
|
||||
// TODO: IMPLEMENT GAMEPLAY
|
||||
private static final String RESET = "\033[0m"; // Text Reset
|
||||
|
||||
private static final String GREEN = "\033[0;32m"; // HP / Healing
|
||||
private static final String BLUE = "\033[0;34m"; // Stamina
|
||||
private static final String RED = "\033[0;31m"; // Enemies
|
||||
private static final String YELLOW = "\033[0;33m"; // Coins / XP
|
||||
private static final String PURPLE = "\033[0;35m"; // Events
|
||||
private static final String CYAN = "\033[0;36m"; // Locations
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
Random random = new Random();
|
||||
|
||||
|
||||
ArrayList<Enemy> forestEnemies = new ArrayList<>();
|
||||
Location forest = new Location("The Dark Forest", forestEnemies);
|
||||
|
||||
ArrayList<Enemy> graveyardEnemies = new ArrayList<>();
|
||||
Location graveyard = new Location("The Forsaken Graveyard", graveyardEnemies);
|
||||
|
||||
ArrayList<Enemy> cryptEnemies = new ArrayList<>();
|
||||
Location crypt = new Location("The Blood Crypt", cryptEnemies);
|
||||
|
||||
Location[] standardLocations = {forest, graveyard, crypt};
|
||||
|
||||
Enemy dragon = new Dragon(new Flame("Flame", 60, 10));
|
||||
|
||||
|
||||
|
||||
System.out.println("\n" + PURPLE + "=== WELCOME TO THE GRIM REALM ===" + RESET);
|
||||
System.out.println("Choose Your Character:");
|
||||
System.out.println("1. Knight | 2. Assassin | 3. Wizard");
|
||||
String choice = scanner.nextLine();
|
||||
|
||||
System.out.print("Enter Your Hero's Name: ");
|
||||
String name = scanner.nextLine();
|
||||
|
||||
Player player = null;
|
||||
switch (choice) {
|
||||
case "1":
|
||||
player = new Knight(name, new Sword("Sword", 40, 5), new KnightArmor("Knight Plate", 30));
|
||||
break;
|
||||
case "2":
|
||||
player = new Assassin(name, new HiddenBlade("Hidden Blade", 30, 5), new AssassinArmor("Assassin Plate", 30));
|
||||
break;
|
||||
case "3":
|
||||
player = new Wizard(name, new Staff("Staff", 20, 5), new WizardArmor("Wizard Plate", 30));
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Choice.");
|
||||
}
|
||||
|
||||
|
||||
boolean playing = true;
|
||||
String currentState = "CAMP";
|
||||
while (playing && player.isAlive()) {
|
||||
|
||||
switch (currentState) {
|
||||
case "CAMP":
|
||||
player.fillStats();
|
||||
System.out.println("\n" + CYAN + "=== CAMP ===" + RESET);
|
||||
System.out.println("HP: " + GREEN + player.getHP() + "/" + player.getMaxHP() + RESET + " | " +
|
||||
"Stamina: " + BLUE + player.getStamina() + "/" + player.getMaxStamina() + RESET);
|
||||
System.out.println("Level: " + player.getLevel() + " | Coins: " + YELLOW + player.getCoins() + RESET + "\n");
|
||||
System.out.println("1. Step Into The Portal");
|
||||
System.out.println("2. Visit The Merchant");
|
||||
System.out.println("3. Rest By The Fire (Quit Game)");
|
||||
|
||||
String hubChoice = scanner.nextLine();
|
||||
|
||||
switch (hubChoice) {
|
||||
case "1":
|
||||
System.out.println("\n" + PURPLE + "The Portal Swirls." + RESET + " Where To?");
|
||||
System.out.println("1. Hunt The Dragon's Minions (Random Location)");
|
||||
System.out.println("2. Challenge The Dragon (The Castle)");
|
||||
String portalChoice = scanner.nextLine();
|
||||
|
||||
switch (portalChoice) {
|
||||
case "1":
|
||||
currentState = "RANDOM";
|
||||
break;
|
||||
case "2":
|
||||
currentState = "CASTLE";
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Choice.");
|
||||
}
|
||||
break;
|
||||
case "2":
|
||||
visitMerchant(player, scanner);
|
||||
break;
|
||||
case "3":
|
||||
System.out.println(PURPLE + "You Rest By The Fire. Game Saved." + RESET);
|
||||
playing = false;
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Choice.");
|
||||
}
|
||||
break;
|
||||
|
||||
case "RANDOM":
|
||||
Location currentLoc = standardLocations[random.nextInt(standardLocations.length)];
|
||||
Enemy areaEnemy = null;
|
||||
|
||||
if (currentLoc.getName().contains("Forest")) {
|
||||
areaEnemy = new Goblin(new Dagger("Dagger", 20, 5));
|
||||
} else if (currentLoc.getName().contains("Graveyard")) {
|
||||
areaEnemy = new Skeleton(new Sword("Dagger", 20, 5));
|
||||
} else if (currentLoc.getName().contains("Crypt")) {
|
||||
areaEnemy = new Vampire(new Sword("Dagger", 20, 5));
|
||||
}
|
||||
|
||||
System.out.println("\n" + PURPLE + "You Get Out Of The Portal..." + RESET);
|
||||
System.out.println("You Find Yourself In " + CYAN + currentLoc.getName() + RESET + ".");
|
||||
System.out.println("A " + RED + areaEnemy.getName() + RESET + " Is Lurking In The Shadows!");
|
||||
|
||||
System.out.println("\nWhat Will You Do?");
|
||||
System.out.println("1. Fight The " + areaEnemy.getName());
|
||||
System.out.println("2. Step Back Into The Portal (Teleport Elsewhere)");
|
||||
|
||||
String portalChoice = scanner.nextLine();
|
||||
|
||||
switch (portalChoice) {
|
||||
case "1":
|
||||
battle(player, areaEnemy, scanner);
|
||||
if (!player.isAlive()) {
|
||||
break;
|
||||
}
|
||||
System.out.println("\nWhat Will You Do Now?");
|
||||
System.out.println("1. Continue Hunting The Enemies");
|
||||
System.out.println("2. Get Back To Camp To Recover");
|
||||
String victoryChoice = scanner.nextLine();
|
||||
|
||||
switch (victoryChoice) {
|
||||
case "1":
|
||||
currentState = "RANDOM";
|
||||
break;
|
||||
case "2":
|
||||
currentState = "CAMP";
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Choice.");
|
||||
}
|
||||
break;
|
||||
case "2":
|
||||
System.out.println("\n" + PURPLE + "The Portal Swirls." + RESET + " Where To?");
|
||||
System.out.println("1. Hunt The Dragon's Minions (Random Location)");
|
||||
System.out.println("2. Return To The Camp");
|
||||
String portalChoice1 = scanner.nextLine();
|
||||
|
||||
switch (portalChoice1) {
|
||||
case "1":
|
||||
currentState = "RANDOM";
|
||||
break;
|
||||
case "2":
|
||||
currentState = "CAMP";
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Choice.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Choice.");
|
||||
}
|
||||
break;
|
||||
|
||||
case "CASTLE":
|
||||
System.out.println("\nYou Stand Before The Massive Gates Of The Castle.");
|
||||
|
||||
if (player.getInventory().hasAllKeys()) {
|
||||
System.out.println("The Three Keys React To The Dark Magic." + PURPLE + " The Gate Opens!" + RESET);
|
||||
player.setCanDefend(false);
|
||||
battle(player, dragon, scanner);
|
||||
player.setCanDefend(true);
|
||||
playing = false;
|
||||
} else {
|
||||
System.out.println("You Must Have All 3 Keys To Open The Gates.");
|
||||
System.out.println("\n" + PURPLE + "The Portal Behind You Swirls." + RESET + " Where To?");
|
||||
System.out.println("1. Hunt The Dragon's Minions (Find The Missing Keys)");
|
||||
System.out.println("2. Return To Camp");
|
||||
|
||||
String castleChoice = scanner.nextLine();
|
||||
switch (castleChoice) {
|
||||
case "1":
|
||||
currentState = "RANDOM";
|
||||
break;
|
||||
case "2":
|
||||
currentState = "CAMP";
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Choice.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void battle(Player player, Enemy enemy, Scanner scanner) {
|
||||
System.out.println("\n" + ">>>" + PURPLE + " BATTLE STARTED: " + GREEN + player.getName() + RESET + " VS " + RED + enemy.getName() + RESET + " <<<");
|
||||
|
||||
boolean isPlayerTurn = true;
|
||||
|
||||
while (player.isAlive() && enemy.isAlive()) {
|
||||
if (isPlayerTurn) {
|
||||
boolean actionTaken = false;
|
||||
|
||||
while (!actionTaken && player.isAlive()) {
|
||||
if(!player.isStunned()) {
|
||||
System.out.println("\n--- YOUR TURN ---");
|
||||
System.out.println("HP: " + GREEN + player.getHP() + RESET + " | Stamina: " + BLUE + player.getStamina() + RESET +
|
||||
" |" + PURPLE + "VS" + RESET + "| " +
|
||||
"Enemy HP: " + RED + enemy.getHP() + RESET + " | " + "Enemy Stamina: " + BLUE + enemy.getStamina() + RESET);
|
||||
|
||||
System.out.println("1. Light Attack (" + BLUE + "0" + RESET + ") | " +
|
||||
"2. Heavy Attack (" + BLUE + player.getWeapon().getStaminaCost() + RESET + ") | " +
|
||||
"3. Defend (" + BLUE + "5" + RESET + ") | " +
|
||||
"4. Special (" + BLUE + "20" + RESET + ") | " +
|
||||
"5. " + YELLOW + "Use Healing Potion" + RESET + " (" + BLUE + "0" + RESET + ") | " +
|
||||
"6. " + YELLOW + "Use Stamina Potion" + RESET + " (" + BLUE + "0" + RESET + ")");
|
||||
// Removed Healing By Using Stamina Since It Didn't Really Make Sense.
|
||||
|
||||
String choice = scanner.nextLine();
|
||||
|
||||
switch (choice) {
|
||||
case "1":
|
||||
player.lightAttack(enemy);
|
||||
actionTaken = true;
|
||||
break;
|
||||
case "2":
|
||||
player.heavyAttack(enemy);
|
||||
actionTaken = true;
|
||||
break;
|
||||
case "3":
|
||||
player.defend();
|
||||
actionTaken = true;
|
||||
break;
|
||||
case "4":
|
||||
player.specialAbility(enemy);
|
||||
actionTaken = true;
|
||||
break;
|
||||
case "5":
|
||||
Item healingFlask = player.getInventory().getMiscItemByName("Healing Flask");
|
||||
|
||||
if (healingFlask == null || healingFlask.getQuantity() <= 0) {
|
||||
System.out.println("You Don't Have Any Healing Flasks Left!");
|
||||
break;
|
||||
}
|
||||
|
||||
healingFlask.use(player);
|
||||
break;
|
||||
case "6":
|
||||
Item staminaPotion = player.getInventory().getMiscItemByName("Stamina");
|
||||
|
||||
if (staminaPotion == null || staminaPotion.getQuantity() <= 0) {
|
||||
System.out.println("You Don't Have Any Stamina Potions Left!");
|
||||
break;
|
||||
}
|
||||
|
||||
staminaPotion.use(player);
|
||||
break;
|
||||
|
||||
default: System.out.println("Invalid Selection.");
|
||||
}
|
||||
} else {
|
||||
System.out.println("\n" + PURPLE + "You Are Stunned And Skip Your Turn!" + RESET);
|
||||
actionTaken = true;
|
||||
player.setStunned(false);
|
||||
|
||||
}
|
||||
}
|
||||
isPlayerTurn = false;
|
||||
} else {
|
||||
|
||||
if (!enemy.isStunned()) {
|
||||
System.out.println("\n--- ENEMY TURN ---");
|
||||
int enemyMove = enemy.choice();
|
||||
System.out.println(enemyMove);
|
||||
switch (enemyMove) {
|
||||
case 1:
|
||||
enemy.lightAttack(player);
|
||||
break;
|
||||
case 2:
|
||||
enemy.heavyAttack(player);
|
||||
break;
|
||||
case 3:
|
||||
enemy.defend();
|
||||
break;
|
||||
case 4: // Dragon's Special Ability Which Stuns The Player
|
||||
player.takeDamage(80, enemy);
|
||||
player.setStunned(true);
|
||||
System.out.println("The Dragon Has Stunned You!");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
System.out.println("\n" + PURPLE + "The Enemy Is Stunned And Skips Their Turn!" + RESET);
|
||||
enemy.setStunned(false);
|
||||
}
|
||||
isPlayerTurn = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enemy.isAlive()) {
|
||||
handleVictory(player, enemy);
|
||||
} else if (!player.isAlive()) {
|
||||
System.out.println(RED + "\nYOU DIED." + RESET + " Your Soul Remains In The Grim Realm...");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void handleVictory(Player player, Enemy enemy) {
|
||||
System.out.println("\n" + GREEN + "--- VICTORY! ---" + RESET);
|
||||
player.addCoins(enemy.getCoinReward());
|
||||
|
||||
if (!(enemy instanceof Dragon)) {
|
||||
if (enemy.hasKey()) {
|
||||
if (enemy instanceof Goblin && !player.hasGoblinKey()) {
|
||||
player.findGoblinKey();
|
||||
} else if (enemy instanceof Skeleton && !player.hasSkeletonKey()) {
|
||||
player.findSkeletonKey();
|
||||
} else if (enemy instanceof Vampire && !player.hasVampireKey()) {
|
||||
player.findVampireKey();
|
||||
}
|
||||
} else {
|
||||
System.out.println("The Enemy Had No Keys With Him.");
|
||||
}
|
||||
player.gainXP(enemy.getXpReward());
|
||||
} else {
|
||||
player.gainXP(enemy.getXpReward());
|
||||
System.out.println(PURPLE + "THE DRAGON DESCENDS!" + RESET);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void visitMerchant(Player player, Scanner scanner) {
|
||||
boolean inShop = true;
|
||||
|
||||
ArrayList<Weapon> weaponsForSale = new ArrayList<>();
|
||||
ArrayList<Integer> weaponPrice = new ArrayList<>();
|
||||
ArrayList<Armor> armorsForSale = new ArrayList<>();
|
||||
ArrayList<Integer> armorPrice = new ArrayList<>();
|
||||
|
||||
// You Can Add Multiple Armors And Weapons Here
|
||||
if (player instanceof Knight) {
|
||||
weaponsForSale.add(new Sword("GreatSword", 50, 0));
|
||||
weaponPrice.add(100);
|
||||
armorsForSale.add(new KnightArmor("Templar Plate", 70));
|
||||
armorPrice.add(200);
|
||||
} else if (player instanceof Assassin) {
|
||||
weaponsForSale.add(new HiddenBlade("Dual Hidden Blades", 40, 0));
|
||||
weaponPrice.add(100);
|
||||
armorsForSale.add(new KnightArmor("Master Assassin Plate", 70));
|
||||
armorPrice.add(200);
|
||||
} else if (player instanceof Wizard) {
|
||||
weaponsForSale.add(new Staff("GreatStaff", 30, 0));
|
||||
weaponPrice.add(100);
|
||||
armorsForSale.add(new KnightArmor("Magician Plate", 70));
|
||||
armorPrice.add(200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Item healingPotion = new Flask(3);
|
||||
int healingPotionPrice = 20;
|
||||
|
||||
Item staminaPotion = new StaminaPotion(3);
|
||||
int staminaPotionPrice = 10;
|
||||
|
||||
System.out.println("\nWelcome, Stranger!");
|
||||
|
||||
while (inShop) {
|
||||
System.out.println("\n" + CYAN + "=== MERCHANT SHOP ===" + RESET);
|
||||
System.out.println("1. View and Buy Equipment");
|
||||
System.out.println("2. Manage Equipment");
|
||||
System.out.println("0. Exit Shop");
|
||||
|
||||
String choice = scanner.nextLine();
|
||||
|
||||
switch (choice) {
|
||||
case "1":
|
||||
boolean viewingWares = true;
|
||||
while (viewingWares) {
|
||||
System.out.println("\n" + PURPLE + "=== FOR SALE ===" + RESET);
|
||||
System.out.println("Your Coins: " + YELLOW + player.getCoins() + RESET);
|
||||
|
||||
System.out.println("1. Weapons");
|
||||
System.out.println("2. Armors");
|
||||
System.out.println("3. " + healingPotion.getName() + " (" + healingPotion.getQuantity() + ") " + " - " + YELLOW + healingPotionPrice + RESET + " Coins");
|
||||
System.out.println("4. " + staminaPotion.getName() + " (" + healingPotion.getQuantity() + ") " + " - " + YELLOW + staminaPotionPrice + RESET + " Coins");
|
||||
System.out.println("0. Back To Main Shop Menu");
|
||||
|
||||
System.out.println("\nEnter Item Number:");
|
||||
String buyChoice = scanner.nextLine();
|
||||
|
||||
switch (buyChoice) {
|
||||
case "1":
|
||||
boolean buyingWeapons = true;
|
||||
while (buyingWeapons) {
|
||||
for (int i = 0; i < weaponsForSale.size(); i++) {
|
||||
System.out.println((i + 1) + ". " + weaponsForSale.get(i).getName() + " (Damage: " + weaponsForSale.get(i).getDamage() + ")" + " - " + YELLOW + weaponPrice.get(i) + " Coins" + RESET);
|
||||
}
|
||||
System.out.println("0. Back To Buy Menu");
|
||||
String weaponChoice = scanner.nextLine();
|
||||
try {
|
||||
int weaponIdx = Integer.parseInt(weaponChoice) - 1;
|
||||
|
||||
|
||||
if (weaponIdx == -1) {
|
||||
buyingWeapons = false;
|
||||
} else if (weaponIdx >= 0 && weaponIdx < weaponsForSale.size()) {
|
||||
Weapon selectedWeapon = weaponsForSale.get(weaponIdx);
|
||||
int price = weaponPrice.get(weaponIdx);
|
||||
|
||||
if (player.getCoins() >= price) {
|
||||
player.spendCoins(price);
|
||||
player.getInventory().addWeapon(selectedWeapon);
|
||||
System.out.println(GREEN + "Bought " + selectedWeapon.getName() + "!" + RESET);
|
||||
|
||||
weaponsForSale.remove(weaponIdx);
|
||||
weaponPrice.remove(weaponIdx);
|
||||
|
||||
|
||||
} else {
|
||||
System.out.println(RED + "Not Enough Coins!" + RESET);
|
||||
}
|
||||
} else if (weaponIdx != -1) {
|
||||
System.out.println("Invalid Weapon Selection.");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case "2":
|
||||
boolean buyingArmors = true;
|
||||
while (buyingArmors) {
|
||||
for (int i = 0; i < armorsForSale.size(); i++) {
|
||||
System.out.println((i + 1) + ". " + armorsForSale.get(i).getName() + " (Defense: " + armorsForSale.get(i).getDefense() + ")" + " - " + YELLOW + armorPrice.get(i) + " Coins" + RESET);
|
||||
}
|
||||
System.out.println("0. Back To Buy Menu");
|
||||
String armorChoice = scanner.nextLine();
|
||||
try {
|
||||
int armorIdx = Integer.parseInt(armorChoice) - 1;
|
||||
|
||||
|
||||
if (armorIdx == -1) {
|
||||
buyingArmors = false;
|
||||
} else if (armorIdx >= 0 && armorIdx < armorsForSale.size()) {
|
||||
Armor selectedArmor = armorsForSale.get(armorIdx);
|
||||
int price = armorPrice.get(armorIdx);
|
||||
|
||||
if (player.getCoins() >= price) {
|
||||
player.spendCoins(price);
|
||||
player.getInventory().addArmor(selectedArmor);
|
||||
System.out.println(GREEN + "Bought " + selectedArmor.getName() + "!" + RESET);
|
||||
|
||||
armorsForSale.remove(armorIdx);
|
||||
armorPrice.remove(armorIdx);
|
||||
|
||||
|
||||
} else {
|
||||
System.out.println(RED + "Not Enough Coins!" + RESET);
|
||||
}
|
||||
} else if (armorIdx != -1) {
|
||||
System.out.println("Invalid Armor Selection.");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "3":
|
||||
if (player.getCoins() >= healingPotionPrice) {
|
||||
player.spendCoins(healingPotionPrice);
|
||||
player.getInventory().addMiscItem(healingPotion);
|
||||
System.out.println(GREEN + "Bought " + healingPotion.getName() + "!" + RESET);
|
||||
} else {
|
||||
System.out.println(RED + "Not Enough Coins!" + RESET);
|
||||
}
|
||||
break;
|
||||
case "4":
|
||||
if (player.getCoins() >= staminaPotionPrice) {
|
||||
player.spendCoins(staminaPotionPrice);
|
||||
player.getInventory().addMiscItem(staminaPotion);
|
||||
System.out.println(GREEN + "Bought " + staminaPotion.getName() + "!" + RESET);
|
||||
} else {
|
||||
System.out.println(RED + "Not Enough Coins!" + RESET);
|
||||
}
|
||||
break;
|
||||
case "0":
|
||||
viewingWares = false;
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "2":
|
||||
boolean managing = true;
|
||||
while (managing) {
|
||||
System.out.println("\n" + PURPLE + "=== MANAGE EQUIPMENT ===" + RESET);
|
||||
System.out.println("1. Weapons");
|
||||
System.out.println("2. Armors");
|
||||
System.out.println("3. View Inventory");
|
||||
System.out.println("0. Back To Main Shop Menu");
|
||||
|
||||
String manageChoice = scanner.nextLine();
|
||||
|
||||
switch (manageChoice) {
|
||||
case "1":
|
||||
boolean choosingWeapon = true;
|
||||
while (choosingWeapon) {
|
||||
System.out.println("\n--- Your Weapons ---");
|
||||
for (int i = 0; i < player.getInventory().getWeapons().size(); i++) {
|
||||
Weapon w = player.getInventory().getWeapons().get(i);
|
||||
String equipped = (w == player.getWeapon()) ? GREEN + " (Equipped)" + RESET : "";
|
||||
System.out.println((i + 1) + ". " + w.getName() + " (Damage: " + w.getDamage() + ")" + equipped);
|
||||
}
|
||||
System.out.println("0. Back To Equipment Menu");
|
||||
System.out.println("\nEnter Number Of Weapon To Equip:");
|
||||
try {
|
||||
int eqW = Integer.parseInt(scanner.nextLine());
|
||||
if (eqW == 0) {
|
||||
choosingWeapon = false;
|
||||
} else if (eqW > 0 && eqW <= player.getInventory().getWeapons().size()) {
|
||||
player.equipWeapon(eqW - 1);
|
||||
} else {
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "2":
|
||||
boolean choosingArmor = true;
|
||||
while (choosingArmor) {
|
||||
System.out.println("\n--- Your Armors ---");
|
||||
for (int i = 0; i < player.getInventory().getArmors().size(); i++) {
|
||||
Armor a = player.getInventory().getArmors().get(i);
|
||||
String equipped = (a == player.getArmor()) ? GREEN + " (Equipped)" + RESET : "";
|
||||
System.out.println((i + 1) + ". " + a.getName() + " (Defense: " + a.getDefense() +" | Durability: " + a.getDurability() + ")" + equipped);
|
||||
}
|
||||
System.out.println("0. Back To Equipment Menu");
|
||||
System.out.println("\nEnter Number Of Armor To Equip And Repair (" + YELLOW + "20" + RESET + " Coins) If Needed:");
|
||||
try {
|
||||
int eqA = Integer.parseInt(scanner.nextLine());
|
||||
if (eqA == 0) {
|
||||
choosingArmor = false;
|
||||
} else if (eqA > 0 && eqA <= player.getInventory().getArmors().size()) {
|
||||
Armor selectedArmor = player.getInventory().getArmors().get(eqA - 1);
|
||||
if (selectedArmor.getDurability() < 100) {
|
||||
if (player.getCoins() > 20) {
|
||||
selectedArmor.repair();
|
||||
player.spendCoins(20);
|
||||
} else {
|
||||
System.out.println(RED + "Not Enough Coins!" + RESET);
|
||||
}
|
||||
}
|
||||
player.equipArmor(eqA - 1);
|
||||
} else {
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case"3":
|
||||
boolean viewingPotions = true;
|
||||
while (viewingPotions) {
|
||||
System.out.println("\n--- Your Items ---");
|
||||
|
||||
player.getInventory().showMiscItems();
|
||||
|
||||
System.out.println("0. Back To Equipment Menu");
|
||||
String potionChoice = scanner.nextLine();
|
||||
|
||||
if (potionChoice.equals("0")) {
|
||||
viewingPotions = false;
|
||||
} else {
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "0":
|
||||
managing = false;
|
||||
break;
|
||||
default:
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "0":
|
||||
System.out.println("Come Back Anytime.");
|
||||
inShop = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
System.out.println("Invalid Selection.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,37 @@
|
||||
package org.project.entity;
|
||||
|
||||
public interface Entity {
|
||||
void attack(Entity target);
|
||||
void lightAttack(Entity target);
|
||||
|
||||
void heavyAttack(Entity target);
|
||||
|
||||
void defend();
|
||||
|
||||
void heal(int health);
|
||||
|
||||
void fillMana(int mana);
|
||||
void takeDamage(int damage, Entity attacker);
|
||||
|
||||
void takeDamage(int damage);
|
||||
void fillHealth(int health);
|
||||
|
||||
void fillStamina(int stamina);
|
||||
|
||||
boolean spendStamina(int cost);
|
||||
|
||||
boolean isAlive();
|
||||
|
||||
boolean isDefending();
|
||||
|
||||
boolean isStunned();
|
||||
|
||||
void setStunned(boolean status);
|
||||
|
||||
String getName();
|
||||
|
||||
int getHP();
|
||||
|
||||
int getMaxHP();
|
||||
|
||||
int getMaxMP();
|
||||
int getStamina();
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS METHODS
|
||||
*/
|
||||
int getMaxStamina();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Dragon extends Enemy {
|
||||
|
||||
public Dragon(Weapon weapon) {
|
||||
super("Furious Dragon", 300, 100, weapon, 0.0);
|
||||
this.xpReward = 200;
|
||||
this.coinReward = 150;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int choice() {
|
||||
double roll = Math.random();
|
||||
|
||||
if (this.getStamina() >= 30) {
|
||||
if (roll < 0.3) {return 1;}
|
||||
else if (roll < 0.7) {return 2;}
|
||||
else if (roll < 0.8) {return 3;}
|
||||
else {return 4;}
|
||||
} else if (this.getStamina() >= this.weapon.getStaminaCost()) {
|
||||
if (roll < 0.4) {return 1;}
|
||||
else if (roll < 0.9) {return 2;}
|
||||
else {return 3;}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +1,154 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Enemy {
|
||||
Weapon weapon;
|
||||
private int hp;
|
||||
private int mp;
|
||||
public abstract class Enemy implements Entity {
|
||||
protected String name;
|
||||
protected int hp;
|
||||
protected int maxHP;
|
||||
protected int stamina;
|
||||
protected int maxStamina;
|
||||
|
||||
public Enemy(int hp, int mp, Weapon weapon) {
|
||||
protected int xpReward;
|
||||
protected int coinReward;
|
||||
protected boolean hasKey;
|
||||
|
||||
protected boolean isDefending = false;
|
||||
protected boolean isStunned = false;
|
||||
|
||||
protected Weapon weapon;
|
||||
|
||||
public Enemy(String name, int hp, int stamina, Weapon weapon, double keyChance) {
|
||||
this.name = name;
|
||||
this.hp = hp;
|
||||
this.mp = mp;
|
||||
|
||||
this.maxHP = hp;
|
||||
this.stamina = stamina;
|
||||
this.maxStamina = stamina;
|
||||
this.weapon = weapon;
|
||||
this.hasKey = Math.random() < keyChance;
|
||||
|
||||
}
|
||||
|
||||
public boolean isDefending() {
|
||||
return isDefending;
|
||||
}
|
||||
|
||||
public boolean isStunned() {
|
||||
return isStunned;
|
||||
}
|
||||
|
||||
public void setStunned(boolean status) {
|
||||
this.isStunned = status;
|
||||
}
|
||||
|
||||
public boolean hasKey() {
|
||||
return hasKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage) {
|
||||
hp -= damage;
|
||||
public void lightAttack(Entity target) {
|
||||
target.takeDamage(20, this);
|
||||
System.out.println(name + " Performs a Light Attack!");
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
@Override
|
||||
public void heavyAttack(Entity target) {
|
||||
if (spendStamina(weapon.getStaminaCost())) {
|
||||
target.takeDamage(weapon.getDamage(), this);
|
||||
System.out.println(name + " Performs a Heavy Attack!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defend() {
|
||||
if (spendStamina(5)) {
|
||||
this.isDefending = true;
|
||||
System.out.println(name + " Gets Ready To Block Your Attack...");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heal(int health) {
|
||||
if (spendStamina(10)) {
|
||||
this.hp = Math.min(maxHP, this.hp + health);
|
||||
System.out.println(name + " Healed " + health + " HP!");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract int choice();
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage, Entity attacker) {
|
||||
if (this.isDefending) {
|
||||
if (!attacker.isDefending()) {
|
||||
System.out.println(name + " Defended Your Attack!");
|
||||
}
|
||||
this.isDefending = false;
|
||||
return;
|
||||
}
|
||||
this.hp = Math.max(0, this.hp - damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillHealth(int health) {
|
||||
this.hp = Math.min(maxHP, this.hp + health);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStamina(int stamina) {
|
||||
this.stamina = Math.min(maxStamina, this.stamina + stamina);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spendStamina(int cost) {
|
||||
if (this.stamina >= cost) {
|
||||
this.stamina -= cost;
|
||||
return true;
|
||||
}
|
||||
System.out.println(name + " Doesn't Have Enough Stamina!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return this.hp > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHP() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
public int getMp() {
|
||||
return mp;
|
||||
@Override
|
||||
public int getMaxHP() {
|
||||
return maxHP;
|
||||
}
|
||||
|
||||
public int getStamina() {
|
||||
return stamina;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStamina() {
|
||||
return maxStamina;
|
||||
}
|
||||
|
||||
public Weapon getWeapon() {
|
||||
return weapon;
|
||||
}
|
||||
|
||||
public int getXpReward() {
|
||||
return xpReward;
|
||||
}
|
||||
|
||||
public int getCoinReward() {
|
||||
return coinReward;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Goblin extends Enemy {
|
||||
|
||||
public Goblin(Weapon weapon) {
|
||||
super("Goblin", 140, 40, weapon, 0.3);
|
||||
this.xpReward = 80;
|
||||
this.coinReward = 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int choice() {
|
||||
double roll = Math.random();
|
||||
|
||||
if (this.getStamina() >= this.weapon.getStaminaCost()) {
|
||||
if (roll < 0.2) {return 1;}
|
||||
else if (roll < 0.8) {return 2;}
|
||||
else {return 3;}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,45 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class Skeleton {
|
||||
// TODO: DESIGN ENEMY AND IMPLEMENT THE CONSTRUCTOR
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Skeleton extends Enemy {
|
||||
|
||||
private static final String RESET = "\033[0m";
|
||||
|
||||
private static final String PURPLE = "\033[0;35m";
|
||||
|
||||
|
||||
private boolean hasResurrected = false;
|
||||
|
||||
public Skeleton(Weapon weapon) {
|
||||
super("Skeleton", 160, 20, weapon, 0.3);
|
||||
this.xpReward = 100;
|
||||
this.coinReward = 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage, Entity attacker) {
|
||||
super.takeDamage(damage, attacker);
|
||||
|
||||
if (!isAlive() && !hasResurrected) {
|
||||
fillHealth(getMaxHP() / 2);
|
||||
this.hasResurrected = true;
|
||||
System.out.println(name + " Falls To The Ground... But Then" + PURPLE + " Resurrects From The Dead!" + RESET);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int choice() {
|
||||
double roll = Math.random();
|
||||
|
||||
if (this.getStamina() >= this.weapon.getStaminaCost()) {
|
||||
if (roll < 0.33) {return 1;}
|
||||
else if (roll < 0.67) {return 2;}
|
||||
else {return 3;}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.Weapon;
|
||||
import org.project.item.Item;
|
||||
|
||||
public class Vampire extends Enemy {
|
||||
|
||||
public Vampire(Weapon weapon) {
|
||||
super("Vampire", 180, 20, weapon, 0.3);
|
||||
this.xpReward = 120;
|
||||
this.coinReward = 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightAttack(Entity target) {
|
||||
super.lightAttack(target);
|
||||
fillHealth(5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heavyAttack(Entity target) {
|
||||
super.heavyAttack(target);
|
||||
int lifeStolen = weapon.getDamage() / 4;
|
||||
fillHealth(lifeStolen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int choice() {
|
||||
double roll = Math.random();
|
||||
|
||||
|
||||
if (this.getStamina() >= this.weapon.getStaminaCost()) {
|
||||
if (roll < 0.33) {return 1;}
|
||||
else if (roll < 0.67) {return 2;}
|
||||
else {return 3;}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.project.entity.players;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.armors.Armor;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Assassin extends Player {
|
||||
|
||||
private boolean dodgeNext = false;
|
||||
private boolean counterReady = false;
|
||||
|
||||
public Assassin(String name, Weapon weapon, Armor armor) {
|
||||
super(name, 60, 50, weapon, armor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightAttack(Entity target) {
|
||||
if (counterReady) {
|
||||
target.takeDamage(30, this);
|
||||
System.out.println(name + " Performs a Light Attack After Dodging Enemy's Attack!");
|
||||
counterReady = false;
|
||||
} else {
|
||||
super.lightAttack(target);
|
||||
dodgeNext = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heavyAttack(Entity target) {
|
||||
if (spendStamina(weapon.getStaminaCost())) {
|
||||
if (counterReady) {
|
||||
target.takeDamage(weapon.getDamage() * 2, this);
|
||||
System.out.println(name + " Performs a Heavy Attack After Dodging Enemy's Attack!");
|
||||
counterReady = false;
|
||||
} else {
|
||||
super.heavyAttack(target);
|
||||
dodgeNext = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage, Entity attacker) {
|
||||
if (dodgeNext) {
|
||||
System.out.println(name + " Dodged Enemy's Attack!");
|
||||
dodgeNext = false;
|
||||
counterReady = true;
|
||||
return;
|
||||
}
|
||||
super.takeDamage(damage, attacker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
if (spendStamina(20)) {
|
||||
this.dodgeNext = true;
|
||||
this.counterReady = false;
|
||||
System.out.println(name + " Gets Ready To Dodge Enemy's Attack...");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,21 @@
|
||||
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.item.armors.Armor;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Knight extends Player {
|
||||
|
||||
public Knight(String name, Weapon weapon, Armor armor) {
|
||||
super(name, 80, 20, weapon, armor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
if (spendStamina(20)) {
|
||||
target.takeDamage(weapon.getDamage() * 2, this);
|
||||
target.setStunned(true);
|
||||
System.out.println(name + " Strikes The Enemy With His Sword!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,64 +3,160 @@ package org.project.entity.players;
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.armors.Armor;
|
||||
import org.project.item.weapons.Weapon;
|
||||
import org.project.item.Inventory;
|
||||
|
||||
|
||||
public abstract class Player implements Entity {
|
||||
|
||||
private static final String RESET = "\033[0m";
|
||||
|
||||
private static final String GREEN = "\033[0;32m";
|
||||
private static final String BLUE = "\033[0;34m";
|
||||
private static final String YELLOW = "\033[0;33m";
|
||||
private static final String PURPLE = "\033[0;35m";
|
||||
|
||||
|
||||
// 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;
|
||||
protected int hp;
|
||||
protected int maxHP;
|
||||
protected int stamina;
|
||||
protected int maxStamina;
|
||||
|
||||
public Player(String name, int hp, int mp, Weapon weapon, Armor armor) {
|
||||
private int xp = 0;
|
||||
private int level = 1;
|
||||
private int xpToNextLevel = 100;
|
||||
private int coins = 0;
|
||||
|
||||
protected boolean canDefend = true;
|
||||
protected boolean isDefending = false;
|
||||
protected boolean isStunned = false;
|
||||
|
||||
public Armor armor;
|
||||
public Weapon weapon;
|
||||
protected Inventory inventory;
|
||||
|
||||
private boolean hasGoblinKey = false;
|
||||
private boolean hasSkeletonKey = false;
|
||||
private boolean hasVampireKey = false;
|
||||
|
||||
|
||||
public Player(String name, int hp, int stamina, Weapon weapon, Armor armor) {
|
||||
this.name = name;
|
||||
this.hp = hp;
|
||||
this.mp = mp;
|
||||
|
||||
this.maxHP = hp;
|
||||
this.stamina = stamina;
|
||||
this.maxStamina = stamina;
|
||||
this.weapon = weapon;
|
||||
this.armor = armor;
|
||||
this.inventory = new Inventory(this);
|
||||
|
||||
this.inventory.addWeapon(weapon);
|
||||
this.inventory.addArmor(armor);
|
||||
}
|
||||
|
||||
public void setCanDefend(boolean status) {
|
||||
this.canDefend = status;
|
||||
}
|
||||
|
||||
public boolean isDefending() {
|
||||
return isDefending;
|
||||
}
|
||||
|
||||
public boolean isStunned() {
|
||||
return isStunned;
|
||||
}
|
||||
|
||||
public void setStunned(boolean status) {
|
||||
this.isStunned = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attack(Entity target) {
|
||||
target.takeDamage(weapon.getDamage());
|
||||
public void lightAttack(Entity target) {
|
||||
target.takeDamage(20, this);
|
||||
System.out.println(name + " Performs a Light Attack!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heavyAttack(Entity target) {
|
||||
if (spendStamina(weapon.getStaminaCost())) {
|
||||
target.takeDamage(weapon.getDamage(), this);
|
||||
System.out.println(name + " Performs a Heavy Attack!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defend() {
|
||||
// TODO
|
||||
if (!canDefend) {
|
||||
System.out.println("You Can't Defend Against The Dragon!");
|
||||
} else {
|
||||
if (spendStamina(5)) {
|
||||
this.isDefending = true;
|
||||
System.out.println(name + " Gets Ready To Block Enemy's Attack...");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage) {
|
||||
hp -= damage - armor.getDefense();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heal(int health) {
|
||||
hp += health;
|
||||
if (hp > maxHP) {
|
||||
hp = maxHP;
|
||||
if (spendStamina(10)) {
|
||||
this.hp = Math.min(maxHP, this.hp + health);
|
||||
System.out.println(name + " Healed " + health + " HP!");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void specialAbility(Entity target);
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage, Entity attacker) {
|
||||
if (isDefending) {
|
||||
if (!attacker.isDefending()) {
|
||||
System.out.println(name + " Defended Enemy's Attack!");
|
||||
}
|
||||
isDefending = false;
|
||||
return;
|
||||
}
|
||||
int dmg = armor.damageTaken(damage);
|
||||
this.hp = Math.max(0, this.hp - dmg);
|
||||
armor.reduceDurability(dmg / 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillMana(int mana) {
|
||||
mp += mana;
|
||||
if (mp > maxMP) {
|
||||
mp = maxMP;
|
||||
}
|
||||
public void fillHealth(int health) {
|
||||
this.hp = Math.min(maxHP, this.hp + health);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStamina(int stamina) {
|
||||
this.stamina = Math.min(maxStamina, this.stamina + stamina);
|
||||
}
|
||||
|
||||
public void fillStats() {
|
||||
this.hp = maxHP;
|
||||
this.stamina = maxStamina;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spendStamina(int cost) {
|
||||
if (this.stamina >= cost) {
|
||||
this.stamina -= cost;
|
||||
return true;
|
||||
}
|
||||
System.out.println("Not Enough Stamina!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return this.hp > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
@Override
|
||||
public int getHP() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
@@ -69,13 +165,14 @@ public abstract class Player {
|
||||
return maxHP;
|
||||
}
|
||||
|
||||
public int getMp() {
|
||||
return mp;
|
||||
@Override
|
||||
public int getStamina() {
|
||||
return stamina;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMP() {
|
||||
return maxMP;
|
||||
public int getMaxStamina() {
|
||||
return maxStamina;
|
||||
}
|
||||
|
||||
public Weapon getWeapon() {
|
||||
@@ -86,4 +183,89 @@ public abstract class Player {
|
||||
return armor;
|
||||
}
|
||||
|
||||
public int getXP() {
|
||||
return xp;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getCoins() {
|
||||
return coins;
|
||||
}
|
||||
|
||||
public void equipWeapon(int index) {
|
||||
Weapon newWeapon = inventory.getWeapon(index);
|
||||
this.weapon = newWeapon;
|
||||
}
|
||||
|
||||
public void equipArmor(int index) {
|
||||
Armor newArmor = inventory.getArmor(index);
|
||||
this.armor = newArmor;
|
||||
}
|
||||
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public void gainXP(int amount) {
|
||||
this.xp += amount;
|
||||
System.out.println("Gained " + YELLOW + amount + RESET + " XP.");
|
||||
while (xp >= xpToNextLevel) {
|
||||
levelUp();
|
||||
}
|
||||
}
|
||||
|
||||
private void levelUp() {
|
||||
int oldHP = this.maxHP;
|
||||
int oldStamina = this.maxStamina;
|
||||
level++;
|
||||
xpToNextLevel = (int) (xpToNextLevel * 2.5);
|
||||
|
||||
|
||||
this.maxHP += 20;
|
||||
this.maxStamina += 10;
|
||||
fillStats();
|
||||
|
||||
System.out.println(PURPLE + "LEVEL UP!" + RESET + " Level: " + level );
|
||||
System.out.println("HP: " + oldHP + " -> " + GREEN + maxHP + RESET + " | Stamina: " + oldStamina + " -> " + BLUE + maxStamina + RESET);
|
||||
System.out.println("XP: " + xp + " | XP To Next Level: " + xpToNextLevel);
|
||||
}
|
||||
|
||||
public void addCoins(int amount) {
|
||||
this.coins += amount;
|
||||
System.out.println("Found " + YELLOW + amount + RESET + " Coins.");
|
||||
}
|
||||
|
||||
public void spendCoins(int amount) {
|
||||
this.coins -= amount;
|
||||
}
|
||||
|
||||
public boolean hasGoblinKey() {
|
||||
return hasGoblinKey;
|
||||
}
|
||||
|
||||
public boolean hasSkeletonKey() {
|
||||
return hasSkeletonKey;
|
||||
}
|
||||
|
||||
public boolean hasVampireKey() {
|
||||
return hasVampireKey;
|
||||
}
|
||||
|
||||
public void findGoblinKey(){
|
||||
hasGoblinKey = true;
|
||||
System.out.println("Found The" + PURPLE + " Goblin" + RESET + " Key!");
|
||||
}
|
||||
|
||||
public void findSkeletonKey(){
|
||||
hasSkeletonKey = true;
|
||||
System.out.println("Found The" + PURPLE + " Skeleton" + RESET + " Key!");
|
||||
}
|
||||
|
||||
public void findVampireKey(){
|
||||
hasVampireKey = true;
|
||||
System.out.println("Found The" + PURPLE + " Vampire" + RESET + " Key!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.project.entity.players;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.armors.Armor;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Wizard extends Player {
|
||||
|
||||
public Wizard(String name, Weapon weapon, Armor armor) {
|
||||
super(name, 100, 30, weapon, armor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
if (spendStamina(20)) {
|
||||
target.takeDamage(weapon.getDamage() * 2, this);
|
||||
if (!target.isDefending()) {
|
||||
this.fillHealth(getMaxHP() / 5);
|
||||
System.out.println(name + " Recites The Special Phrase!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.project.item;
|
||||
|
||||
import org.project.entity.players.Player;
|
||||
import org.project.item.armors.Armor;
|
||||
import org.project.item.weapons.Weapon;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Inventory {
|
||||
private Player player;
|
||||
|
||||
public Inventory(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
private ArrayList<Weapon> weapons = new ArrayList<>();
|
||||
private ArrayList<Armor> armors = new ArrayList<>();
|
||||
private ArrayList<Item> miscItems = new ArrayList<>();
|
||||
|
||||
public ArrayList<Weapon> getWeapons() {
|
||||
return weapons;
|
||||
}
|
||||
|
||||
public ArrayList<Armor> getArmors() {
|
||||
return armors;
|
||||
}
|
||||
|
||||
public void addWeapon(Weapon weapon) {
|
||||
weapons.add(weapon);
|
||||
}
|
||||
|
||||
public void addArmor(Armor armor) {
|
||||
armors.add(armor);
|
||||
}
|
||||
|
||||
public void addMiscItem(Item newItem) {
|
||||
for (int i = 0; i < miscItems.size(); i++) {
|
||||
Item existingItem = miscItems.get(i);
|
||||
|
||||
if (existingItem.getName().equals(newItem.getName())) {
|
||||
existingItem.addQuantity(newItem.getQuantity());
|
||||
return;
|
||||
}
|
||||
}
|
||||
miscItems.add(newItem);
|
||||
}
|
||||
|
||||
public void showMiscItems() {
|
||||
int listNumber = 1;
|
||||
|
||||
for (int i = 0; i < miscItems.size(); i++) {
|
||||
Item currentItem = miscItems.get(i);
|
||||
|
||||
if (currentItem.getName().contains("Healing Flask") && currentItem.getQuantity() > 0) {
|
||||
System.out.println(listNumber + ". " + currentItem.getName() + " (" + currentItem.getQuantity() + ")");
|
||||
listNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < miscItems.size(); i++) {
|
||||
Item currentItem = miscItems.get(i);
|
||||
|
||||
if (currentItem.getName().contains("Stamina") && currentItem.getQuantity() > 0) {
|
||||
System.out.println(listNumber + ". " + currentItem.getName() + " (" + currentItem.getQuantity() + ")");
|
||||
listNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Weapon getWeapon(int index) {
|
||||
return weapons.get(index);
|
||||
}
|
||||
|
||||
public Armor getArmor(int index) {
|
||||
return armors.get(index);
|
||||
}
|
||||
|
||||
public Item getMiscItemByName(String baseName) {
|
||||
for (int i = 0; i < miscItems.size(); i++) {
|
||||
Item item = miscItems.get(i);
|
||||
if (item.getName().contains(baseName)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasAllKeys() {
|
||||
return player.hasGoblinKey() && player.hasSkeletonKey() && player.hasVampireKey();
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,13 @@ package org.project.item;
|
||||
import org.project.entity.Entity;
|
||||
|
||||
public interface Item {
|
||||
void use(Entity target);
|
||||
String getName();
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS METHODS
|
||||
*/
|
||||
int getQuantity();
|
||||
|
||||
void addQuantity(int amount);
|
||||
|
||||
boolean canUse();
|
||||
|
||||
void use(Entity target);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,50 @@
|
||||
package org.project.item.armors;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Armor {
|
||||
private String name;
|
||||
private int defense;
|
||||
private int maxDefense;
|
||||
private int durability;
|
||||
private int maxDurability;
|
||||
private boolean isBroken;
|
||||
|
||||
private boolean isBroke;
|
||||
|
||||
public Armor(int defense, int durability) {
|
||||
public Armor(String name, int defense) {
|
||||
this.name = name;
|
||||
this.defense = defense;
|
||||
this.durability = durability;
|
||||
this.durability = 100;
|
||||
this.maxDurability = durability;
|
||||
this.isBroken = false;
|
||||
}
|
||||
|
||||
public int damageTaken(int damage) {
|
||||
if (isBroken) {
|
||||
return damage;
|
||||
}
|
||||
double percent = (100.0 - defense) / 100.0;
|
||||
return (int) (damage * percent);
|
||||
}
|
||||
|
||||
public void reduceDurability(int amount) {
|
||||
this.durability -= amount;
|
||||
checkBreak();
|
||||
}
|
||||
|
||||
|
||||
public void checkBreak() {
|
||||
if (durability <= 0) {
|
||||
isBroke = true;
|
||||
defense = 0;
|
||||
if (this.durability <= 0 && !isBroken) {
|
||||
this.durability = 0;
|
||||
this.isBroken = true;
|
||||
System.out.println("Your Armor Has Broken!");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: (BONUS) UPDATE THE REPAIR METHOD
|
||||
|
||||
public void repair() {
|
||||
isBroke = false;
|
||||
defense = maxDefense;
|
||||
durability = maxDurability;
|
||||
this.isBroken = false;
|
||||
this.durability = 100;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getDefense() {
|
||||
@@ -35,8 +54,4 @@ public abstract class Armor {
|
||||
public int getDurability() {
|
||||
return durability;
|
||||
}
|
||||
|
||||
public boolean isBroke() {
|
||||
return isBroke;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.project.item.armors;
|
||||
|
||||
public class AssassinArmor extends Armor {
|
||||
|
||||
public AssassinArmor(String name, int defense) {
|
||||
super(name, defense);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.project.item.armors;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class KnightArmor {
|
||||
// TODO: DESIGN ARMOR'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
|
||||
public class KnightArmor extends Armor {
|
||||
|
||||
public KnightArmor(String name, int defense) {
|
||||
super(name, defense);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.project.item.armors;
|
||||
|
||||
public class WizardArmor extends Armor {
|
||||
|
||||
public WizardArmor(String name, int defense) {
|
||||
super(name, defense);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
public abstract class Consumable implements Item {
|
||||
protected String name;
|
||||
protected int quantity;
|
||||
|
||||
public Consumable(String name, int quantity) {
|
||||
this.name = name;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQuantity(int amount) {
|
||||
this.quantity += amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return quantity > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void use(Entity target);
|
||||
}
|
||||
|
||||
@@ -2,15 +2,38 @@ package org.project.item.consumables;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class Flask {
|
||||
/*
|
||||
THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN.
|
||||
*/
|
||||
public class Flask extends Consumable {
|
||||
|
||||
public Flask(int quantity) {
|
||||
super("Healing Flask", quantity);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQuantity() {
|
||||
return super.getQuantity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQuantity(int amount) {
|
||||
this.quantity += amount;
|
||||
}
|
||||
|
||||
// TODO: UPDATE USE METHOD
|
||||
@Override
|
||||
public void use(Entity target) {
|
||||
target.heal(target.getMaxHP() / 10);
|
||||
if (canUse()) {
|
||||
int healAmount = target.getMaxHP() / 4;
|
||||
target.fillHealth(healAmount);
|
||||
quantity--;
|
||||
System.out.println(target.getName()+ " Used Healing Flask And Healed " + healAmount + " HP!");
|
||||
System.out.println("Remaining: " + quantity);
|
||||
} else {
|
||||
System.out.println("The Flask Is Empty!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.project.item.consumables;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
|
||||
public class StaminaPotion extends Consumable {
|
||||
|
||||
public StaminaPotion(int quantity) {
|
||||
super("Stamina Potion", quantity);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQuantity() {
|
||||
return super.getQuantity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQuantity(int amount) {
|
||||
this.quantity += amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void use(Entity target) {
|
||||
if (canUse()) {
|
||||
int staminaAmount = target.getMaxStamina() / 4;
|
||||
target.fillStamina(staminaAmount);
|
||||
quantity--;
|
||||
System.out.println(target.getName()+ " Used Stamina Potion And Added " + staminaAmount + " To His Stamina!");
|
||||
System.out.println("Remaining: " + quantity);
|
||||
} else {
|
||||
System.out.println("The Flask Is Empty!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class Dagger extends Weapon{
|
||||
|
||||
public Dagger(String name, int damage, int staminaCost) {
|
||||
super(name, damage, staminaCost);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class Flame extends Weapon{
|
||||
|
||||
public Flame(String name, int damage, int staminaCost) {
|
||||
super(name, damage, staminaCost);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class HiddenBlade extends Weapon{
|
||||
|
||||
public HiddenBlade(String name, int damage, int staminaCost) {
|
||||
super(name, damage, staminaCost);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class Staff extends Weapon{
|
||||
|
||||
public Staff(String name, int damage, int staminaCost) {
|
||||
super(name, damage, staminaCost);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,8 @@
|
||||
package org.project.item.weapons;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
public class Sword extends Weapon {
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class Sword {
|
||||
/*
|
||||
THIS IS AN EXAMPLE OF A WEAPON DESIGN.
|
||||
*/
|
||||
|
||||
int abilityCharge;
|
||||
|
||||
public Sword() {
|
||||
// TODO: DESIGN SWORD'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
|
||||
}
|
||||
|
||||
// TODO: (BONUS) UPDATE THE UNIQUE ABILITY
|
||||
public void uniqueAbility(ArrayList<Entity> targets) {
|
||||
abilityCharge += 2;
|
||||
for (Entity target : targets) {
|
||||
target.takeDamage(getDamage());
|
||||
}
|
||||
public Sword(String name, int damage, int staminaCost) {
|
||||
super(name, damage, staminaCost);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,25 @@
|
||||
package org.project.item.weapons;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Weapon {
|
||||
private String name;
|
||||
private int damage;
|
||||
private int manaCost;
|
||||
private int staminaCost;
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS ATTRIBUTES
|
||||
*/
|
||||
|
||||
public Weapon(int damage, int manaCost) {
|
||||
public Weapon(String name, int damage, int staminaCost) {
|
||||
this.name = name;
|
||||
this.damage = damage;
|
||||
this.manaCost = manaCost;
|
||||
this.staminaCost = staminaCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void use(Entity target) {
|
||||
target.takeDamage(damage);
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getDamage() {
|
||||
return damage;
|
||||
}
|
||||
|
||||
public int getManaCost() {
|
||||
return manaCost;
|
||||
public int getStaminaCost() {
|
||||
return staminaCost;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS METHODS
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
package org.project.location;
|
||||
|
||||
import org.project.entity.enemies.Enemy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
public class Location {
|
||||
private String name;
|
||||
|
||||
private ArrayList<Enemy> enemies;
|
||||
private Random random = new Random();
|
||||
|
||||
public Location(ArrayList<Location> locations, ArrayList<Enemy> enemies) {
|
||||
this.locations = locations;
|
||||
public Location(String name, ArrayList<Enemy> enemies) {
|
||||
this.name = name;
|
||||
this.enemies = enemies;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ArrayList<Location> getLocations() {
|
||||
return locations;
|
||||
}
|
||||
|
||||
public ArrayList<Enemy> getEnemies() {
|
||||
return enemies;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,175 +1,127 @@
|
||||
# Fourth Assignment - Java Knight ⚔️
|
||||
A turn-based RPG with Roguelike elements which can be run in the terminal.
|
||||
# Java Knight ⚔️
|
||||
Welcome to **Java Knight**, a turn-based RPG with Roguelike elements which can be run in the terminal.
|
||||
|
||||
### **Prologue: The Legend of Javanest**
|
||||
*For centuries, the land of Javanest lived in peace, until a magical Dragon attacked, plunging the realm into absolute darkness. With a wicked curse, the Dragon transformed the innocent people into horrific monsters: Goblins, Skeletons, and Vampires. Retreating to its impenetrable Castle, the Dragon divided the three keys to its lair and hid them among these cursed creatures. Now, it is your duty to step up and save the land. You must battle these monsters, recover the unique key from each monster type, and finally slay the Dragon to break the curse and restore peace to Javanest!*
|
||||
|
||||
### **Introduction**
|
||||
Welcome to **Java knight**, a turn-based RPG inspired by Roguelike games! In this assignment, you will develop a **text-based role-playing game (RPG)**. This project is designed to rigorously test your understanding of **Object-Oriented Programming (OOP) principles**.
|
||||
|
||||
⚠️ **REQUIREMENT:** You **must** utilize all the OOP concepts you have learned so far—including *Inheritance, Interfaces, Abstract Classes, Encapsulation, Polymorphism, Overloading, and Overriding*. It is extremely important that you use everything in its right place. Your design and architecture will be graded based on how well you apply these principles to avoid code duplication and maintain a clean structure.
|
||||
|
||||
🎯 **Your goal is not just to complete the assignment but to learn and apply OOP effectively!**
|
||||
|
||||
### **What is a Turn-Based Game?**
|
||||
In this combat system, two sides - which are usually the player's side and the enemy's side - attack each other in turns. The side which is not attacking can perform actions to avoid or deflect the enemy's attack.
|
||||
|
||||
### **Core Mechanics:**
|
||||
- **Turn-based combat** – Players and monsters take turns attacking each other.
|
||||
- **Character classes with Unique Traits** – Players can choose from archetypes like **Knight, Assassin, or Wizard**, each starting with distinctly different base stats.
|
||||
- **Unified Mana/Stamina System** – All player classes use a unified resource (Mana/Stamina) to perform actions.
|
||||
- **Standardized Action Set** – Every player character has exactly 5 specific actions available during their turn.
|
||||
- **Experience & Leveling System** – Earn XP based on enemy strength to automatically level up and increase your base stats.
|
||||
- **Progression System** – You cannot fight the Dragon immediately. You must farm enemies for a chance to drop their specific key, collect all three, and grow stronger first.
|
||||
### Prologue: The Legend of Javanest 📜
|
||||
*For centuries, the land of Javanest lived in peace, until a magical Dragon attacked, plunging the realm into absolute darkness. With a wicked curse, the Dragon transformed the innocent people into horrific monsters: Goblins, Skeletons, and Vampires. Retreating to its impenetrable Castle, the Dragon divided the three keys to its lair and hid them among these cursed creatures. Now, it is your duty to step up and save the land! You must navigate through dangerous encounters, recover the unique key from each monster type, and slay the Dragon to break the curse and restore peace to Javanest!*
|
||||
|
||||
---
|
||||
|
||||
## Tasks 📝
|
||||
## How The Game Works 🎮
|
||||
|
||||
### 1️⃣ Step 1: Fork & Setup 🍴
|
||||
1. **Fork** this repository and clone it to your local machine.
|
||||
```bash
|
||||
git clone https://git.meshcomp.ir/AdvancedProgramming1404/HW-04-JAVA-KNIGHT.git
|
||||
```
|
||||
2. Create a new branch named `develop` and switch to it.
|
||||
```bash
|
||||
git checkout -b develop
|
||||
```
|
||||
### 2️⃣ Step 2: Implement the Class Hierarchy 🌲
|
||||
- **The Core Loop:** The game starts with the player spawning at his camp. He can then choose to:
|
||||
- **1. Enter the portal:** Which will lead either to a random standard location (`Forest`, `Graveyard`, or `Crypt`), or to the Dragon's castle based on the player's choice.
|
||||
- **2. Visit the Merchant:** To buy and store Weapons, Armors and different Consumables.
|
||||
|
||||
A well-structured OOP hierarchy is crucial. Avoid duplicating code by placing shared logic in abstract classes.
|
||||
|
||||
- **Entities & Locations:** You have `Entity`, `Item`(Bonus) , and `Location`.
|
||||
- **Players:** `Player` is an abstract class implementing `Entity`. Subclasses: `Wizard`, `Knight`, `Assassin`.
|
||||
- **Base Stat Differences:** Each class must have distinct starting stats. For example:
|
||||
- **Knight:** Highest Base Damage.
|
||||
- **Wizard:** Highest Max Health (HP).
|
||||
- **Assassin:** Highest Max Stamina/Mana.
|
||||
- **Enemies:** `Enemy` is an abstract class implementing `Entity`. Subclasses: `Skeleton`, `Goblin`, `Vampire`, and **`Dragon`**.
|
||||
- **The Boss:** Even though `Dragon` is the final boss, it **must** be a subclass of `Enemy` to inherit common combat properties, while possessing extremely high stats and unique mechanics.
|
||||
- **Item (Bonus):** `Consumable`, `Armor`, `Weapon` are abstract classes implementing `Item`. example :
|
||||
- KnightArmor extends Armor - you can add more subclasses of Armor for extra score
|
||||
- Sword extends Weapon - you can add more subclasses of Weapon for extra score
|
||||
- Flask extends Consumable - you can add more subclasses of Consumable for extra score
|
||||
- **Player Choices:** Before engaging in a fight and after spawning at a random location, the player is presented with the following options:
|
||||
- **1. Fight the enemy:** Enter the turn-based combat sequence.
|
||||
- **2. Move to another location:** Skip the current enemy and spawn a new random one.
|
||||
- **Going to the Castle to fight the Dragon:** The player may enter the Dragon's castle only if he has all 3 unique keys.
|
||||
|
||||
|
||||
- **The Key Drop Logic (RNG Gatekeeping):**
|
||||
- When the player defeats an enemy, there is a **specific percentage chance** that it will drop the unique key associated with its species (Goblin Key, Skeleton Key, Vampire Key).
|
||||
- **One Key Per Species:** Once a player obtains a specific key (e.g., Goblin Key), subsequent enemies of that same type (other Goblins) will **never** drop a key again.
|
||||
|
||||
|
||||
- **Experience & Leveling System:**
|
||||
- Defeating an enemy grants **XP** and some **Coins**. The amount of XP and Coins scale proportionally to the enemy's power level.
|
||||
- Upon reaching an XP threshold, the player levels up. **Leveling up automatically increases the player's Max HP and Max Stamina**, making them strong enough to eventually face the Dragon.
|
||||
|
||||
|
||||
- **Post-Combat Recovery:** After each successful battle, the player has the choice to either continue fighting enemies, or to return to his camp to recover HP and Stamina.
|
||||
|
||||
|
||||
- **Final Boss Fight:** Once all three unique keys are collected, the player can unlock the Castle gates to face the Dragon. Defeating the Dragon breaks the curse, resulting in **Victory**. Dying at any point results in **Game Over**.
|
||||
|
||||
---
|
||||
|
||||
## Core Mechanics ⚙️
|
||||
|
||||
- **Turn-based combat** – The player and monsters take turns attacking each other or defending themselves. Each entity performs one of these 4 possible moves during their turn:
|
||||
- **1. Light Attack:** Deals basic damage, no Stamina cost.
|
||||
- **2. Heavy Attack:** Deals high damage, medium Stamina cost.
|
||||
- **3. Defend:** Completely blocks the enemy's *next* strike, medium Stamina cost.
|
||||
- **4. Special Ability:** A unique class-based ultimate move, high Stamina cost (available only to the player and the final boss).
|
||||
|
||||
In addition to these moves, the player can also use consumables mid-fight without using their turn.
|
||||
|
||||
|
||||
- **Character classes with Unique Traits** – Players can choose from archetypes like **Knight, Assassin, or Wizard**, each starting with distinctly different base stats:
|
||||
- **Knight** ⚔️: Highest Base Damage.
|
||||
- **Special:** Performs a shield bash that stuns the enemy, forcing them to skip their next turn while dealing heavy damage.
|
||||
- **Assassin** 🗡️: Highest Max Stamina.
|
||||
- **Special:** Turns invisible, dodging the next incoming attack completely and guaranteeing a Critical Hit on their next turn.
|
||||
- **Wizard** 🧙♂️: Highest Max Health (HP).
|
||||
- **Special:** Casts a devastating spell that damages the enemy while simultaneously replenishing some HP.
|
||||
|
||||
|
||||
- **Monsters' Attributes:**
|
||||
- **Goblin** 👹: High heavy attack chance but low health.
|
||||
- **Skeleton** 💀: Can resurrect once per battle with 50% HP.
|
||||
- **Vampire** 🧛♂️: Life steal ability – a portion of the damage it deals to the player is added back to its own health.
|
||||
- **Dragon (Final Boss)** 🐉: Immune to normal defense. Its fiery breath bypasses shields and deals massive damage.
|
||||
|
||||
---
|
||||
|
||||
## OOP Principles Used 📋
|
||||
|
||||
This project is made using Object-Oriented Programming (OOP) Principles. Here is a brief explanation of how and where these concepts are applied:
|
||||
|
||||
### 1️⃣ Encapsulation
|
||||
**Encapsulation** means restricting direct access to an object's internal data by using `private` (or in some cases `protected`) modifiers, and requiring all access and interactions to happen through public methods.
|
||||
|
||||
For example, variables like `hp`, `stamina`, or armor's `durability` cannot be directly viewed or changed by external classes. Instead, we use getter/setter methods to access these variables and methods like `takeDamage()`, `spendStamina()` and `reduceDurability()` to handle the logic.
|
||||
|
||||
|
||||
### 2️⃣ Inheritance
|
||||
**Inheritance** means deriving new classes from existing ones, allowing child classes to automatically contain fields and methods from its parent class without rewriting duplicate code.
|
||||
|
||||
We use inheritance when we want a class to include the general logic of another class, but with some of its features defined more specifically.
|
||||
|
||||
For example, `Knight`, `Assassin` or `Wizard` all inherit the general mechanics of the `Player` class, but each subclass specifies its own unique base stats and special abilities.
|
||||
|
||||
|
||||
### 3️⃣ Abstraction
|
||||
**Abstraction** means defining a class's base structure, while leaving the specific execution details for later.
|
||||
|
||||
We can achieve and use abstraction in two ways:
|
||||
|
||||
- **1. Through Abstract Classes:** For example, `Player` and `Enemy` classes are abstract. They contain the base structure and important logic needed, but each subclass define its own unique details in its own way (like `specialAbility()` for `Player` subclasses and `choice()` for `Enemy` subclasses).
|
||||
|
||||
|
||||
- **2. Through Interfaces:** For example, The `Entity` interface acts as a strict contract which ensures that any implementing class (like `Player` and `Enemy`) MUST include behaviors like `lightAttack()`, `defend()` and any methods mentioned in the interface. This allows the game to handle players and monsters the same way.
|
||||
|
||||
|
||||
### 4️⃣ Polymorphism
|
||||
**Polymorphism** means different objects responding differently to the exact same method call. Java automatically figures out which specific version of the method to run at runtime based on the object's active subclass type.
|
||||
|
||||
For example, when the combat loop executes `player.specialAbility(enemy);`, Java automatically determines which version to execute: the Knight’s shield, the Assassin’s strike, or the Wizard’s spell.
|
||||
|
||||

|
||||
|
||||
### 3️⃣ Step 3: Implement Player & Monster Methods 🏹
|
||||
---
|
||||
|
||||
**Player Actions (The Rule of Five):**
|
||||
Every player class **must** implement exactly the following 5 actions (You can use an interface like `ICombatActions`). Every action (except Light Attack) consumes a specific amount of Mana/Stamina. The **Special Ability** must consume the *highest* amount of Mana compared to the others.
|
||||
1. **Light Attack:** Deals moderate damage and costs **NO Mana**. (Note: If the player runs out of Mana/Stamina, this is the ONLY action they can perform.)
|
||||
2. **Heavy Attack:** Deals high damage, medium Mana cost.
|
||||
3. **Defend:** Completely blocks or significantly reduces the damage of the enemy's *next* strike. Medium Mana cost.
|
||||
4. **Heal:** Restores a portion of the player's HP. Medium-high Mana cost.
|
||||
5. **Special Ability:** A unique class-based ultimate move (Highest Mana cost):
|
||||
- **Wizard** 🧙♂️: Casts a devastating spell that damages the enemy while simultaneously replenishing some HP.
|
||||
- **Assassin** 🗡️: Turns invisible, dodging the next incoming attack completely and guaranteeing a *Critical Hit* on their next turn.
|
||||
- **Knight** ⚔️: Performs a shield bash that stuns the enemy, forcing them to skip their next turn while dealing heavy damage.
|
||||
## Additional Features ⭐
|
||||
|
||||
**Monster Abilities:**
|
||||
- **Goblin** 👹: High critical hit chance but low health.
|
||||
- **Skeleton** ☠️: Can resurrect once per battle with 50% HP.
|
||||
- **Vampire** 🦇: Lifesteal ability – a portion of the damage it deals to the player is added back to its own health.
|
||||
- **Dragon (Final Boss)** 🐉: Immune to normal defense. Its fiery breath bypasses shields and deals massive damage.
|
||||
- ### Merchant System & Inventory Management:
|
||||
Coins you earn from defeated enemies can be spent at the Camp merchant to purchase, store, and use different Weapons, Armors, and Consumables like Healing Flasks and Stamina Potions!
|
||||
|
||||
🔹 Make sure each entity **prints messages** when performing actions. example output (while in combat) :
|
||||
|
||||
```bash
|
||||
You chose to FIGHT!
|
||||
|
||||
[Ser Duncan - 45/45 HP | 40/40 Mana]
|
||||
[Goblin - 30/30 HP]
|
||||
- ### Enhanced Console UX:
|
||||
ANSI escape codes are used to provide color-coded narrative output.
|
||||
|
||||
---
|
||||
|
||||
Your Turn:
|
||||
1. Light Attack | 2. Heavy Attack (-8 Mana) | 3. Defend (-6 Mana) | 4. Heal (-12 Mana) | 5. Shield Bash (-15 Mana)
|
||||
## How To Run And Play 🕹️
|
||||
|
||||
1. **Fork** this repository and clone it to your local machine:
|
||||
```
|
||||
|
||||
```bash
|
||||
→ Chose: Light Attack
|
||||
⚔️ Ser Duncan (Knight) used Light Attack! (0 Mana)
|
||||
Goblin took 10 damage!
|
||||
Goblin has 20/30 HP remaining.
|
||||
Ser Duncan Mana: 40/40
|
||||
```
|
||||
```
|
||||
Goblin's Turn :
|
||||
👹 Goblin used Critical Strike!
|
||||
💥 Critical hit! Ser Duncan took 20 damage!
|
||||
Ser Duncan has 25/45 HP remaining.
|
||||
```
|
||||
🔹 *Narrative Console:* Use ANSI escape codes to print colorful narrative logs (e.g., Red for damage, Blue for Mana usage, Green for healing).
|
||||
git clone https://git.meshcomp.ir/Soroush/Assignment-4.git
|
||||
````
|
||||
2. **Open** the project folder in your preferred Java IDE.
|
||||
|
||||
|
||||
### 4️⃣ Step 4: Implement the Game Loop & Progression 🎮
|
||||
|
||||
1. **The Core Loop:** The game starts with the player entering a location. A random standard enemy (`Goblin`, `Skeleton`, or `Vampire`) spawns immediately.
|
||||
2. **Player Choices:** Before engaging, the player is presented with the following options:
|
||||
- **1. Fight the enemy:** Enter the turn-based combat sequence.
|
||||
- **2. Move to another location:** Skip the current enemy and spawn a new random one.
|
||||
- **3. Go to the Castle to fight the Dragon:** *(Note: This option must remain strictly hidden or locked until the player has successfully collected all 3 keys).*
|
||||
3. **The Key Drop Logic (RNG Gatekeeping):**
|
||||
- When the player defeats an enemy, there is a **specific percentage chance (e.g., 20%)** that it will drop the unique key associated with its species (Goblin Key, Skeleton Key, Vampire Key).
|
||||
- **One Key Per Species:** Once a player obtains a specific key (e.g., Goblin Key), subsequent enemies of that same type (other Goblins) will **never** drop a key again.
|
||||
- The player **must collect all 3 distinct keys** to unlock Option 3 and enter the Castle.
|
||||
4. **Post-Combat Recovery:** After each successful battle, the player's HP and Mana bars must automatically replenish (either fully or partially) to their base amounts so they are ready for the next encounter.
|
||||
5. **Experience & Leveling System:**
|
||||
- Defeating an enemy grants **XP**. The amount of XP must scale proportionally to the enemy's power level.
|
||||
- Upon reaching an XP threshold, the player levels up. **Leveling up must automatically increase the player's Max HP and Max Stamina/Mana**, making them strong enough to eventually face the Dragon.
|
||||
6. **Final Boss Fight:** Once the 3 Keys are obtained and the player chooses to go to the Castle, they will face the Dragon. Defeating the Dragon breaks the curse, resulting in **Victory**. Dying at any point results in **Game Over**.
|
||||
|
||||
🔹 Example game loop structure:
|
||||
|
||||
```java
|
||||
while (player.isAlive() && enemy.isAlive())
|
||||
player.attack(enemy);
|
||||
if (enemy.isAlive()) {
|
||||
enemy.attack(player);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 5️⃣ Step 5: Extra Features & Bonus Tasks ⭐
|
||||
*(Optional for extra credit)*
|
||||
|
||||
✅ **Dynamic Economy & Merchant System:** Implement coins that drop from enemies. Add a "Visit Merchant" option to the main loop where players can spend coins to buy specific weapons, armors, or consumables.
|
||||
✅ **Multiple Weapons & Inventory:** Players can buy, store, and swap between multiple weapons or use consumables mid-combat.
|
||||
✅ **Multiplayer/Party Mode:** Allow multiple players to team up and fight multiple enemies together. The Dragon's breath attack will damage the entire party simultaneously.
|
||||
✅ **PvP Mode:** Implement a **Player vs. Player** combat system.
|
||||
|
||||
### 6️⃣ Step 6: Write a Comprehensive README 📄
|
||||
As the final mandatory step of your development, you must replace the default `README.md` with your own comprehensive documentation. Your README should include:
|
||||
- A brief introduction to the game.
|
||||
- How to compile and run your project from the terminal.
|
||||
- An explanation of the classes, design patterns, and OOP principles you used.
|
||||
- A brief guide on how to play (controls, stats, classes).
|
||||
|
||||
---
|
||||
|
||||
## Evaluation Criteria ⚖
|
||||
|
||||
| **Criteria** | **Points** |
|
||||
|-------------------------------------------------------------|------------|
|
||||
| Proper use of OOP principles | **50** |
|
||||
| Working combat mechanics, Leveling System & Enemy abilities | **20** |
|
||||
| Clear and Comprehensive `README.md` | **20** |
|
||||
| Code readability, documentation, and comments | **10** |
|
||||
| Meaningful, interactive, & colored console outputs | **10** |
|
||||
| Inventory (item) and Merchant System (bonus) | **20** |
|
||||
| Other Extra features (bonus tasks) | **20** |
|
||||
| **Total Score** | **150** |
|
||||
|
||||
## Tips 🚀
|
||||
- **Follow OOP principles**: Avoid redundant code by using inheritance properly. Think carefully about what belongs in an abstract class vs. a specific subclass. Make sure you use overriding and overloading correctly.
|
||||
- **Test your code**: Run different scenarios (fighting, running out of mana, leveling up, dying) to ensure everything works as expected.
|
||||
- **Ask for help**: If you're stuck, reach out to your classmates or mentors.
|
||||
|
||||
## Submission ⌛
|
||||
- **Deadline**: Submit your assignment before **21 Ordibehesht (May 11th, 2026)**.
|
||||
- **Submission Format**: Push your code to your forked repository, create a PR, and ensure your comprehensive `README.md` is included in the root directory.
|
||||
3. **Run** the `Main.java` class and enjoy the game!
|
||||
|
||||

|
||||
###### - Born of God and Void. You shall seal the blinding light that plagues their dreams. You are the Vessel. You are the Java Knight.
|
||||
Reference in New Issue
Block a user