implement Java-Knight gameplay #1

Closed
emad wants to merge 3 commits from develop into main
65 changed files with 2191 additions and 261 deletions
+5
View File
@@ -16,5 +16,10 @@
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://mirror-maven.runflare.com/maven2" />
</remote-repository>
</component>
</project>
Binary file not shown.
+435
View File
@@ -0,0 +1,435 @@
import org.project.Colors;
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.item.armors.AssassineArmor;
import org.project.item.armors.KnightArmor;
import org.project.item.armors.WizardArmor;
import org.project.item.consumables.*;
import org.project.item.weapons.AssassineWeapon;
import org.project.item.weapons.Sword;
import org.project.item.weapons.WizardWeapon;
import org.project.location.Location;
import javax.swing.plaf.metal.MetalRootPaneUI;
import java.util.*;
public class Main {
public static void main(String[] args) {
// TODO: ADD LOCATIONS TO YOUR GAME
List<Location> locations = new ArrayList<>();
Location loc1 = new Location("Darkmire Hollow");
loc1.addEnemy(new Goblin());
Location loc2 = new Location("Grave of the Fallen");
loc2.addEnemy(new Skeleton());
Location loc3 = new Location("Goblin Warrens");
loc3.addEnemy(new Goblin());
Location loc4 = new Location("Vampire's Embrace");
loc4.addEnemy(new Vampire());
Location loc5 = new Location("Blightmoor Cave");
loc5.addEnemy(new Goblin());
loc5.addEnemy(new Skeleton());
Location loc6 = new Location("Crypt of Bleeding Bones");
loc6.addEnemy(new Vampire());
loc6.addEnemy(new Skeleton());
Location loc7 = new Location("Cackling Caverns");
loc7.addEnemy(new Goblin());
loc7.addEnemy(new Goblin());
loc7.addEnemy(new Goblin());
Collections.addAll(locations, loc1, loc2, loc3, loc4, loc5, loc6, loc7);
List<Consumable> allItems = new ArrayList<>();
Collections.addAll(allItems, new CursedDagger(), new ElixirOfLife(), new GoblinGland(), new HealthPotion(), new HolyWater(), new ManaPotion());
// TODO: IMPLEMENT GAMEPLAY
System.out.print(Colors.RESET + "enter your name: ");
Scanner in = new Scanner(System.in);
String name = in.next();
System.out.println("Choos your character: ");
System.out.println("1.Knight: ");
System.out.println("2.Assassine");
System.out.println("3.Wizard");
Player player = null;
int input2 = in.nextInt();
while (input2 > 3 || input2 < 1)
{
System.out.println("invalid input!");
System.out.println("Choos your character: ");
System.out.println("1.Knight");
System.out.println("2.Assassine");
System.out.println("3.Wizard");
input2 = in.nextInt();
}
switch (input2)
{
case (1):
{
player = new Knight(name, new Sword(), new KnightArmor());
break;
}
case(2):
{
player = new Assassin(name, new AssassineWeapon(), new AssassineArmor());
break;
}
case(3):
{
player = new Wizard(name, new WizardWeapon(), new WizardArmor());
break;
}
}
player.showInf();
System.out.println(Colors.YELLO + "WELCOME\n" + Colors.RESET);
boolean running = true;
while (running)
{
Location randomLoc = locations.get(new Random().nextInt(7));
// while (randomLoc.isDiscovered())
// {
// randomLoc = locations.get(new Random().nextInt());
// }
System.out.println(randomLoc);
System.out.println("1.Fight the enemies");
System.out.println("2.Move to another location");
System.out.println("3.Visit Merchant");
boolean hasKeys = player.getGoblinKey() && player.getSkeletonKey() && player.getVampireKey();
if(hasKeys)
{
System.out.println("4.Go to the Castle to fight the Dragon");
}
int input = in.nextInt();
while((input > 4 || input < 1))
{
System.out.println("input is invalid!");
System.out.println(randomLoc);
System.out.println("1.Fight the enemies");
System.out.println("2.Move to another location");
System.out.println("3.Visit Merchant");
if(hasKeys)
{
System.out.println("4.Go to the Castle to fight the Dragon");
}
in.next();
input = in.nextInt();
}
switch (input)
{
case (1):
{
if(randomLoc.getEnemies().size() > 1)
{
System.out.println("You have to fight with " + randomLoc.getEnemies().size() + " enemies!");
System.out.println("How Brave you are!");
System.out.println(Colors.YELLO + "Have luck" + Colors.RESET);
}
//or && running?
while (randomLoc.checkLives() && player.isAlive())
{
for (int i = 0; i < randomLoc.getEnemies().size(); i++) {
Enemy currentEnemy = randomLoc.getEnemies().get(i);
System.out.println("You know have to fight with " + currentEnemy.getClass().getSimpleName());
if (combat(player, currentEnemy))
{
// randomLoc.defeatEnemy(currentEnemy);
if (player.needLevelUp()) {
player.levelUp();
}
}
else
{
running = false;
break;
}
}
}
if(player.isAlive())
{
player.setHp(player.getMaxHP());
player.fillMana(player.getMaxMP());
player.getArmor().repair();
randomLoc.resetLives();
}
break;
}
case(2):
{
break;
}
case(3):
{
System.out.println(Colors.WELCOME + "Welcome, traveler!" + Colors.RESET);
int input4;
do
{
for (int i = 0; i < allItems.size(); i++) {
System.out.println((i + 1) + "." + allItems.get(i));
}
System.out.println(Colors.ORANGE + "[" + Colors.RESET + "Your coins: " + Colors.COIN + player.getCoin() + Colors.ORANGE + "]" + Colors.RESET);
System.out.println("Which one do you want to buy?");
System.out.println("enter your choice(write 0 to back): ");
input4 = in.nextInt();
if(input4 < 0 || input4 > allItems.size())
{
System.out.println("invalid input!");
continue;
}
switch (input4) {
case (0): {
break;
}
case (1): {
if (allItems.get(0).getValue() <= player.getCoin()) {
player.addItem(allItems.get(0));
player.setCoin(player.getCoin() - allItems.get(0).getValue());
break;
}
}
case (2): {
if (allItems.get(1).getValue() <= player.getCoin()) {
player.addItem(allItems.get(1));
player.setCoin(player.getCoin() - allItems.get(1).getValue());
break;
}
}
case (3): {
if (allItems.get(2).getValue() <= player.getCoin()) {
player.addItem(allItems.get(2));
player.setCoin(player.getCoin() - allItems.get(2).getValue());
break;
}
}
case (4): {
if (allItems.get(3).getValue() <= player.getCoin()) {
player.addItem(allItems.get(3));
player.setCoin(player.getCoin() - allItems.get(3).getValue());
break;
}
}
case (5): {
if (allItems.get(4).getValue() <= player.getCoin()) {
player.addItem(allItems.get(4));
player.setCoin(player.getCoin() - allItems.get(4).getValue());
break;
}
}
case (6): {
if (allItems.get(5).getValue() <= player.getCoin()) {
player.addItem(allItems.get(5));
player.setCoin(player.getCoin() - allItems.get(5).getValue());
break;
}
}
default: {
System.out.println("You don't have enough coin!");
}
}
}while (input4 != 0 );
}
case(4):
{
if(hasKeys)
{
if (combat(player, new Dragon()))
{
System.out.println(Colors.PINK + "VICTORY!!!!!!!");
running = false;
}
else
{
running = false;
break;
}
}
}
}
}
}
public static boolean combat(Player p, Enemy e)
{
int round = 0;
while (p.isAlive() && e.isAlive())
{
System.out.printf(Colors.ORANGE + "[" + Colors.RESET + p.getName() + " - " +"%d/%d HP | %d/%d Mana" + Colors.ORANGE + "]\n" ,
p.getHp(), p.getMaxHP(), p.getMp(), p.getMaxMP());
System.out.printf(Colors.ORANGE + "[" + Colors.RESET + e.getClass().getSimpleName() + " - " +"%d/%d HP" + Colors.ORANGE + "]\n" + Colors.RESET, e.getHp(), e.getMaxHP() );
System.out.println("Your turn:");
switch (p) {
case Knight knight ->
System.out.println("1.Light Attack | 2.Heavy Attack " + Colors.ORANGE + "(" + Colors.RESET + "-50 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 3.Defend " + Colors.ORANGE + "(" + Colors.RESET + "-40 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 4.Heal" + Colors.ORANGE + "(" + Colors.RESET + "-45 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 5.Shield Bash " + Colors.ORANGE + "(" + Colors.RESET + "-65 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")\n" + Colors.RESET);
case Assassin assassin ->
System.out.println("1.Light Attack | 2.Heavy Attack " + Colors.ORANGE + "(" + Colors.RESET + "-55 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 3.Defend " + Colors.ORANGE + "(" + Colors.RESET + "-40 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 4.Heal" + Colors.ORANGE + "(" + Colors.RESET + "-45 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 5.Shadow Strike " + Colors.ORANGE + "(" + Colors.RESET + "-80 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")\n" + Colors.RESET);
case Wizard wizard ->
System.out.println("1.Light Attack | 2.Heavy Attack " + Colors.ORANGE + "(" + Colors.RESET + "-65 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 3.Defend " + Colors.ORANGE + "(" + Colors.RESET + "-40 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 4.Heal" + Colors.ORANGE + "(" + Colors.RESET + "-45 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")" +
Colors.RESET + " | 5.Blood Burn " + Colors.ORANGE + "(" + Colors.RESET + "-75 " + Colors.BLUE + "Mana" + Colors.ORANGE + ")\n" + Colors.RESET);
default -> {
}
}
if(!p.getItems().isEmpty())
{
System.out.println("press 6 to see your items.");
}
Scanner input = new Scanner(System.in);
int input3 = input.nextInt();
switch (input3)
{
case(1):
{
p.lightAttack(e);
break;
}
case(2):
{
if(!p.heavyAttack(e))
{
continue;
}
break;
}
case(4):
{
if(!p.heal(p.getMaxHP()))
{
continue;
}
break;
}
case(5):
{
if(!p.specialAbility(e))
{
continue;
}
break;
}
case(6):
{
if(!p.getItems().isEmpty())
{
p.showItems();
System.out.println("What do you want to use: ");
System.out.println("press 0 to back.");
int input5 = input.nextInt();
while (input5 < 0 || input5 > p.getItems().size())
{
System.out.println("invalid input!");
p.showItems();
System.out.println("What do you want to use: ");
System.out.println("press 0 to back.");
input5 =input.nextInt();
}
if(input5 == 0)
{
continue;
}
else
{
if(p.getItems().get(input5 - 1).getTargetType().equals("Player"))
{
p.getItems().get(input5 - 1).use(p);
}
else
{
if(p.getItems().get(input5 - 1) instanceof GoblinGland)
{
p.getItems().get(input5 - 1).use(e);
continue;
}
p.getItems().get(input5 - 1).use(e);
}
p.useItem(p.getItems().get(input5 - 1));
}
}
break;
}
}
if(e.isAlive())
{
if(e.isConfuse())
{
System.out.println("Enemy lost its turn cause of confusion");
continue;
}
if (input3 == 5 && p instanceof Knight)
{
System.out.printf("%s couldn't do anything in his round\n", e.getClass().getSimpleName());
continue;
}
if(e.HaveBleed())
{
e.bleed((AssassineWeapon) p.getWeapon());
}
System.out.printf("%s" + "'s turn:\n", e.getClass().getSimpleName());
if (input3 == 3) {
p.defend(e.attack());
}
else
{
if(e instanceof Dragon)
{
p.takeDamage((Dragon) e);
}
else if(p instanceof Wizard w) {
w.takeDamage(e ,e.attack());
}
else
{
p.takeDamage(e.attack());
}
}
if(!p.isAlive())
{
System.out.println("You " + Colors.RED + "Died" + Colors.BLUE + ":(");
return false;
}
round++;
if(round == 2)
{
round = 0;
if (p instanceof Knight) {
p.fillMana(p.getMaxMP() / 5);
} else {
p.fillMana(p.getMaxMP() / 10);
}
}
}
else
{
System.out.println("You defeat the " + e.getClass().getSimpleName());
p.addXp(e);
p.addCoin(e);
e.dropKey(p);
System.out.println("Keep moving forward!");
System.out.println("Good luck!");
return true;
}
}
return false;
}
}
@@ -0,0 +1,33 @@
package org.project;
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.item.armors.AssassineArmor;
import org.project.item.armors.KnightArmor;
import org.project.item.armors.WizardArmor;
import org.project.item.weapons.AssassineWeapon;
import org.project.item.weapons.Sword;
import org.project.item.weapons.WizardWeapon;
import org.project.location.Location;
import java.util.*;
public class Colors {
public static final String RED = "\u001b[38;5;124m";
public static final String LIGHTRED = "\u001b[38;5;160m";
public static final String ORANGE = "\u001b[38;5;202m";
public static final String FIRE = "\u001b[38;5;208m";
public static final String BLUE = "\u001b[38;5;33m";
public static final String KEY = "\u001b[38;5;14m";
public static final String LIGHTBLUE = "\u001b[38;5;68m";
public static final String RESET = "\u001b[38;5;15m";
public static final String YELLO = "\u001b[38;5;226m";
public static final String COIN = "\u001b[38;5;214m";
public static final String PURPLE = "\u001b[38;5;164m";
public static final String PINK = "\u001b[38;5;205m";
public static final String HEAL = "\u001b[38;5;203m";
public static final String WELCOME = "\u001b[38;5;65m";
}
@@ -1,15 +0,0 @@
package org.project;
import org.project.location.Location;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// TODO: ADD LOCATIONS TO YOUR GAME
List<Location> locations = new ArrayList<>();
// TODO: IMPLEMENT GAMEPLAY
}
}
@@ -1,20 +1,14 @@
package org.project.entity;
public interface Entity {
void attack(Entity target);
void defend();
void heal(int health);
void fillMana(int mana);
void takeDamage(int damage);
int getMaxHP();
int getMaxMP();
boolean heal(int health);
boolean isAlive();
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
@@ -0,0 +1,13 @@
package org.project.entity;
public interface ICombatActions {
void lightAttack(Entity target);
boolean heavyAttack(Entity target);
boolean specialAbility(Entity target);
boolean defend(int damage);
}
@@ -0,0 +1,25 @@
package org.project.entity.enemies;
import org.project.entity.players.Player;
import java.util.Random;
public class Dragon extends Enemy{
public Dragon() {
super(250, 58);
}
@Override
public int attack() {
if(new Random().nextInt(100) < 35)
{
return 100;
}
return super.getDamage();
}
@Override
public void dropKey(Player player) {
return;
}
}
@@ -1,34 +1,126 @@
package org.project.entity.enemies;
import org.project.item.weapons.Weapon;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.players.Player;
import org.project.item.weapons.AssassineWeapon;
// TODO: UPDATE IMPLEMENTATION
public abstract class Enemy {
Weapon weapon;
public abstract class Enemy implements Entity {
private final int maxHp;
private int hp;
private int mp;
private Boolean haveBleed;
private int bleedingRound;
private final int damage;
public boolean confuse;//for Goblin Gland
public Enemy(int hp, int mp, Weapon weapon) {
this.hp = hp;
this.mp = mp;
this.weapon = weapon;
public Enemy(int maxHp, int damage) {
this.maxHp = maxHp;
this.hp = maxHp;
this.damage = damage;
this.haveBleed = false;
this.bleedingRound = 0;
this.confuse = false;
}
public abstract void dropKey(Player player);
@Override
public boolean heal(int health) {
System.out.printf("%s healed for " + Colors.LIGHTRED + "%d" + Colors.RESET +" HP\n", this.getClass().getSimpleName(), health);
hp += health;
if (hp > maxHp) {
hp = maxHp;
}
return true;
}
public void bleed(AssassineWeapon aw)
{
if(haveBleed)
{
if(bleedingRound > 0)
{
System.out.printf("%s Bleeds out!."+ Colors.RED + "-%d" +Colors.RESET + " HP\n", this.getClass().getSimpleName(), aw.getBleedDamage());
hp -= aw.getBleedDamage();
this.bleedingRound--;
}
if(bleedingRound == 0)
{
this.haveBleed = false;
}
}
}
@Override
public boolean isAlive() {
return hp > 0;
}
public int attack()
{
return damage;
}
@Override
public void takeDamage(int damage) {
hp -= damage;
if(hp < 0)
{
hp = 0;
}
System.out.printf("%s took " + Colors.RED + "%d" + Colors.RESET + " damage!\n",this.getClass().getSimpleName(), damage);
}
public int getDamage() {
return damage;
}
@Override
public int getMaxHP() {
return maxHp;
}
public int getHp() {
return hp;
}
public int getMp() {
return mp;
public void setHp(int hp) {
this.hp = hp;
if(hp > maxHp)
{
hp = maxHp;
}
}
public Weapon getWeapon() {
return weapon;
public int getBleedingRound() {
return bleedingRound;
}
public void setBleedingRound(int bleedingRound) {
this.bleedingRound = bleedingRound;
}
public Boolean HaveBleed() {
return haveBleed;
}
public void setHaveBleed(Boolean haveBleed) {
this.haveBleed = haveBleed;
}
public void setConfuse(boolean confuse) {
this.confuse = confuse;
}
public boolean isConfuse()
{
if(confuse)
{
confuse = false;
return true;
}
return false;
}
}
@@ -0,0 +1,45 @@
package org.project.entity.enemies;
import org.project.Colors;
import org.project.entity.players.Player;
import java.util.Random;
public class Goblin extends Enemy{
private int hitChance;
public Goblin() {
super(50, 25);
this.hitChance = 20;
}
Random rand = new Random();
@Override
public int attack() {
if(rand.nextInt(100) < hitChance)
{
System.out.println("Goblin used Critical Strike!");
hitChance -= 5;
return 45;
}
else
{
System.out.println("Goblin attacked");
return super.getDamage();
}
}
@Override
public void dropKey(Player player) {
if(!player.getGoblinKey())
{
if (new Random().nextInt(100) < 50) {
System.out.println("Goblin dropped a " + Colors.KEY +"KEY" +Colors.RESET +"!");
player.setGoblinKey(true);
}
}
}
}
@@ -1,6 +1,53 @@
package org.project.entity.enemies;
import org.project.Colors;
import org.project.entity.players.Player;
import java.util.Random;
// TODO: UPDATE IMPLEMENTATION
public class Skeleton {
public class Skeleton extends Enemy {
private int lifeChance;
public Skeleton() {
super(60, 30);
this.lifeChance = 1;
}
@Override
public boolean isAlive() {
if(super.getHp() <= 0)
{
if(lifeChance >= 1)
{
lifeChance--;
super.setHp(30);
System.out.println("Skeleton rises again with 50% of HP (current HP: 30)");
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
@Override
public void dropKey(Player player) {
if(!player.getSkeletonKey())
{
if (new Random().nextInt(100) < 35) {
System.out.println("Skeleton dropped a " + Colors.KEY +"KEY" +Colors.RESET +"!");
player.setSkeletonKey(true);
}
}
}
// TODO: DESIGN ENEMY AND IMPLEMENT THE CONSTRUCTOR
}
@@ -0,0 +1,38 @@
package org.project.entity.enemies;
import org.project.Colors;
import org.project.entity.players.Player;
import java.util.Random;
public class Vampire extends Enemy{
public Vampire() {
super(75, 35);
}
@Override
public boolean heal(int health) {
super.setHp(super.getHp() + health);
return false;
}
@Override
public int attack() {
System.out.println("Your HP is know VAMPIRE's HP!");
super.heal(super.getDamage() / 2);
return super.getDamage();
}
@Override
public void dropKey(Player player) {
if(!player.getVampireKey())
{
if (new Random().nextInt(100) < 40) {
System.out.println("Vampire dropped a " + Colors.KEY +"KEY" +Colors.RESET +"!");
player.setVampireKey(true);
}
}
}
}
@@ -0,0 +1,166 @@
package org.project.entity.players;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.enemies.Enemy;
import org.project.item.armors.AssassineArmor;
import org.project.item.weapons.AssassineWeapon;
public class Assassin extends Player{
public Assassin(String name, AssassineWeapon weapon, AssassineArmor armor) {
super(name, 65, 120, weapon, armor, 70);
super.setDamage(35);
}
@Override
public boolean heavyAttack(Entity target) {
if(super.getMp() >= super.getWeapon().getManaCost()) {
super.setMp(super.getMp() - super.getWeapon().getManaCost());
System.out.printf("%s (%s) uses heavy attack.\n", name, this.getClass().getSimpleName());
target.takeDamage(super.getWeapon().getDamage());
AssassineWeapon aw = (AssassineWeapon) super.getWeapon();
aw.manaGain(this, aw.getDamage() / 4);
if(target instanceof Enemy enemy)
{
enemy.setHaveBleed(aw.bleed());
if(enemy.HaveBleed())
{
enemy.setBleedingRound(aw.getBleedingRound());
}
}
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " for heavy attack.");
return false;
}
@Override
public boolean specialAbility(Entity target) {
if(super.getMp() >= 80) {
super.setMp(super.getMp() - 80);
System.out.printf("%s (Assassin) uses special ability.\n", super.name);
System.out.println("Assassin dodged the " + Colors.RED + "attack" + Colors.RESET);
AssassineWeapon aw = (AssassineWeapon) super.getWeapon();
System.out.printf("%s (Assassin) used Critical Strike!\n", super.name);
target.takeDamage(aw.criticalHit());
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " for your special ability.");
return false;
}
@Override
public void takeDamage(int damage) {
AssassineArmor asA = (AssassineArmor) super.getArmor();
int defence = asA.getDefense(damage);
if(defence == damage)
{
System.out.printf("%s (Assassin) dodged the attack.\n", super.name);
}
else {
super.takeDamage(damage - defence);
}
}
@Override
public void levelUp() {
if(super.getLevel() < 5) {
addLevel();
System.out.printf("You leveled up! " +Colors.ORANGE +"(" +Colors.RESET +"your level: " + Colors.LIGHTBLUE + "%d" + Colors.ORANGE +")\n" + Colors.RESET, super.getLevel());
switch (super.getLevel())
{
case (2):
{
super.setMaxHP(super.getMaxHP() + ((20*super.getMaxHP()) / 100 ));
System.out.println("Your maximum HP is know 20% higher");
super.setDamage(super.getDamage() + ((20*super.getMaxHP()) / 100 ));
System.out.println("Your base damage is know 20% higher");
break;
}
case(3):
{
AssassineArmor asA = (AssassineArmor) super.getArmor();
asA.setDodgeChance(asA.getDodgeChance() + asA.getDodgeChance()/2);
System.out.println("Your dodge chance is know " + asA.getDodgeChance());
asA.setMaxPercentage(2*asA.getMaxPercentage());
System.out.println("Your defend percentage is know " + asA.getDefensePercentage());
break;
}
case(4):
{
super.setDamage(super.getDamage() + super.getDamage()/ 2);
System.out.println("Your base damage is know 50% higher");
AssassineWeapon aw = (AssassineWeapon) super.getWeapon();
aw.setBleedingRound(aw.getBleedingRound() + 1);
System.out.println("Bleed duration on the enemy increased to two rounds (up from one)");
aw.setBleedChance(2*aw.getBleedChance());
System.out.println("Your bleed chance is know " + aw.getBleedChance());
break;
}
case (5):
{
super.setMaxHP(super.getMaxHP() + ((30*super.getMaxHP()) / 100 ));
System.out.println("Your maximum HP is know 20% higher");
super.setMaxMP(super.getMaxMP() + ((10*super.getMaxMP()) / 100 ));
System.out.println("Your maximum Mana is know 10% higher");
super.getWeapon().setDamage(super.getWeapon().getDamage() + ((30*super.getWeapon().getDamage()) / 100 ));
System.out.println("Your Weapon damage is know 30% higher");
super.setMaxDefense(super.getMaxDefense() + ((20*super.getMaxDefense()) / 100 ));
System.out.println("Your maximum defence is know 20% higher");
AssassineWeapon aw = (AssassineWeapon) super.getWeapon();
aw.setBleedingRound(aw.getBleedingRound() + 1);
System.out.println("Bleed duration on the enemy increased to three rounds (up from two)");
break;
}
}
super.setHp(super.getMaxHP());
super.fillMana(super.getMaxMP());
super.getArmor().repair();
System.out.println();
showInf();
}
}
@Override
public void showInf() {
AssassineArmor asA = (AssassineArmor) super.getArmor();
AssassineWeapon aw = (AssassineWeapon) super.getWeapon();
System.out.println("Your information:");
System.out.printf("""
Level: %d
HP: %d
MP: %d
Coin: %d
Maximum defence: %d
Armor:
Durability: %d
DefensePercentage: %d
Dodge chance: %d
Weapon:
Damage: %d
Mana cost: %d
Bleed chance: %d
Bleed damage: %d
Bleeding round: %d
Critical hit: %d
""", super.getLevel(), super.getHp(), super.getMp(), super.getCoin(), super.getMaxDefense(),
super.getArmor().getDurability(), super.getArmor().getDefensePercentage(),
asA.getDodgeChance(), aw.getDamage(), aw.getManaCost() ,aw.getBleedChance(),
aw.getBleedDamage(), aw.getBleedingRound(), 3*aw.getDamage() );
}
}
@@ -1,6 +1,117 @@
package org.project.entity.players;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.item.armors.Armor;
import org.project.item.armors.KnightArmor;
import org.project.item.weapons.Sword;
import org.project.item.weapons.Weapon;
// TODO: UPDATE IMPLEMENTATION
public class Knight {
public class Knight extends Player {
public Knight(String name,Sword weapon, KnightArmor armor) {
//although hp is low but armor is wow.
super(name, 60, 70, weapon, armor, 65);
super.setDamage(40);
}
@Override
public boolean specialAbility(Entity target) {
if(super.getMp() > 65) {
super.setMp(super.getMp() - 65);
System.out.printf("%s (Knight) uses special ability.\n", super.name);
Sword sword = (Sword) super.getWeapon();
target.takeDamage(sword.getSpecialDamage());
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " for your special ability.");
return false;
}
@Override
public void levelUp() {
if(super.getLevel() < 5) {
addLevel();
System.out.printf("You leveled up! " +Colors.ORANGE +"(" +Colors.RESET +"your level: " + Colors.LIGHTBLUE + "%d" + Colors.ORANGE +")\n" + Colors.RESET, super.getLevel());
switch (super.getLevel())
{
case (2):
{
super.setMaxHP(super.getMaxHP() + ((15*super.getMaxHP()) / 100 ));
System.out.println("Your maximum HP is know 15% higher");
super.setMaxMP(super.getMaxMP() + ((10*super.getMaxMP()) / 100 ));
System.out.println("Your maximum Mana is know 10% higher");
break;
}
case(3):
{
super.getArmor().setMaxDurability(super.getArmor().getMaxDurability() + ((10 * super.getArmor().getMaxDurability()) / 100));
System.out.println("Your armor's maximum durability is know 10% higher");
break;
}
case(4):
{
super.setMaxHP(super.getMaxHP() + ((40*super.getMaxHP()) / 100 ));
System.out.println("Your maximum HP is know 40% higher");
Sword sword = (Sword) super.getWeapon();
sword.setSpecialDamage(sword.getSpecialDamage() + (10*sword.getSpecialDamage() / 100 ));
System.out.println("Your sword special damage is know 10% higher");
super.setDamage(super.getDamage() + ((20*super.getDamage()) / 100 ));
System.out.println("Your base damage is know 20% higher");
super.getWeapon().setManaCost(super.getWeapon().getManaCost() - ((10*super.getWeapon().getManaCost()) / 100 ));
System.out.println("Your weapon mana cost decreased by 10%");
break;
}
case (5):
{
super.setMaxMP(super.getMaxMP() + ((20*super.getMaxMP()) / 100 ));
System.out.println("Your maximum mana is know 20% higher");
super.setMaxDefense(super.getMaxDefense() + ((20*super.getMaxDefense()) / 100 ));
System.out.println("Your maximum defence is know 20% higher");
super.getWeapon().setDamage(super.getWeapon().getDamage() + ((10*super.getWeapon().getDamage()) / 100 ));
System.out.println("Your sword damage is know 10% higher");
break;
}
}
}
super.setHp(super.getMaxHP());
super.fillMana(super.getMaxMP());
super.getArmor().repair();
System.out.println();
showInf();
}
@Override
public void showInf() {
Sword sword = (Sword) super.getWeapon();
System.out.println("Your information:");
System.out.printf("""
level: %d
HP: %d
MP: %d
Coin: %d
Maximum defence: %d
armor:
durability: %d
defensePercentage: %d
Sword:
damage: %d
mana cost: %d
Special damage: %d
""", super.getLevel(), super.getHp(), super.getMp(), super.getCoin(), super.getMaxDefense(),
super.getArmor().getDurability(), super.getArmor().getDefensePercentage(),
sword.getDamage(),sword.getManaCost() ,sword.getSpecialDamage());
}
// TODO: DESIGN KNIGHT'S WEAPON AND ARMOR AND IMPLEMENT THE CONSTRUCTOR
}
@@ -1,53 +1,229 @@
package org.project.entity.players;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.ICombatActions;
import org.project.entity.enemies.*;
import org.project.item.armors.Armor;
import org.project.item.consumables.Consumable;
import org.project.item.weapons.Weapon;
import java.util.ArrayList;
import static java.lang.Math.*;
// TODO: UPDATE IMPLEMENTATION
public abstract class Player {
public abstract class Player implements Entity, ICombatActions {
protected String name;
Weapon weapon;
Armor armor;
private Weapon weapon;
private Armor armor;
private ArrayList<Consumable> items;
private int hp;
private int maxHP;
private int mp;
private int maxMP;
private int damage;
private int level;
private int xp;
private Boolean goblinKey;
private Boolean skeletonKey;
private Boolean vampireKey;
private int coin;
private int maxDefense;
public Player(String name, int hp, int mp, Weapon weapon, Armor armor) {
public Player(String name, int hp, int mp, Weapon weapon, Armor armor, int maxDefense) {
this.name = name;
this.hp = hp;
this.maxHP = hp;
this.maxMP = mp;
this.mp = mp;
this.level = 1;
this.weapon = weapon;
this.armor = armor;
this.maxDefense = maxDefense;
this.goblinKey = false;
this.skeletonKey = false;
this.vampireKey = false;
this.coin = 0;
this.items = new ArrayList<>();
}
@Override
public void attack(Entity target) {
target.takeDamage(weapon.getDamage());
public void lightAttack(Entity target)
{
System.out.printf("%s (%s) used light attack\n", name, this.getClass().getSimpleName());
target.takeDamage(damage);
}
@Override
public void defend() {
// TODO
public boolean heavyAttack(Entity target) {
if(mp >= weapon.getManaCost()) {
System.out.printf("%s (%s) uses heavy attack\n", name, this.getClass().getSimpleName());
target.takeDamage(weapon.getDamage());
mp -= weapon.getManaCost();
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " for heavy attack.");
return false;
}
@Override
public boolean defend(int damage) {
if(mp > 40)
{
mp -= 40;
if (damage > maxDefense) {
int defense = (80 * damage) / 100;
this.takeDamage(damage - defense);
System.out.printf("You took " + Colors.BLUE + "%d" + Colors.RESET + "damage!" + Colors.YELLO + "(%d damage was defended)\n" + Colors.RESET, damage, damage - defense);
} else {
System.out.println("The enemy hit defended!");
}
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " to defend.");
return false;
}
@Override
public void takeDamage(int damage) {
hp -= damage - armor.getDefense();
hp -= (damage - armor.getDefense(damage));
System.out.printf("%s (%s) took " + Colors.RED + "%d" + Colors.RESET + " damage!\n",name, this.getClass().getSimpleName(), damage - armor.getDefense(damage) );
}
public void takeDamage(Dragon dragon)
{
hp -= dragon.getDamage();
System.out.println("you can't defeat dragon " + Colors.FIRE + "Fire" + Colors.RESET + "!");
System.out.printf("%s (%s) took " + Colors.RED + "%d" + Colors.RESET + " damage!\n",name, this.getClass().getSimpleName(), dragon.getDamage());
}
@Override
public void heal(int health) {
hp += health;
if (hp > maxHP) {
hp = maxHP;
public boolean heal(int health) {
if(mp > 45)
{
mp -= 45;
System.out.printf("%s (%s) healed for " +Colors.HEAL +"%d" +Colors.RESET+ " HP.\n", name, this.getClass().getSimpleName(), health);
hp += health;
if (hp > maxHP) {
hp = maxHP;
}
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " to heal.");
return false;
}
public void addItem(Consumable c)
{
if(items.contains(c))
{
items.get(items.indexOf(c)).addOne();
}
else
{
items.add(c);
}
}
public ArrayList<Consumable> getItems() {
return items;
}
public void useItem(Consumable c)
{
if(items.get(items.indexOf(c)).getNumberOf() > 1)
{
items.get(items.indexOf(c)).reduceOne();
if(items.get(items.indexOf(c)).getNumberOf() == 0)
{
items.remove(c);
}
}
else
{
items.remove(c);
}
}
public void showItems()
{
if(!items.isEmpty())
{
System.out.println("Your items: ");
for (int i = 0; i < items.size(); i++) {
System.out.println((i + 1) + "." + items.get(i).getClass().getSimpleName() + ": " + items.get(i).getNumberOf());
}
}
}
public abstract void levelUp();
public void addLevel(){level++;}
public Boolean needLevelUp()
{
switch (this.level)
{
case (1): {
if (xp >= 50) {
return true;
}
break;
}
case (2):
{
if(xp >= 150)
{
return true;
}
break;
}
case (3):
{
if(xp >= 300)
{
return true;
}
break;
}
case (4):
{
if(xp > 500)
{
return true;
}
break;
}
}
return false;
}
public void showInf()
{
System.out.printf("""
level: %d
HP: %d
MP: %d
Maximum defence: %d
armor:
durability: %d
defensePercentage: %d
weapon:
damage: %d
mana cost: %d
""", getLevel(), getHp(), getMp(), getMaxDefense(), armor.getDurability(), armor.getDefensePercentage(), weapon.getDamage(), weapon.getManaCost());
}
@Override
public boolean isAlive() {
return hp > 0;
}
public void fillMana(int mana) {
mp += mana;
if (mp > maxMP) {
@@ -55,6 +231,63 @@ public abstract class Player {
}
}
public void addXp(int XP)
{
if(xp < 600)
{
this.xp += XP;
System.out.printf("You received %d Xp " + Colors.ORANGE +
"[" + Colors.RESET + "Your Xp: %d" + Colors.ORANGE + "]\n", XP, this.xp);
}
}
public void addXp(Enemy e)
{
if(xp < 600)
{
int amount = 0;
if (e instanceof Goblin) {
amount = 50;
} else if (e instanceof Skeleton) {
amount = 70;
} else if (e instanceof Vampire) {
amount = 90;
}
this.xp += amount;
System.out.printf("You received %d Xp " + Colors.ORANGE +
"[" + Colors.RESET + "Your Xp: %d" + Colors.ORANGE + "]\n" + Colors.RESET, amount, this.xp);
}
}
public void addCoin(int amount)
{
this.coin += amount;
System.out.println(Colors.COIN + "+" + amount + " coins!" + Colors.RESET);
}
public void addCoin(Enemy e)
{
int amount = 0;
if (e instanceof Goblin) {
amount = 10;
} else if (e instanceof Skeleton) {
amount = 25;
} else if (e instanceof Vampire) {
amount = 60;
}
this.coin += amount;
System.out.println("You gained " + Colors.COIN + amount + " coins" + Colors.RESET + ".");
}
public int getCoin() {
return coin;
}
public void setCoin(int coin) {
this.coin = coin;
}
public String getName() {
return name;
@@ -64,20 +297,34 @@ public abstract class Player {
return hp;
}
@Override
public void setHp(int hp) {
this.hp = hp;
}
public int getMaxHP() {
return maxHP;
}
public int getMp() {
return mp;
public void setMaxHP(int maxHP) {
this.maxHP = maxHP;
}
@Override
public int getMaxMP() {
return maxMP;
}
public void setMp(int mp) {
this.mp = mp;
}
public void setMaxMP(int maxMP) {
this.maxMP = maxMP;
}
public int getMp() {
return mp;
}
public Weapon getWeapon() {
return weapon;
}
@@ -86,4 +333,46 @@ public abstract class Player {
return armor;
}
public int getDamage() {
return damage;
}
public void setDamage(int baseDamage){this.damage = baseDamage;}
public int getXp(){return xp;}
public int getLevel(){return level;}
public int getMaxDefense() {
return maxDefense;
}
public void setMaxDefense(int maxDefense) {
this.maxDefense = maxDefense;
}
public Boolean getSkeletonKey() {
return skeletonKey;
}
public void setSkeletonKey(Boolean skeletonKey) {
this.skeletonKey = skeletonKey;
}
public Boolean getVampireKey() {
return vampireKey;
}
public void setVampireKey(Boolean vampireKey) {
this.vampireKey = vampireKey;
}
public Boolean getGoblinKey() {
return goblinKey;
}
public void setGoblinKey(Boolean goblinKey) {
this.goblinKey = goblinKey;
}
}
@@ -0,0 +1,132 @@
package org.project.entity.players;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.item.armors.WizardArmor;
import org.project.item.weapons.Weapon;
import org.project.item.weapons.WizardWeapon;
public class Wizard extends Player{
public Wizard(String name, Weapon weapon, WizardArmor armor) {
super(name, 80, 80, weapon, armor,50 );
super.setDamage(35);
super.setMp(super.getMp() + armor.getBonus(this));
super.setMaxMP(super.getMp() + armor.getBonus(this));
}
@Override
public boolean heavyAttack(Entity target) {
if(super.getMp() > super.getWeapon().getManaCost()) {
super.setMp(super.getMp() - super.getWeapon().getManaCost());
System.out.printf("%s (Wizard) performed a triple attack!\n", super.name);
WizardWeapon wp = (WizardWeapon) super.getWeapon();
target.takeDamage(wp.tripleAttack());
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " for heavy attack.");
return false;
}
@Override
public boolean specialAbility(Entity target) {
if(super.getMp() > 75) {
super.setMp(super.getMp() - 75);
System.out.printf("%s (Wizard) uses special ability.\n", super.name);
WizardWeapon w = (WizardWeapon) super.getWeapon();
target.takeDamage(4*w.getDamage());
super.setHp(super.getHp() + ((6 * super.getMaxHP()) / 10));
return true;
}
System.out.println("You don't have enough " + Colors.BLUE + "Mana" + Colors.RESET + " for your special ability.");
return false;
}
public void takeDamage(Entity attacker ,int damage) {
super.takeDamage(damage);
WizardArmor w = (WizardArmor) super.getArmor();
w.reflect(attacker, damage);
}
@Override
public void levelUp() {
if(super.getLevel() < 5) {
addLevel();
System.out.printf("You leveled up! " +Colors.ORANGE +"(" +Colors.RESET +"your level: " + Colors.LIGHTBLUE + "%d" + Colors.ORANGE +")\n" + Colors.RESET, super.getLevel());
switch (getLevel())
{
case (2):
{
super.setMaxDefense(super.getMaxDefense() + ((30*super.getMaxDefense()) / 100));
System.out.println("Your maximum defence is know 30% higher");
super.setMaxHP(super.getMaxHP() + ((10*super.getMaxHP()) / 100));
System.out.println("Your maximum HP is know 10% higher");
break;
}
case(3):
{
WizardArmor wizardArmor = (WizardArmor) super.getArmor();
wizardArmor.setBonusPercent(2*wizardArmor.getBonusPercent());
System.out.println("Bonus percentage has doubled");
wizardArmor.setReflectPercent(wizardArmor.getReflectPercent() + ((30*wizardArmor.getReflectPercent()) / 100));
System.out.println("Reflect percentage increased");
break;
}
case(4):
{
super.setMaxHP(super.getMaxMP() + ((30*super.getMaxMP()) / 100 ));
System.out.println("Your maximum HP is know 30% higher");
WizardWeapon wizardWeapon = (WizardWeapon) super.getWeapon();
wizardWeapon.setManaCost(wizardWeapon.getManaCost() - ((35* wizardWeapon.getManaCost()) / 100 ));
System.out.println("Your weapon mana cost decreased by 35%");
super.setDamage(super.getDamage() + ((50*super.getDamage()) / 100 ));
System.out.println("Your base damage is know 50% higher");
break;
}
case (5):
{
super.setMaxMP(super.getMaxMP() + ((50*super.getMaxMP()) / 100 ));
System.out.println("Your maximum mana is know 50% higher");
super.setMaxHP(super.getMaxHP() + ((40*super.getMaxHP()) / 100 ));
System.out.println("Your maximum HP is know 40% higher");
super.setMaxDefense(super.getMaxDefense() + ((50*super.getMaxDefense()) / 100));
System.out.println("Your maximum defence is know 50% higher");
break;
}
}
}
super.setHp(super.getMaxHP());
super.fillMana(super.getMaxMP());
super.getArmor().repair();
System.out.println();
showInf();
}
@Override
public void showInf() {
WizardArmor w = (WizardArmor) super.getArmor();
System.out.println("Your information:");
System.out.printf("""
level: %d
HP: %d
MP: %d
Coin: %d
Maximum defence: %d
Special damage(in special attack): %d
armor:
durability: %d
defensePercentage: %d
Bonus percentage: %d
Reflect percent: %d
weapon:
damage: %d
mana cost: %d
""", super.getLevel(), super.getHp(), super.getMp(), super.getCoin(), super.getMaxDefense(),
4*super.getWeapon().getDamage(), w.getDurability(), w.getDefensePercentage(),
w.getBonusPercent(), w.getReflectPercent(), super.getWeapon().getDamage(), super.getWeapon().getManaCost());
}
}
@@ -1,11 +0,0 @@
package org.project.item;
import org.project.entity.Entity;
public interface Item {
void use(Entity target);
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
}
@@ -1,42 +1,88 @@
package org.project.item.armors;
// TODO: UPDATE IMPLEMENTATION
public abstract class Armor {
private int defense;
private int maxDefense;
public abstract class Armor{
private int defensePercentage;
private int maxPercentage;
private int durability;
private int maxDurability;
private boolean isBroke;
public Armor(int defense, int durability) {
this.defense = defense;
this.durability = durability;
public Armor(int maxPercentage, int maxDurability) {
this.maxPercentage = maxPercentage;
this.defensePercentage = maxPercentage;
this.maxDurability = maxDurability;
this.durability = maxDurability;
}
public void checkBreak() {
if (durability <= 0) {
isBroke = true;
defense = 0;
defensePercentage = 0;
System.out.println("Your armor broke!");
}
}
// TODO: (BONUS) UPDATE THE REPAIR METHOD
public void repair() {
isBroke = false;
defense = maxDefense;
defensePercentage = maxPercentage;
durability = maxDurability;
}
public int getDefense() {
public int getDefense(int damage) {
int defense = (defensePercentage * damage) / 100;
this.defensePercentage -= (10 * this.defensePercentage) / 100;
reduceDurability(15);
return defense;
}
public int getDurability() {
return durability;
}
public void setDurability(int durability) {
this.durability = durability;
}
public void reduceDurability(int percentage)
{
if(durability < percentage)
{
this.durability = 0;
}
else {
this.durability -= (percentage * this.durability) / 100;
}
}
public void setMaxDurability(int maxDurability)
{
this.maxDurability = maxDurability;
}
public int getMaxDurability() {
return maxDurability;
}
public void setMaxPercentage(int maxPercentage) {
this.maxPercentage = maxPercentage;
}
public int getMaxPercentage() {
return maxPercentage;
}
public int getDefensePercentage() {
return defensePercentage;
}
public boolean isBroke() {
return isBroke;
}
public void setBroke(boolean broke) {
isBroke = broke;
}
}
@@ -0,0 +1,35 @@
package org.project.item.armors;
import java.util.Random;
public class AssassineArmor extends Armor{
private int dodgeChance;
public AssassineArmor() {
super(12, 100);
this.dodgeChance = 10;
}
@Override
public int getDefense(int damage) {
if(new Random().nextInt(100) < dodgeChance)
{
System.out.println("Assassin dodged the Hit!");
super.reduceDurability(20);
return damage;
}
else {
return super.getDefense(damage);
}
}
public int getDodgeChance() {
return dodgeChance;
}
public void setDodgeChance(int dodgeChance) {
this.dodgeChance = dodgeChance;
}
}
@@ -1,6 +1,12 @@
package org.project.item.armors;
import org.project.entity.Entity;
// TODO: UPDATE IMPLEMENTATION
public class KnightArmor {
public class KnightArmor extends Armor {
public KnightArmor() {
super(80, 100);
}
// TODO: DESIGN ARMOR'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
}
@@ -0,0 +1,48 @@
package org.project.item.armors;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.players.Wizard;
public class WizardArmor extends Armor{
//Reflects enemy attacks back to itself.
//you get mana bonus
private int bonusPercent;
private int reflectPercent;
public WizardArmor() {
super(10, 75);
this.bonusPercent = 15;
this.reflectPercent = 20;
}
public void reflect(Entity attacker, int damage)
{
System.out.printf( Colors.ORANGE + "%d" + Colors.RESET + " of damage reflected.\n", (reflectPercent*damage) / 100);
attacker.takeDamage((reflectPercent*damage) / 100);
super.reduceDurability(12);
}
public int getBonus(Wizard w)
{
return (bonusPercent*w.getMaxMP()) / 100;
}
public int getBonusPercent() {
return bonusPercent;
}
public void setBonusPercent(int bonusPercent) {
this.bonusPercent = bonusPercent;
}
public int getReflectPercent() {
return reflectPercent;
}
public void setReflectPercent(int reflectPercent) {
this.reflectPercent = reflectPercent;
}
}
@@ -1,8 +1,47 @@
package org.project.item.consumables;
import org.project.entity.Entity;
// TODO: UPDATE IMPLEMENTATION
public abstract class Consumable {
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
private String name;
private int value;
int numberOf;
private final String targetType;
public Consumable(String name, int value, String targetType) {
this.name = name;
this.value = value;
this.targetType = targetType;
this.numberOf = 1;
}
public abstract void use(Entity entity);
public abstract String toString();
public String getName() {
return name;
}
public String getTargetType() {
return targetType;
}
public void addOne()
{
numberOf++;
}
public void reduceOne(){numberOf--;}
public int getNumberOf() {
return numberOf;
}
public int getValue() {
return value;
}
}
@@ -0,0 +1,41 @@
package org.project.item.consumables;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.enemies.Dragon;
import org.project.entity.enemies.Enemy;
import java.util.Collections;
import java.util.Random;
public class CursedDagger extends Consumable{
public CursedDagger() {
super("Cursed Dagger", 80, "Enemy");
}
@Override
public void use(Entity entity) {
Enemy e = (Enemy) entity;
if(!(entity instanceof Dragon))
{
if (new Random().nextInt(100) < 30) {
System.out.println("Cursed Dagger strikes true!");
System.out.println("Enemy " + Colors.RED + "died" + Colors.RESET + "instantly.");
e.setHp(0);
} else {
System.out.println("Cursed dagger fails.");
e.takeDamage(10);
}
}
else
{
System.out.println("Cursed Dagger has no effect on Dragon");
}
}
@Override
public String toString() {
return "Cursed Dagger:\n" + Colors.COIN + "80 coins" +Colors.RESET +"\n30% chance to instantly kill a enemy(do not work for dragon). if it fails, deals 10 damage.";
}
}
@@ -0,0 +1,28 @@
package org.project.item.consumables;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.players.Player;
import java.util.Collections;
public class ElixirOfLife extends Consumable{
public ElixirOfLife() {
super("Elixir Of Life", 100, "Player");
}
@Override
public void use(Entity entity) {
System.out.println(Colors.RED +"Life" +Colors.RESET+ " and " + Colors.BLUE+ "Mana"+ Colors.RESET+ "restored.");
Player p = (Player) entity;
p.setHp(p.getMaxHP());
p.setMp(p.getMaxMP());
}
@Override
public String toString() {
return "Elixir Of Life:\n" + Colors.COIN + "100 coins" +Colors.RESET +"\nFully restores HP and Mana.";
}
}
@@ -1,16 +0,0 @@
package org.project.item.consumables;
import org.project.entity.Entity;
// TODO: UPDATE IMPLEMENTATION
public class Flask {
/*
THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN.
*/
// TODO: UPDATE USE METHOD
@Override
public void use(Entity target) {
target.heal(target.getMaxHP() / 10);
}
}
@@ -0,0 +1,23 @@
package org.project.item.consumables;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.enemies.Enemy;
public class GoblinGland extends Consumable{
public GoblinGland() {
super("Goblin Gland", 15, "Enemy");
}
@Override
public void use(Entity entity) {
System.out.println("Enemy is confused!");
Enemy e = (Enemy) entity;
e.setConfuse(true);
}
@Override
public String toString() {
return "Goblin Gland:\n" + Colors.COIN + "15 coins" +Colors.RESET +"\nThrow to confuse an enemy for 1 turn(They miss their attack).";
}
}
@@ -0,0 +1,24 @@
package org.project.item.consumables;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.players.Player;
public class HealthPotion extends Consumable{
public HealthPotion() {
super("Health Potion", 20, "Player");
}
@Override
public void use(Entity entity) {
System.out.println("You drink The potion. +20 " + Colors.HEAL + "HP" + Colors.RESET + ".");
Player p = (Player) entity;
p.setHp(p.getHp() + 30);
}
@Override
public String toString() {
return "Health Potion:\n" + Colors.COIN + "20 coins" +Colors.RESET +"\nRestores 30 HP instantly.";
}
}
@@ -0,0 +1,31 @@
package org.project.item.consumables;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.enemies.Skeleton;
import org.project.entity.enemies.Vampire;
public class HolyWater extends Consumable{
public HolyWater() {
super("Holy Water", 40, "Enemy");
}
@Override
public void use(Entity entity) {
if(entity instanceof Skeleton || entity instanceof Vampire)
{
System.out.println("Holy water burns the enemy!");
entity.takeDamage(50);
}
else
{
System.out.println("You can't use Holy Water on non-Skeleton|Vampire enemy!");
}
}
@Override
public String toString() {
return "Holy water:\n" + Colors.COIN + "40 coins" +Colors.RESET + "\nDeals 50 holy damage to undead(Skeleton, Vampire).";
}
}
@@ -0,0 +1,23 @@
package org.project.item.consumables;
import org.project.Colors;
import org.project.entity.Entity;
import org.project.entity.players.Player;
public class ManaPotion extends Consumable{
public ManaPotion() {
super("Mana Potion", 20, "Player");
}
@Override
public void use(Entity entity) {
Player p = (Player) entity;
System.out.println("You drink The potion. +20 " + Colors.BLUE + "Mana" + Colors.RESET + ".");
p.setMp(p.getMp() + 20);
}
@Override
public String toString() {
return "Mana Potion:\n" +Colors.COIN + "20 coins" +Colors.RESET +"\nRestores 20 MP instantly";
}
}
@@ -0,0 +1,61 @@
package org.project.item.weapons;
import org.project.Colors;
import org.project.entity.players.Assassin;
import java.util.Random;
public class AssassineWeapon extends Weapon{
private int silenceChance;
private int bleedChance;
private int bleedDamage;
private int bleedingRound;
public AssassineWeapon() {
super(45, 55);
this.bleedChance = 30;
this.bleedDamage = 10;
this.bleedingRound = 1;
}
public Boolean bleed()
{
return new Random().nextInt(100) < bleedChance;
}
public int criticalHit()
{
return 3*super.getDamage();
}
public void manaGain(Assassin assassin, int amount)
{
System.out.printf("%d of damage gained as " + Colors.BLUE + "Mana.\n" + Colors.RESET, amount);
assassin.fillMana(amount);
}
public int getBleedingRound() {
return bleedingRound;
}
public void setBleedingRound(int bleedingRound) {
this.bleedingRound = bleedingRound;
}
public int getBleedChance() {
return bleedChance;
}
public void setBleedChance(int bleedChance) {
this.bleedChance = bleedChance;
}
public int getBleedDamage() {
return bleedDamage;
}
public void setBleedDamage(int bleedDamage) {
this.bleedDamage = bleedDamage;
}
}
@@ -5,22 +5,34 @@ import org.project.entity.Entity;
import java.util.ArrayList;
// TODO: UPDATE IMPLEMENTATION
public class Sword {
public class Sword extends Weapon {
/*
THIS IS AN EXAMPLE OF A WEAPON DESIGN.
*/
int abilityCharge;
private int specialDamage;
public Sword() {
super(65, 50);
this.specialDamage = 85;
// 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 int getSpecialDamage() {
return specialDamage;
}
public void setSpecialDamage(int specialDamage) {
this.specialDamage = specialDamage;
}
}
@@ -1,9 +1,7 @@
package org.project.item.weapons;
import org.project.entity.Entity;
// TODO: UPDATE IMPLEMENTATION
public abstract class Weapon {
public abstract class Weapon{
private int damage;
private int manaCost;
@@ -16,15 +14,18 @@ public abstract class Weapon {
this.manaCost = manaCost;
}
@Override
public void use(Entity target) {
target.takeDamage(damage);
}
public int getDamage() {
return damage;
}
public void setDamage(int damage) {
this.damage = damage;
}
public void setManaCost(int manaCost) {
this.manaCost = manaCost;
}
public int getManaCost() {
return manaCost;
}
@@ -0,0 +1,14 @@
package org.project.item.weapons;
import org.project.entity.Entity;
public class WizardWeapon extends Weapon{
public WizardWeapon() {
super(35, 65);
}
public int tripleAttack()
{
return 3*super.getDamage();
}
}
@@ -1,27 +1,110 @@
package org.project.location;
import org.project.entity.enemies.Enemy;
import org.project.entity.enemies.Goblin;
import org.project.entity.enemies.Skeleton;
import org.project.entity.enemies.Vampire;
import java.util.ArrayList;
public class Location {
private String name;
// private Boolean isDiscovered;
private int skeleton = 0;
private int vampire = 0;
private int goblin = 0;
private ArrayList<Enemy> enemies;
public Location(ArrayList<Location> locations, ArrayList<Enemy> enemies) {
this.locations = locations;
this.enemies = enemies;
public Location(String name) {
this.name = name;
this.enemies = new ArrayList<>();
// this.isDiscovered = false;
}
public void defeatEnemy(Enemy enemy)
{
enemies.remove(enemy);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append(name).append("\n").append("Enemies: \n");
if(skeleton > 0)
{
s.append("Skeleton: ").append(skeleton).append("\n");
}
if(vampire > 0)
{
s.append("Vampire: ").append(vampire).append("\n");
}
if(goblin > 0)
{
s.append("Goblin: ").append(goblin).append("\n");
}
return s.toString();
}
public boolean checkLives()
{
for (Enemy enemy : enemies) {
if (enemy.isAlive()) {
return true;
}
}
return false;
}
public void resetLives()
{
for (Enemy enemy : enemies) {
enemy.setHp(enemy.getMaxHP());
enemy.setHaveBleed(false);
enemy.setBleedingRound(0);
}
}
public String getName() {
return name;
}
public ArrayList<Location> getLocations() {
return locations;
public int getGoblin() {
return goblin;
}
public int getVampire() {
return vampire;
}
public int getSkeleton() {
return skeleton;
}
public void addEnemy(Enemy enemy)
{
enemies.add(enemy);
if(enemy instanceof Skeleton)
{
skeleton++;
} else if (enemy instanceof Vampire)
{
vampire++;
}
else if(enemy instanceof Goblin)
{
goblin++;
}
}
// public Boolean isDiscovered() {
// return isDiscovered;
// }
//
// public void setDiscovered(Boolean discovered) {
// isDiscovered = discovered;
// }
public ArrayList<Enemy> getEnemies() {
return enemies;
}
Binary file not shown.
Binary file not shown.
+184 -152
View File
@@ -1,175 +1,207 @@
# Fourth Assignment - Java Knight ⚔️
A turn-based RPG with Roguelike elements which can be run in the terminal.
# ⚔️ Java Knight A Text-Based RPG
### **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!*
# Prologue: The Legend of 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**.
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!
⚠️ **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.
## 🎮 Gameplay Overview
🎯 **Your goal is not just to complete the assignment but to learn and apply OOP effectively!**
- Turnbased combat. Each turn you can choose **one** of the following actions:
- **Light attack** quick, low damage.
- **Heavy attack** slower but higher damage.
- **Special attack** unique to your hero (see below).
- **Heal** restore some HP.
- **Defend** reduce incoming damage until your next turn.
- **Use item** consume one of your consumable items.
### **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.
- Explore **different locations**, fight enemies, earn **XP** (to level up) and **coins** (to buy items).
- Collect **three keys** (drop randomly from enemies) to unlock the **Dragons castle**.
- Defeat the **Dragon** to save the city and win the game.
- **If your hero dies, the game resets** no second chances.
---
## Tasks 📝
## 🛡️ Heroes
### 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 🌲
### 🗡️ Knight
- **Highest base damage**.
- **Weapon (Sword):** High base damage + bonus special damage.
- **Special attack:** Deals bonus sword damage **and stuns** the enemy (enemy loses its next turn).
- **Armor (Shield):** Blocks **80% of all damage** best defence.
- **Low HP** but very tanky.
- **Lowest mana**.
- **Passive:** Minimum damage reduction is higher than any other hero.
A well-structured OOP hierarchy is crucial. Avoid duplicating code by placing shared logic in abstract classes.
### 🔮 Wizard
- **Highest HP**, average mana.
- **Weapon (Wooden Staff):** Allows a **triple attack** (three strikes in one turn).
- **Armor (Robe):** Low defence, but **reflects some attacks** back to the enemy, give you some **mana bunos**
- **Special attack (Destructive Spell):** Massive damage to one enemy **and heals the Wizard** for a portion of that damage.
- **Low base damage** relies on special and triple attack.
- **Passive:** Worst damage reduction you feel every hit.
- **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
### 🌙 Assassin
- **Highest mana**.
- **Weapon (Dagger):** Each normal hit **restores mana**.
- **Armor (Leather):** Chance to **dodge** incoming attacks entirely; if not, moderate defence.
- **Special attack:** Dodges the enemys next attack **and** lands a guaranteed **critical hit** on that enemy.
- **Mana sustain** allows frequent special attacks.
![structure](Readme_Pictures/structure.png)
### 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.
**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.
🔹 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]
> **Level cap:** All heroes can level up from **1 to 5**. Each level improves core stats and may unlock new bonuses.
---
Your Turn:
1. Light Attack | 2. Heavy Attack (-8 Mana) | 3. Defend (-6 Mana) | 4. Heal (-12 Mana) | 5. Shield Bash (-15 Mana)
```
## 👹 Enemies
```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).
### 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).
| Enemy | Key Features |
|-------------|-----------------------------------------------------------------------------------------------------------|
| 💀 Skeleton | Revives once after death with **half HP**. |
| 🧛 Vampire | Life steal each hit drains your HP and heals the Vampire. **Lowest key drop chance**. |
| 👺 Goblin | Low HP, high damage. Chance to land a **massive critical hit**. **Highest key drop chance**. |
| 🐉 Dragon | **Final boss.** Unblockable attacks armour and shield are useless. Cannot be defeated before level 45. |
---
## Evaluation Criteria ⚖
## 🧪 Consumable Items (Usable / Throwable)
| **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** |
| Item | Effect |
|----------------|-------------------------------------------------------------------------------------------------------------|
| Health Potion | Restores HP. |
| Mana Potion | Restores mana. |
| Elixir of Life | Fully restores HP and mana (rare). |
| Holy Water | Deals heavy damage to undead (Skeleton, Vampire). |
| Cursed Dagger | Throw at an enemy chance to instantly kill a enemy(do not work for dragon). if it fails, deals 10 damage. |
| Goblin Gland | Throw confuses the enemy and skip turn. |
## 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.
> Items can be **used on yourself** (e.g., potions) or **thrown at enemies** (e.g., Holy Water, Cursed Dagger).
## 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.
---
## 🗺️ Locations
- **Darkmire Hollow**
- **Grave of the Fallen**
- **Goblin Warrens**
- **Vampire's Embrace**
- **Blightmoor Cave**
- **Crypt of Bleeding Bones**
- **Cackling Caverns**
Each location contains 1 3 enemies. You can choose which location to enter next. Defeat all enemies in a location to earn rewards and move on.
---
## 💰 Progression & Victory Conditions
- **XP** Gain XP from each defeated enemy. Level up (1→5) to increase stats.
- **Coins** Earn coins from battles. Spend them to buy items.
- **Keys** Each enemy has a chance to drop a key. Collect **three keys** to unlock the Dragons castle.
- **Dragon fight** Even with three keys, you need **at least level 4** (better level 5) to survive the Dragons unblockable attacks.
- **Win** Defeat the Dragon → you save the city → game ends.
---
## 🧱 ObjectOriented Programming (OOP) Principles
This project is built on the four fundamental pillars of OOP: **Abstraction**, **Encapsulation**, **Inheritance**, and **Polymorphism**.
### 🔍 Abstraction
- **Abstract classes**:
- `Enemy` defines the core of any hostile creature.
- `Player` base for all playable heroes.
- `Consumable` parent for all usable/throwable items.
- **Interfaces**:
- `Entity` forces implementing classes to have common methods (e.g., `getName()`, `getHealth()`, `takeDamage()`).
- `ICombatAction` standardises combat actions like `attack()`, `defend()`, `specialAbility()`.
- **Implementation**:
- `Enemy` implements `Entity`.
- `Player` implements **both** `Entity` and `ICombatAction`.
- All concrete enemies (Skeleton, Vampire, etc.) extend `Enemy`.
- All heroes extend `Player`.
- All items extend `Consumable`.
### 🔒 Encapsulation
- All fields (attributes) in every class are declared **`private`** or **`protected`**.
- Public **getters and setters** are provided where necessary, allowing controlled access and modification.
- Internal state is hidden, reducing bugs and unintended interference.
### 👪 Inheritance
- **Player hierarchy**:
```
Player (abstract)
|── Knight
├── Wizard
└── Assassin
```
- Enemy hierarchy:
```
Enemy (abstract)
├── Skeleton
├── Vampire
├── Goblin
└── Dragon
```
- Consumable hierarchy (partial):
```
Consumable (abstract)
├── HealthPotion
├── ManaPotion
├── ElixirOfLife
├── HolyWater
├── CursedDagger
└── GoblinGland
```
### 🔄 Polymorphism
- Most methods are overridden in subclasses to provide specialised behaviour (e.g., `specialAbility()` behaves differently for Knight, Wizard, and Assassin).
- The game logic frequently refers to `Player` references that actually point to `Knight`, `Wizard`, or `Assassin` objects the correct overridden method is called automatically at runtime.
- Similarly, `Enemy` references hold concrete enemy types, and polymorphic calls handle unique abilities (e.g., `Skeleton` revives, `Vampire` steals life).
- This design allows adding new character types or enemies without changing existing combat or turnmanagement code.
---
## 📊 Class Hierarchy Diagram
![structure](./Readme_Pictures/deepseek_mermaid_20260517_fed73c.jpg)
---
### Compilation & Execution
- **Using an IDE (IntelliJ IDEA, Eclipse, etc.):**
Simply open the project and **run the `Main` class**.
- **Using command line: compile all files then run the Main class**
```bash
javac Main.java
java Main
Make sure you have Java 11 or higher installed.
---
<div align="center">
✨Have fun (and don't die)!✨
</div>
---
> *This README was fully generated by **DeepSeek** an AI assistant created by DeepSeek Company.*
![cover](Readme_Pictures/image.png)
###### - Born of God and Void. You shall seal the blinding light that plagues their dreams. You are the Vessel. You are the Java Knight.
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB