diff --git a/Java-Knight/src/main/java/org/project/Main.java b/Java-Knight/src/main/java/org/project/Main.java index a47f124..7c1619d 100644 --- a/Java-Knight/src/main/java/org/project/Main.java +++ b/Java-Knight/src/main/java/org/project/Main.java @@ -1,6 +1,10 @@ package org.project; -import org.project.entity.enemies.Dragon; +import org.project.entity.enemies.*; +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.location.Location; import java.util.ArrayList; @@ -16,31 +20,27 @@ public class Main { private static final String YELLOW = "\u001B[33m"; private static final String BLUE = "\u001B[34m"; private static final String PURPLE = "\u001B[35m"; + private static final String LIGHT_GRAY = "\u001B[37m"; + + private static boolean gameRunning = true; + static List locations = new ArrayList<>(); public static void main(String[] args) { // Scanner Scanner scanner = new Scanner(System.in); - Random random1 = new Random(112233); // for initial location - Random random2 = new Random(123123); // to choose enemy - // Note : seed numbers don't mean anything (They are just magical numbers) + Random random = new Random(); // for initial location - List locations = new ArrayList<>(); Location castle = new Location("Castle", new Dragon()); Location forest = new Location("Forest"); Location ruins = new Location("Ruins"); Location village = new Location("Village"); - locations.add(castle); locations.add(forest); locations.add(ruins); locations.add(village); - - int randNum1 = random1.nextInt() % 3; - int randNum2 = random2.nextInt() % 3; - - Location initialPlayerLoc = locations.get(randNum1 + 1); // since the index 0 : castle can't be the player initial location. + locations.add(castle); // Main loop @@ -54,7 +54,8 @@ public class Main { int option = scanner.nextInt(); switch (option){ case 1: - + startNewGame(scanner, random); + break; case 2: System.out.println("Goodbye!"); isRunning = false; @@ -68,4 +69,262 @@ public class Main { } + + public static void startNewGame(Scanner scanner, Random random){ + Player player; + int randNum1 = random.nextInt(3); // used for random initial location. + + System.out.println("Enter your name : "); + String name = scanner.next(); + Location currentLocation = locations.get(randNum1); + + int charOption = 0; + + while ( (charOption <= 0) || + (charOption >= 4)) { + System.out.println("Choose your character :"); + System.out.println(BLUE + " 1." + RESET + " Knight"); + System.out.println(BLUE + " 2." + RESET + " Assassin (it has a cool" + GREEN + " green" + RESET + " light saber!)"); + System.out.println(BLUE + " 3." + RESET + " Wizard"); + charOption = scanner.nextInt(); + } + + player = switch (charOption) { + case 1 -> new Knight(name); + case 2 -> new Assassin(name); + case 3 -> new Wizard(name); + default -> null; + }; + + // Resetting running flag + gameRunning = true; + + System.out.println("Going to : " + GREEN + currentLocation.getName() + RESET); + spawnRandomEnemy(currentLocation,random); + + + while (gameRunning && !player.isDead()){ + int choice = miniMenu(locations.get(randNum1),player); + + switch (choice){ + case 1: + if (currentLocation.hasEnemy()) { + fight(player, currentLocation.getEnemy(), currentLocation); + if (!player.isDead() && !currentLocation.hasEnemy()) { + // gain XP and recover + player.gainXP(100); + player.fullRestore(); + System.out.println(GREEN + "HP and MP restored." + RESET); + // spawn a new enemy + spawnRandomEnemy(currentLocation, random); + } + } + break; + case 2: + currentLocation = moveToLocation(scanner, currentLocation); + if (!currentLocation.hasEnemy()) { + spawnRandomEnemy(currentLocation, random); + } + System.out.println(GREEN + "Arrived at: " + currentLocation.getName() + RESET); + break; + case 3: + if (player.hasAllKeys()){ + currentLocation = locations.get(3); + System.out.println(PURPLE + "--DRAGON FIGHT--" + RESET); + Enemy dragon = new Dragon(); + fight(player,dragon,currentLocation); + if (dragon.isDead()){ + System.out.println(YELLOW + "VICTORY!" + RESET); + System.out.println("Level : " + player.getLevel()); + gameRunning = false; + } + }else { + System.out.println("Locked !"); + } + break; + + } + } + } + + public static int miniMenu(Location loc,Player player){ + boolean castleUnLocked = player.hasGoblinKey() && player.hasSkelKey() && player.hasVampKey(); + Scanner scanner = new Scanner(System.in); + int chosenOpt = 0; + + while (chosenOpt <= 0 || + chosenOpt >= 4){ + System.out.println("Choose an option : "); + + System.out.println(BLUE + " 1." + RESET + " Stay at " + loc.getName() + " and fight with " + loc.getEnemy().getName()); + System.out.println(BLUE + " 2." + RESET + " Move to another place"); + if (castleUnLocked){ + System.out.println(BLUE + " 3." + RESET + PURPLE + " Go to castle and fight with the dragon" + RESET); + } + else { + System.out.println(" \uD83D\uDD12" + LIGHT_GRAY + "3. Go to castle and fight with the dragon" + RESET); + } + + chosenOpt = scanner.nextInt(); + + } + + return chosenOpt; + } + + private static void spawnRandomEnemy(Location location, Random random) { + int enemyType = random.nextInt(3); + switch (enemyType){ + case 0: + location.setEnemy(new Vampire()); + System.out.println("A " + RED + "Vampire" + RESET + " Spawned !"); + break; + case 1: + location.setEnemy(new Skeleton()); + System.out.println("A Skeleton Spawned !"); + break; + case 2: + location.setEnemy(new Goblin()); + System.out.println("A " + GREEN + "Goblin" + RESET + " Spawned !"); + break; + } + } + + private static Location moveToLocation(Scanner scanner, Location current) { + System.out.println("Choose a location to move to:"); + for (int i = 0; i < locations.size(); i++) { + String prefix = locations.get(i).equals(current) ? " * " : " "; + System.out.println(BLUE + " " + (i + 1) + "." + RESET + " " + prefix + locations.get(i).getName()); + } + + int choice; + do { + choice = scanner.nextInt() - 1; + } while (choice < 0 || choice >= locations.size()); + + return locations.get(choice); + } + + private static void fight(Player player, Enemy enemy, Location location) { + System.out.println(RED + " COMBAT STARTED!" + RESET); + + while (!player.isDead() && !enemy.isDead()) { + // Player turn + playerTurn(player, enemy); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // Enemy turn + if (!enemy.isStunned()) { + enemyTurn(player, enemy); + } else { + System.out.println(BLUE + enemy.getName() + RESET + " is stunned. skipping to player..."); + enemy.stun(false); + } + } + + if (player.isDead()){ + System.out.println("Game over."); + System.out.println("Level : " + player.getLevel()); + gameRunning = false; + } else if (enemy.isDead()) { + System.out.println(player.getName() + RED + " Killed " + "a " + RESET + enemy.getName() + " at " + GREEN + location.getName() + RESET); + keyMechanic(player,enemy); + location.clearEnemy(); + + } + + } + + private static void playerTurn(Player player, Enemy enemy) { + Scanner scanner = new Scanner(System.in); + System.out.println("\n" + BLUE + "Your Turn:" + RESET); + System.out.println(RED + "HP : " + RESET + player.getHp() + "/" + player.getMaxHP() + + GREEN + " | MP: " + RESET + player.getMp() + "/" + player.getMaxMP()); + System.out.println(BLUE + " 1." + RESET + " Light Attack " + GREEN + "(0 MP)" + RESET); + System.out.println(BLUE + " 2." + RESET + " Heavy Attack " + GREEN + "(-" + player.getWeapon().getManaCost() + " MP)" + RESET); + System.out.println(BLUE + " 3." + RESET + " Defend " + GREEN + "(-5 MP)" + RESET); + System.out.println(BLUE + " 4." + RESET + " Heal " + GREEN + "(-10 MP)" + RESET); + System.out.println(BLUE + " 5." + RESET + " Special Ability " + GREEN + "(-" + player.specialAblMPCost() + " MP)" + RESET); + + int choice; + do { + choice = scanner.nextInt(); + } while (choice <= 0 || + choice >= 6); + + switch (choice) { + case 1: + player.lightAttack(enemy); + break; + case 2: + if (player.getMp() >= player.getWeapon().getManaCost()) { + player.heavyAttack(enemy); + } else { + System.out.println(RED + "Not enough MP!, light attacking." + RESET); + player.lightAttack(enemy); + System.out.println(RED + "Enemy HP : " + RESET + enemy.getHp()); + } + break; + case 3: + player.defend(); + break; + case 4: + if (player.getMp() >= 10) { + player.heal(30); + } else { + System.out.println(RED + "Not enough MP!" + RESET); + } + break; + case 5: + if (player.getMp() >= player.specialAblMPCost()) { + player.specialAbility(enemy); + } else { + System.out.println(RED + "Not enough MP!" + RESET); + } + break; + } + + System.out.println(RED + "HP : " + RESET + player.getHp() + "/" + player.getMaxHP() + + GREEN + " | MP: " + RESET + player.getMp() + "/" + player.getMaxMP()); + } + + private static void enemyTurn(Player player,Enemy enemy) { + System.out.println(RED + "\n" + enemy.getName() + "'s Turn:" + RESET); + + // randomly choosing between attack and specialAbility with probability of 1/3 + Random random = new Random(); + if (random.nextInt(3) == 0) { + enemy.specialAbility(player); + } else { + enemy.attack(player); + } + + System.out.println(RED + "Your HP : " + RESET + player.getHp() + "/" + player.getMaxHP()); + } + + private static void keyMechanic(Player player, Enemy enemy){ + Random random = new Random(); + + if (enemy instanceof Goblin){ + if (player.hasGoblinKey()) return; + if (random.nextInt(3) == 0) player.gotGoblinKey(); + } + else if (enemy instanceof Vampire){ + if (player.hasVampKey()) return; + if (random.nextInt(3) == 0) player.gotVampKey(); + } + else if (enemy instanceof Skeleton) { + if (player.hasSkelKey()) return; + if (random.nextInt(3) == 0) player.gotSkelKey(); + } + + + } + + } \ No newline at end of file diff --git a/Java-Knight/src/main/java/org/project/entity/enemies/Dragon.java b/Java-Knight/src/main/java/org/project/entity/enemies/Dragon.java index 993e351..16a4262 100644 --- a/Java-Knight/src/main/java/org/project/entity/enemies/Dragon.java +++ b/Java-Knight/src/main/java/org/project/entity/enemies/Dragon.java @@ -13,6 +13,11 @@ public class Dragon extends Enemy{ super(HP, dragonBreath); } + @Override + public String getName() { + return "Dragon"; + } + @Override public void specialAbility(Player target) { target.breakShield(); diff --git a/Java-Knight/src/main/java/org/project/entity/enemies/Enemy.java b/Java-Knight/src/main/java/org/project/entity/enemies/Enemy.java index 1ceb3e7..33317fc 100644 --- a/Java-Knight/src/main/java/org/project/entity/enemies/Enemy.java +++ b/Java-Knight/src/main/java/org/project/entity/enemies/Enemy.java @@ -35,6 +35,9 @@ public abstract class Enemy extends Entity { @Override public void takeDamage(int damage) { hp -= damage; + if (hp <= 0){ + dead = true; + } } @Override @@ -76,6 +79,8 @@ public abstract class Enemy extends Entity { return hp; } + public abstract String getName(); + public Weapon getWeapon() { return weapon; } diff --git a/Java-Knight/src/main/java/org/project/entity/enemies/Goblin.java b/Java-Knight/src/main/java/org/project/entity/enemies/Goblin.java index 4de9d42..5482eca 100644 --- a/Java-Knight/src/main/java/org/project/entity/enemies/Goblin.java +++ b/Java-Knight/src/main/java/org/project/entity/enemies/Goblin.java @@ -22,6 +22,11 @@ public class Goblin extends Enemy{ super.attack(target); } + @Override + public String getName() { + return "Goblin"; + } + // makes the next move critical with a probability of 80 private void criticality(){ Random random = new Random(); diff --git a/Java-Knight/src/main/java/org/project/entity/enemies/Skeleton.java b/Java-Knight/src/main/java/org/project/entity/enemies/Skeleton.java index 7f4cdb3..60f0d5e 100644 --- a/Java-Knight/src/main/java/org/project/entity/enemies/Skeleton.java +++ b/Java-Knight/src/main/java/org/project/entity/enemies/Skeleton.java @@ -19,6 +19,11 @@ public class Skeleton extends Enemy { super.attack(target); } + @Override + public String getName() { + return "Skeleton"; + } + @Override public void makeDie() { if (deathCounter == 0){ diff --git a/Java-Knight/src/main/java/org/project/entity/enemies/Vampire.java b/Java-Knight/src/main/java/org/project/entity/enemies/Vampire.java index fd8666c..8e6e060 100644 --- a/Java-Knight/src/main/java/org/project/entity/enemies/Vampire.java +++ b/Java-Knight/src/main/java/org/project/entity/enemies/Vampire.java @@ -2,6 +2,7 @@ package org.project.entity.enemies; import org.project.entity.Entity; import org.project.entity.players.Player; +import org.project.item.weapons.Bite; import org.project.item.weapons.Weapon; public class Vampire extends Enemy{ @@ -11,7 +12,13 @@ public class Vampire extends Enemy{ private static final int HP = 350; public Vampire(){ - super(HP, null); // vampire doesn't need a weapon. + Weapon vampBite = new Bite(); + super(HP, vampBite); + } + + @Override + public String getName() { + return "Vampire"; } @Override diff --git a/Java-Knight/src/main/java/org/project/entity/players/Assassin.java b/Java-Knight/src/main/java/org/project/entity/players/Assassin.java index 5f5f777..9eeeb1c 100644 --- a/Java-Knight/src/main/java/org/project/entity/players/Assassin.java +++ b/Java-Knight/src/main/java/org/project/entity/players/Assassin.java @@ -11,6 +11,7 @@ public class Assassin extends Player{ private static final int MAX_HP = 300; private static final int MAX_MP = 50; + private static final int SPECIAL_ABILITY_MANA_COST = 5; public Assassin(String name){ Weapon assassinSaber = new LightSaber("Green"); // idk why but i gave assassin a light saber. @@ -20,8 +21,14 @@ public class Assassin extends Player{ @Override public void specialAbility(Enemy target) { + mp -= SPECIAL_ABILITY_MANA_COST; System.out.println("Turning invisible..."); this.defend(); this.makeCritical(true); } + + @Override + public int specialAblMPCost() { + return SPECIAL_ABILITY_MANA_COST + DEFEND_MP_COST; + } } diff --git a/Java-Knight/src/main/java/org/project/entity/players/Knight.java b/Java-Knight/src/main/java/org/project/entity/players/Knight.java index 570d9be..60f66a0 100644 --- a/Java-Knight/src/main/java/org/project/entity/players/Knight.java +++ b/Java-Knight/src/main/java/org/project/entity/players/Knight.java @@ -11,6 +11,7 @@ public class Knight extends Player { private static final int MAX_HP = 400; private static final int MAX_MP = 40; + private static final int SPECIAL_ABILITY_MANA_COST = 20; public Knight(String name){ Weapon knightSword = new Sword(); @@ -25,4 +26,9 @@ public class Knight extends Player { target.stun(true); target.takeDamage(50); } + + @Override + public int specialAblMPCost() { + return SPECIAL_ABILITY_MANA_COST; + } } diff --git a/Java-Knight/src/main/java/org/project/entity/players/Player.java b/Java-Knight/src/main/java/org/project/entity/players/Player.java index 683846d..eead996 100644 --- a/Java-Knight/src/main/java/org/project/entity/players/Player.java +++ b/Java-Knight/src/main/java/org/project/entity/players/Player.java @@ -11,17 +11,23 @@ public abstract class Player extends Entity { Armor armor; private int hp; private int maxHP; - private int mp; + protected int mp; private int maxMP; + private int experiencePoint = 0; + private int level = 1; private boolean stunned = false; private boolean isDefending = false; private boolean critical = false; private boolean dead = false; + private boolean hasGoblinKey = false; + private boolean hasVampKey = false; + private boolean hasSkelKey = false; + private int xpToNextLevel = 100; private static final double DAMAGE_MODIFIER = 1.5; // Used for critical hits private static final int LIGHT_ATTACK_DAMAGE = 10; - private static final int DEFEND_MP_COST = 5; - private static final int HEAL_MP_COST = 10; + protected static final int DEFEND_MP_COST = 5; + protected static final int HEAL_MP_COST = 10; public Player(String name, int hp, int mp, Weapon weapon, Armor armor,int maxHP, int maxMP) { this.name = name; @@ -79,6 +85,10 @@ public abstract class Player extends Entity { isDefending = false; } + if (hp <= 0){ + this.dead = true; + } + } @Override @@ -90,6 +100,37 @@ public abstract class Player extends Entity { } } + public void gainXP(int amount){ + this.experiencePoint += amount; + + while (experiencePoint >= xpToNextLevel){ + levelUp(); + } + } + + public void levelUp(){ + this.experiencePoint -= xpToNextLevel; + this.level++; + + int oldMaxHP = maxHP; + int oldMaxMP = maxMP; + + maxMP += 5; + maxHP += 50; + + hp = maxHP; + mp = maxMP; + + System.out.println("Level Up! You are now at level : " + level); + System.out.println("Stats : HP : " + oldMaxHP + " -> " + maxHP + " , MP : " + oldMaxMP + " -> " + maxMP); + + xpToNextLevel += xpToNextLevel / 4; + } + + public int getLevel(){ + return level; + } + public void fillMana(int mana) { mp += mana; if (mp > maxMP) { @@ -97,6 +138,30 @@ public abstract class Player extends Entity { } } + public void fullRestore(){ + this.setHp(getMaxHP()); + this.setMp(getMaxMP()); + } + + public void gotGoblinKey(){ + hasGoblinKey = true; + System.out.println("Got the goblin key."); + } + + public void gotVampKey(){ + hasVampKey = true; + System.out.println("Got the vampire key."); + } + + public void gotSkelKey(){ + hasSkelKey = true; + System.out.println("Got the skeleton key."); + } + + public void setMp(int mp){ + this.mp = mp; + } + @Override public void makeCritical(boolean value) { critical = value; @@ -144,10 +209,15 @@ public abstract class Player extends Entity { return dead; } + public int getMaxHP() { return maxHP; } + public int getMaxMP(){ + return maxMP; + } + public int getMp() { return mp; } @@ -160,4 +230,21 @@ public abstract class Player extends Entity { return armor; } + public boolean hasGoblinKey(){ + return hasGoblinKey; + } + + public boolean hasSkelKey() { + return hasSkelKey; + } + + public boolean hasVampKey() { + return hasVampKey; + } + + public boolean hasAllKeys(){ + return (hasSkelKey && hasVampKey && hasGoblinKey); + } + + public abstract int specialAblMPCost(); } diff --git a/Java-Knight/src/main/java/org/project/entity/players/Wizard.java b/Java-Knight/src/main/java/org/project/entity/players/Wizard.java index cd8b956..eda9b52 100644 --- a/Java-Knight/src/main/java/org/project/entity/players/Wizard.java +++ b/Java-Knight/src/main/java/org/project/entity/players/Wizard.java @@ -13,6 +13,7 @@ public class Wizard extends Player{ private static final int MAX_MP = 30; private static final int SPELL_DAMAGE = 40; private static final int SPELL_HEAL_AMOUNT = 20; + private static final int SPECIAL_ABILITY_MANA_COST = 11; public Wizard(String name ){ Weapon wizardWeapon = new FireBall(); @@ -22,8 +23,14 @@ public class Wizard extends Player{ @Override public void specialAbility(Enemy target) { + mp -= SPECIAL_ABILITY_MANA_COST; System.out.println("Casting spell..."); target.takeDamage(SPELL_DAMAGE); this.heal(SPELL_HEAL_AMOUNT); } + + @Override + public int specialAblMPCost() { + return SPECIAL_ABILITY_MANA_COST + HEAL_MP_COST; + } } diff --git a/Java-Knight/src/main/java/org/project/item/consumables/Consumable.java b/Java-Knight/src/main/java/org/project/item/consumables/Consumable.java deleted file mode 100644 index 028e13d..0000000 --- a/Java-Knight/src/main/java/org/project/item/consumables/Consumable.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.project.item.consumables; - -// TODO: UPDATE IMPLEMENTATION -public abstract class Consumable { - /* - TODO: ADD OTHER REQUIRED AND BONUS METHODS - */ -} diff --git a/Java-Knight/src/main/java/org/project/item/consumables/Flask.java b/Java-Knight/src/main/java/org/project/item/consumables/Flask.java deleted file mode 100644 index b0c9a8d..0000000 --- a/Java-Knight/src/main/java/org/project/item/consumables/Flask.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.project.item.consumables; - -import org.project.entity.Entity; - -// TODO: UPDATE IMPLEMENTATION -public class Flask extends Consumable { - /* - THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN. - */ - - // TODO: UPDATE USE METHOD -// @Override -// public void use(Entity target) { -// target.heal(target.getMaxHP() / 10); -// } -} diff --git a/Java-Knight/src/main/java/org/project/item/weapons/Bite.java b/Java-Knight/src/main/java/org/project/item/weapons/Bite.java new file mode 100644 index 0000000..8e222a0 --- /dev/null +++ b/Java-Knight/src/main/java/org/project/item/weapons/Bite.java @@ -0,0 +1,9 @@ +package org.project.item.weapons; + +public class Bite extends Weapon{ + private static final int DAMAGE = 25; + + public Bite(){ + super(DAMAGE, 0); // the dagger is only meant to get used by skeleton and goblin, hence it doesen't use mana. + } +} diff --git a/Java-Knight/src/main/java/org/project/location/Location.java b/Java-Knight/src/main/java/org/project/location/Location.java index 54d90c5..450f1e5 100644 --- a/Java-Knight/src/main/java/org/project/location/Location.java +++ b/Java-Knight/src/main/java/org/project/location/Location.java @@ -7,6 +7,7 @@ import java.util.ArrayList; public class Location { private String name; private Enemy enemy; + private boolean playerInside = false; public Location(String name) { this.name = name; @@ -26,6 +27,10 @@ public class Location { return enemy; } + public void setPlayerInside(boolean value){ + playerInside = value; + } + public void setEnemy(Enemy enemy) { this.enemy = enemy; } @@ -34,6 +39,10 @@ public class Location { return enemy != null && !enemy.isDead(); } + public boolean hasPlayer() { + return playerInside; + } + public void clearEnemy() { this.enemy = null; }