1 Commits
Author SHA1 Message Date
HesamGhazi d60b0ca25f Implementation of JAVA-Knight is complete 2026-06-21 19:48:04 +03:30
62 changed files with 1660 additions and 348 deletions
Generated
+1
View File
@@ -0,0 +1 @@
Main.java
+5
View File
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
@@ -1,15 +1,72 @@
/**
*
*
* @author [Hesam Ghazi]
* @version 1.0
*
* */
package org.project;
import org.project.entity.players.*;
import org.project.game.GameManager;
import org.project.item.weapons.*;
import org.project.location.Location;
import java.util.ArrayList;
import java.util.List;
import org.project.entity.enemies.*;
import org.project.utils.ConsoleColors;
public class Main {
public static void main(String[] args) {
// TODO: ADD LOCATIONS TO YOUR GAME
List<Location> locations = new ArrayList<>();
// Create weapons
Sword sword = new Sword();
Staff staff = new Staff();
Dagger dagger = new Dagger();
// TODO: IMPLEMENT GAMEPLAY
// Create player (choose your class)
Player player = new Knight("Sir Gallahad", sword);
// Player player = new Assassin("Shadow", dagger);
// Player player = new Wizard("Merlin", staff);
// Create locations
GameManager gameManager = getGameManager(player);
// Start the game with colorful intro
System.out.println(ConsoleColors.YELLOW_BOLD + "=== THE CURSE OF THE DRAGON ===" + ConsoleColors.RESET);
System.out.println(ConsoleColors.RED + "The land is cursed by an ancient dragon!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + "Collect the 3 keys to unlock the castle," + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + "defeat the Dragon, and break the curse!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.PURPLE + "----------------------------------------" + ConsoleColors.RESET);
gameManager.startGame();
}
private static GameManager getGameManager(Player player) {
Location town = new Location("Town of Beginnings",
"A peaceful town where your journey begins.");
Location forest = new Location("Dark Forest",
"A dense forest filled with dangerous creatures.");
Location cave = new Location("Cave of Echoes",
"A dark cave where the echoes of your footsteps are your only company.");
Location castle = new Location("Castle of Doom",
"The final stronghold of the evil dragon.");
// Connect locations
town.addConnectedLocation(forest);
town.addConnectedLocation(cave);
forest.addConnectedLocation(town);
forest.addConnectedLocation(cave);
cave.addConnectedLocation(town);
cave.addConnectedLocation(forest);
cave.addConnectedLocation(castle);
castle.addConnectedLocation(cave);
// Create game manager
GameManager gameManager = new GameManager(player, town);
// Add all locations to game manager
gameManager.addLocation(town);
gameManager.addLocation(forest);
gameManager.addLocation(cave);
gameManager.addLocation(castle);
return gameManager;
}
}
@@ -3,19 +3,23 @@ package org.project.entity;
public interface Entity {
void attack(Entity target);
void receiveAttack(int damage, boolean ignoreDefense);
void defend();
void heal(int health);
void heal(int amount);
void fillMana(int mana);
void fillMana(int amount);
void takeDamage(int damage);
boolean isAlive();
int getHP();
int getMP();
int getMaxHP();
int getMaxMP();
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
String getName();
}
@@ -0,0 +1,14 @@
package org.project.entity;
// This class implements five possible actions
public interface ICombatActions {
void lightAttack(Entity target);
void heavyAttack(Entity target);
void defend();
void heal();
void specialAbility(Entity target);
}
@@ -0,0 +1,34 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
public class Boss extends Enemy {
public Boss(String name, int maxHP, int maxMP, int damage, Weapon weapon, int xpReward) {
super(name, maxHP, maxMP, damage, weapon, xpReward);
}
@Override
public void attack(Entity target) {
if (stunned) {
System.out.println(name + " is stunned and cannot attack!");
return;
}
int totalDamage = damage;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
// Boss deals extra damage and ignores defense on heavy attacks
if (Math.random() < 0.3) {
totalDamage *= 1.5;
System.out.println("💢 " + name + " uses a powerful heavy attack!");
target.receiveAttack((int)totalDamage, true);
} else {
System.out.println("⚔️ " + name + " attacks!");
target.receiveAttack(totalDamage, false);
}
}
}
@@ -0,0 +1,215 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
/**
* Dragon - A powerful boss enemy with unique abilities
* Subclass of Boss with enhanced combat capabilities
*/
public class Dragon extends Boss {
private int fireBreathCooldown;
private boolean enraged;
private int enrageThreshold;
public Dragon(String name, int maxHP, int maxMP, int damage, Weapon weapon, int xpReward) {
super(name, maxHP, maxMP, damage, weapon, xpReward);
this.fireBreathCooldown = 0;
this.enraged = false;
this.enrageThreshold = maxHP / 3; // Enrages when HP drops below 33%
}
public Dragon() {
this("Ancient Dragon", 250, 100, 30, null, 600);
}
@Override
public void attack(Entity target) {
if (stunned) {
System.out.println(name + " is stunned and cannot attack!");
return;
}
// Check for enrage status
if (!enraged && hp <= enrageThreshold) {
enraged = true;
damage += 10;
System.out.println("🔥 " + name + " has become ENRAGED! Its attack power increases!");
}
// Decrease cooldown
if (fireBreathCooldown > 0) {
fireBreathCooldown--;
}
// Choose attack type based on conditions
double attackChoice = Math.random();
// Fire breath attack (cooldown of 3 turns)
if (attackChoice < 0.25 && fireBreathCooldown == 0 && mp >= 30) {
fireBreath(target);
}
// Tail swipe - area attack (if enraged)
else if (enraged && attackChoice < 0.45 && mp >= 15) {
tailSwipe(target);
}
// Normal attack
else {
normalAttack(target);
}
}
/**
* Fire Breath - Powerful area attack dealing high damage
*/
private void fireBreath(Entity target) {
if (mp < 30) {
System.out.println(name + " tries to breathe fire but lacks mana!");
normalAttack(target);
return;
}
mp -= 30;
fireBreathCooldown = 3;
int totalDamage = (damage * 2) + (weapon != null ? weapon.getDamage() : 0);
boolean ignoreDefense = true; // Fire burns through defenses
System.out.println("🐉 " + name + " unleashes a devastating FIRE BREATH!");
System.out.println("🔥 Flames engulf the battlefield!");
// Extra damage if enraged
if (enraged) {
totalDamage *= 1.5;
System.out.println("💢 Enraged fire breath deals extra damage!");
}
target.receiveAttack(totalDamage, ignoreDefense);
}
/**
* Tail Swipe - Heavy attack that can stun
*/
private void tailSwipe(Entity target) {
if (mp < 15) {
normalAttack(target);
return;
}
mp -= 15;
int totalDamage = (int)((damage * 1.7) + (weapon != null ? weapon.getDamage() * 0.5 : 0));
System.out.println("🐉 " + name + " performs a massive TAIL SWIPE!");
target.receiveAttack(totalDamage, false);
// Chance to stun the target
if (Math.random() < 0.3) {
System.out.println("💫 The tail swipe stuns the target!");
// Note: Would need to implement stun on Player
}
}
/**
* Normal attack with potential for extra effects
*/
private void normalAttack(Entity target) {
int totalDamage = damage + (weapon != null ? weapon.getDamage() : 0);
// Enraged dragons deal additional damage
if (enraged) {
totalDamage += 5;
}
// Chance for critical hit
boolean isCritical = Math.random() < 0.15;
if (isCritical) {
totalDamage *= 1.8;
System.out.println("💥 " + name + " lands a CRITICAL strike!");
}
System.out.println("🐉 " + name + " attacks with its claws!");
target.receiveAttack(totalDamage, false);
}
/**
* Dragon's special ability - Rage Roar (buffs itself)
*/
public void rageRoar() {
if (mp < 20) {
System.out.println("Not enough mana for Rage Roar!");
return;
}
mp -= 20;
damage += 5;
System.out.println("🐉 " + name + " lets out a terrifying RAGE ROAR!");
System.out.println("💪 " + name + "'s attack power increases permanently!");
}
/**
* Dragon's ultimate ability - Inferno (must be used when enraged)
*/
public void inferno(Entity target) {
if (!enraged) {
System.out.println(name + " must be enraged to use Inferno!");
return;
}
if (mp < 50) {
System.out.println("Not enough mana for Inferno!");
return;
}
mp -= 50;
int totalDamage = damage * 3 + (weapon != null ? weapon.getDamage() * 2 : 0);
System.out.println("🔥🐉 " + name + " summons an INFERNO of pure destruction!");
System.out.println("🌋 The ground erupts in flames!");
target.receiveAttack(totalDamage, true);
// After using inferno, dragon becomes exhausted
damage -= 5;
if (damage < 10) damage = 10;
System.out.println("😰 " + name + " is exhausted from the inferno!");
}
@Override
public void nextTurn() {
super.nextTurn();
// Reduce cooldowns
if (fireBreathCooldown > 0) {
fireBreathCooldown--;
}
}
@Override
public void receiveAttack(int damage, boolean ignoreDefense) {
super.receiveAttack(damage, ignoreDefense);
// Check if dragon becomes enraged due to damage
if (!enraged && hp <= enrageThreshold) {
enraged = true;
this.damage += 10;
System.out.println("🔥 " + name + " roars in pain and becomes ENRAGED!");
System.out.println("⚔️ Its damage increases by 10!");
}
}
@Override
public int getXPReward() {
// Enhanced XP reward for defeating a dragon
int baseReward = super.getXPReward();
return enraged ? baseReward + 100 : baseReward + 50;
}
// Getters
public boolean isEnraged() {
return enraged;
}
public int getFireBreathCooldown() {
return fireBreathCooldown;
}
}
@@ -1,33 +1,142 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
import org.project.utils.ConsoleColors;
// TODO: UPDATE IMPLEMENTATION
public abstract class Enemy {
Weapon weapon;
private int hp;
private int mp;
public Enemy(int hp, int mp, Weapon weapon) {
this.hp = hp;
this.mp = mp;
public abstract class Enemy implements Entity {
protected String name;
protected int hp;
protected int maxHP;
protected int mp;
protected int maxMP;
protected int damage;
protected Weapon weapon;
protected boolean stunned;
protected boolean defending;
protected boolean resurrected;
protected int xpReward;
public Enemy(String name, int maxHP, int maxMP, int damage, Weapon weapon, int xpReward) {
this.name = name;
this.maxHP = maxHP;
this.hp = maxHP;
this.maxMP = maxMP;
this.mp = maxMP;
this.damage = damage;
this.weapon = weapon;
this.stunned = false;
this.defending = false;
this.resurrected = false;
this.xpReward = xpReward;
}
@Override
public void takeDamage(int damage) {
hp -= damage;
public void attack(Entity target) {
if (stunned) {
System.out.println(ConsoleColors.YELLOW + name + " is stunned and cannot attack!" + ConsoleColors.RESET);
return;
}
public int getHp() {
int total = damage;
if (weapon != null) {
total += weapon.getDamage();
}
System.out.println(ConsoleColors.RED + name + " attacks!" + ConsoleColors.RESET);
target.receiveAttack(total, false);
}
@Override
public void receiveAttack(int damage, boolean ignoreDefense) {
if (defending && !ignoreDefense) {
damage /= 2;
defending = false;
}
hp -= damage;
if (hp < 0) {
hp = 0;
}
System.out.println(ConsoleColors.RED + name + " took " + damage + " damage." + ConsoleColors.RESET);
}
@Override
public void defend() {
defending = true;
System.out.println(ConsoleColors.CYAN + name + " is defending." + ConsoleColors.RESET);
}
@Override
public void heal(int amount) {
hp += amount;
if (hp > maxHP) {
hp = maxHP;
}
System.out.println(ConsoleColors.GREEN + name + " healed for " + amount + " HP." + ConsoleColors.RESET);
}
@Override
public void fillMana(int amount) {
mp += amount;
if (mp > maxMP) {
mp = maxMP;
}
System.out.println(ConsoleColors.BLUE + name + " restored " + amount + " Mana." + ConsoleColors.RESET);
}
@Override
public boolean isAlive() {
return hp > 0;
}
@Override
public int getHP() {
return hp;
}
public int getMp() {
@Override
public int getMP() {
return mp;
}
@Override
public int getMaxHP() {
return maxHP;
}
@Override
public int getMaxMP() {
return maxMP;
}
@Override
public String getName() {
return name;
}
public void stun() {
stunned = true;
System.out.println(ConsoleColors.YELLOW + name + " is stunned!" + ConsoleColors.RESET);
}
public boolean isStunned() {
return stunned;
}
public void nextTurn() {
if (stunned) {
stunned = false;
}
defending = false;
}
public int getXPReward() {
return xpReward;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public Weapon getWeapon() {
return weapon;
}
@@ -0,0 +1,37 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
import org.project.utils.ConsoleColors;
public class Goblin extends Enemy {
public Goblin() {
this("Goblin", 40, 15, 8, null, 35);
}
public Goblin(String name, int maxHP, int maxMP, int damage, Weapon weapon, int xpReward) {
super(name, maxHP, maxMP, damage, weapon, xpReward);
}
// Additional constructor for easier creation without weapon
public Goblin(String name, int maxHP, int maxMP, int damage, int xpReward) {
this(name, maxHP, maxMP, damage, null, xpReward);
}
@Override
public void attack(Entity target) {
if (stunned) {
System.out.println(ConsoleColors.YELLOW + name + " is stunned and cannot attack!" + ConsoleColors.RESET);
return;
}
int totalDamage = damage;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
System.out.println(ConsoleColors.RED + name + " swings a crude club!" + ConsoleColors.RESET);
target.receiveAttack(totalDamage, false);
}
}
@@ -1,6 +1,29 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
public class Skeleton extends Enemy {
// TODO: UPDATE IMPLEMENTATION
public class Skeleton {
// TODO: DESIGN ENEMY AND IMPLEMENT THE CONSTRUCTOR
public Skeleton() {
this("Skeleton", 50, 20, 10, null, 50);
}
public Skeleton(String name, int maxHP, int maxMP, int damage, Weapon weapon, int xpReward) {
super(name, maxHP, maxMP, damage, weapon, xpReward);
}
@Override
public void attack(Entity target) {
if (stunned) {
System.out.println(name + " is stunned and cannot attack!");
return;
}
// Skeletons have a chance to miss
if (Math.random() < 0.1) {
System.out.println(name + " attacks but misses!");
return;
}
super.attack(target);
}
}
@@ -0,0 +1,42 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
import org.project.utils.ConsoleColors;
public class Vampire extends Enemy {
public Vampire() {
super("Vampire", 65, 40, 15, null, 70);
}
public Vampire(String name, int maxHP, int maxMP, int damage, Weapon weapon, int xpReward) {
super(name, maxHP, maxMP, damage, weapon, xpReward);
}
// Additional constructor for easier creation without weapon
public Vampire(String name, int maxHP, int maxMP, int damage, int xpReward) {
this(name, maxHP, maxMP, damage, null, xpReward);
}
@Override
public void attack(Entity target) {
if (stunned) {
System.out.println(ConsoleColors.YELLOW + name + " is stunned and cannot attack!" + ConsoleColors.RESET);
return;
}
int totalDamage = damage;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
// Vampire has lifesteal - heals for 30% of damage dealt
System.out.println(ConsoleColors.RED + name + " strikes with vampiric claws!" + ConsoleColors.RESET);
target.receiveAttack(totalDamage, false);
int healAmount = (int)(totalDamage * 0.3);
heal(healAmount);
System.out.println(ConsoleColors.GREEN + name + " drains " + healAmount + " HP!" + ConsoleColors.RESET);
}
}
@@ -0,0 +1,57 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
public class Assassin extends Player {
private boolean invisible;
private boolean criticalAttack;
public Assassin(String name, Weapon weapon) {
super(name, 90, 120, 16, weapon);
this.invisible = false;
this.criticalAttack = false;
}
@Override
public void specialAbility(Entity target) {
if (mp < 22) {
System.out.println("Not enough Mana!");
return;
}
mp -= 22;
invisible = true;
criticalAttack = true;
System.out.println("🗡 Assassin vanished into the shadows.");
}
@Override
public void lightAttack(Entity target) {
int totalDamage = damage;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
if (criticalAttack) {
totalDamage *= 2;
System.out.println("💥 Critical Strike!");
criticalAttack = false;
}
target.receiveAttack(totalDamage, false);
}
@Override
public void receiveAttack(int damage, boolean ignoreDefense) {
if (invisible) {
System.out.println("Attack dodged!");
invisible = false;
return;
}
super.receiveAttack(damage, ignoreDefense);
}
// This method doesn't override anything from Entity interface
// It's just a convenience method that calls receiveAttack
public void takeDamage(int damage) {
receiveAttack(damage, false);
}
}
@@ -1,6 +1,37 @@
package org.project.entity.players;
// TODO: UPDATE IMPLEMENTATION
public class Knight {
// TODO: DESIGN KNIGHT'S WEAPON AND ARMOR AND IMPLEMENT THE CONSTRUCTOR
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
import org.project.utils.ConsoleColors;
public class Knight extends Player {
private boolean stunEnemy = false;
public Knight(String name, Weapon weapon) {
super(name, 120, 60, 18, weapon);
}
@Override
public void specialAbility(Entity target) {
if (mp < 20) {
System.out.println(ConsoleColors.RED + "Not enough Mana!" + ConsoleColors.RESET);
return;
}
mp -= 20;
int totalDamage = damage * 3;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
System.out.println(ConsoleColors.YELLOW_BOLD + "⚔ Shield Bash!" + ConsoleColors.RESET);
target.receiveAttack(totalDamage, false);
stunEnemy = true;
}
public boolean shouldStunEnemy() {
return stunEnemy;
}
public void resetStun() {
stunEnemy = false;
}
}
@@ -1,89 +1,186 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.item.armors.Armor;
import org.project.entity.ICombatActions;
import org.project.item.weapons.Weapon;
import org.project.utils.ConsoleColors;
// TODO: UPDATE IMPLEMENTATION
public abstract class Player {
public abstract class Player implements Entity, ICombatActions {
protected String name;
Weapon weapon;
Armor armor;
private int hp;
private int maxHP;
private int mp;
private int maxMP;
protected int hp;
protected int maxHP;
protected int mp;
protected int maxMP;
protected int damage;
protected int level;
protected int xp;
protected Weapon weapon;
protected boolean defending;
public Player(String name, int hp, int mp, Weapon weapon, Armor armor) {
public Player(String name, int maxHP, int maxMP, int damage, Weapon weapon) {
this.name = name;
this.hp = hp;
this.mp = mp;
this.maxHP = maxHP;
this.hp = maxHP;
this.maxMP = maxMP;
this.mp = maxMP;
this.damage = damage;
this.weapon = weapon;
this.armor = armor;
this.level = 1;
this.xp = 0;
this.defending = false;
}
@Override
public void attack(Entity target) {
target.takeDamage(weapon.getDamage());
lightAttack(target);
}
@Override
public void lightAttack(Entity target) {
int totalDamage = damage;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
System.out.println(ConsoleColors.CYAN_BOLD + name + " used Light Attack!" + ConsoleColors.RESET);
target.receiveAttack(totalDamage, false);
}
@Override
public void heavyAttack(Entity target) {
if (mp < 8) {
System.out.println(ConsoleColors.RED + "Not enough Mana!" + ConsoleColors.RESET);
return;
}
mp -= 8;
int totalDamage = damage * 2;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
System.out.println(ConsoleColors.PURPLE_BOLD + name + " used Heavy Attack!" + ConsoleColors.RESET);
target.receiveAttack(totalDamage, false);
}
@Override
public void defend() {
// TODO
if (mp < 5) {
System.out.println(ConsoleColors.RED + "Not enough Mana!" + ConsoleColors.RESET);
return;
}
@Override
public void takeDamage(int damage) {
hp -= damage - armor.getDefense();
mp -= 5;
defending = true;
System.out.println(ConsoleColors.CYAN + name + " is defending." + ConsoleColors.RESET);
}
@Override
public void heal(int health) {
hp += health;
public void heal() {
if (mp < 10) {
System.out.println(ConsoleColors.RED + "Not enough Mana!" + ConsoleColors.RESET);
return;
}
mp -= 10;
hp += 25;
if (hp > maxHP) {
hp = maxHP;
}
System.out.println(ConsoleColors.GREEN_BOLD + name + " healed for 25 HP." + ConsoleColors.RESET);
}
@Override
public void fillMana(int mana) {
mp += mana;
public void heal(int amount) {
hp += amount;
if (hp > maxHP) {
hp = maxHP;
}
System.out.println(ConsoleColors.GREEN + name + " healed for " + amount + " HP." + ConsoleColors.RESET);
}
@Override
public abstract void specialAbility(Entity target);
@Override
public void receiveAttack(int damage, boolean ignoreDefense) {
if (defending && !ignoreDefense) {
damage /= 2;
defending = false;
}
hp -= damage;
if (hp < 0) {
hp = 0;
}
System.out.println(ConsoleColors.RED + name + " took " + damage + " damage." + ConsoleColors.RESET);
}
// Convenience method
public void takeDamage(int damage) {
receiveAttack(damage, false);
}
@Override
public void fillMana(int amount) {
mp += amount;
if (mp > maxMP) {
mp = maxMP;
}
System.out.println(ConsoleColors.BLUE + name + " restored " + amount + " Mana." + ConsoleColors.RESET);
}
public String getName() {
return name;
@Override
public boolean isAlive() {
return hp > 0;
}
public int getHp() {
public void gainXP(int amount) {
xp += amount;
while (xp >= level * 100) {
xp -= level * 100;
level++;
maxHP += 10;
maxMP += 5;
damage += 3;
hp = maxHP;
mp = maxMP;
System.out.println(ConsoleColors.YELLOW_BOLD + "🌟 " + name + " reached level " + level + "!" + ConsoleColors.RESET);
}
}
@Override
public int getHP() {
return hp;
}
@Override
public int getMP() {
return mp;
}
@Override
public int getMaxHP() {
return maxHP;
}
public int getMp() {
return mp;
}
@Override
public int getMaxMP() {
return maxMP;
}
@Override
public String getName() {
return name;
}
public int getLevel() {
return level;
}
public Weapon getWeapon() {
return weapon;
}
public Armor getArmor() {
return armor;
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public int getDamage() {
return damage;
}
}
@@ -0,0 +1,28 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon;
import org.project.utils.ConsoleColors;
public class Wizard extends Player {
public Wizard(String name, Weapon weapon) {
super(name, 140, 100, 12, weapon);
}
@Override
public void specialAbility(Entity target) {
if (mp < 25) {
System.out.println(ConsoleColors.RED + "Not enough Mana!" + ConsoleColors.RESET);
return;
}
mp -= 25;
int totalDamage = damage * 4;
if (weapon != null) {
totalDamage += weapon.getDamage();
}
System.out.println(ConsoleColors.PURPLE_BOLD + "✨ Arcane Explosion!" + ConsoleColors.RESET);
target.receiveAttack(totalDamage, false);
heal(25);
System.out.println(ConsoleColors.GREEN + name + " restored 25 HP." + ConsoleColors.RESET);
}
}
@@ -0,0 +1,550 @@
package org.project.game;
import org.project.entity.enemies.*;
import org.project.entity.players.Player;
import org.project.entity.players.Knight;
import org.project.location.Location;
import org.project.item.consumables.Consumable;
import org.project.item.consumables.Flask;
import org.project.item.consumables.ManaPotion;
import org.project.utils.ConsoleColors;
import java.util.*;
/**
* GameManager.java
* Controls the main game loop, combat system, and progression mechanics.
*
* @author [Your Name]
* @version 1.0
*/
public class GameManager {
// Game state fields
private Player player;
private Location currentLocation;
private List<Location> locations;
private Scanner scanner;
private List<Consumable> inventory;
// Key collection - required to unlock the castle
private Set<Key> collectedKeys;
private boolean dragonDefeated;
private boolean castleUnlocked;
// Random number generator for enemy spawns and drops
private Random random;
/**
* Initializes the game with a player and starting location.
*
* @param player The player character (Knight, Assassin, or Wizard)
* @param startingLocation Where the adventure begins
*/
public GameManager(Player player, Location startingLocation) {
this.player = player;
this.currentLocation = startingLocation;
this.locations = new ArrayList<>();
this.scanner = new Scanner(System.in);
this.inventory = new ArrayList<>();
this.collectedKeys = new HashSet<>();
this.dragonDefeated = false;
this.castleUnlocked = false;
this.random = new Random();
// Starter items
inventory.add(new Flask());
inventory.add(new Flask());
inventory.add(new ManaPotion());
}
/**
* Adds a location to the game world.
*
* @param location New location to add
*/
public void addLocation(Location location) {
locations.add(location);
}
/**
* Starts the game and displays the welcome message.
*/
public void startGame() {
System.out.println(ConsoleColors.YELLOW_BOLD + "=== WELCOME TO THE REALM OF DRAGONS ===" + ConsoleColors.RESET);
System.out.println(ConsoleColors.RED + "A curse has befallen the land!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + "You must collect the 3 ancient keys to unlock the castle," + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + "defeat the Dragon, and break the curse!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.PURPLE + "----------------------------------------" + ConsoleColors.RESET);
System.out.println();
gameLoop();
}
/**
* Main game loop - handles player turns and game state.
*/
private void gameLoop() {
boolean running = true;
while (running) {
// Check for game over
if (!player.isAlive()) {
System.out.println(ConsoleColors.RED_BOLD + "💀 You have been defeated! Game Over!" + ConsoleColors.RESET);
break;
}
// Check for victory
if (dragonDefeated) {
System.out.println(ConsoleColors.YELLOW_BOLD + "🎉 CONGRATULATIONS! YOU HAVE DEFEATED THE DRAGON!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.GREEN_BOLD + "The curse is broken and the land is saved!" + ConsoleColors.RESET);
break;
}
displayLocationInfo();
System.out.println();
// Spawn enemy if location is empty
if (!currentLocation.hasEnemies()) {
spawnRandomEnemy();
}
checkCastleUnlock();
showMainMenu();
String choice = scanner.nextLine().trim();
switch (choice) {
case "1":
fightEnemy();
break;
case "2":
moveToLocation();
break;
case "3":
if (castleUnlocked) {
fightDragon();
} else {
System.out.println(ConsoleColors.RED + "The castle is locked! You need all 3 keys to enter." + ConsoleColors.RESET);
System.out.println(ConsoleColors.YELLOW + "Keys collected: " + collectedKeys.size() + "/3" + ConsoleColors.RESET);
}
break;
case "4":
useInventory();
break;
case "5":
displayStatus();
break;
case "6":
running = false;
System.out.println(ConsoleColors.YELLOW + "Thanks for playing!" + ConsoleColors.RESET);
break;
default:
System.out.println(ConsoleColors.RED + "Invalid choice. Try again." + ConsoleColors.RESET);
}
}
scanner.close();
}
/**
* Displays current location information and player stats.
*/
private void displayLocationInfo() {
System.out.println(ConsoleColors.CYAN_BOLD + "\n📍 " + currentLocation.getName() + ConsoleColors.RESET);
System.out.println(currentLocation.getDescription());
System.out.println(ConsoleColors.GREEN + "HP: " + player.getHP() + "/" + player.getMaxHP() +
" | " + ConsoleColors.BLUE + "MP: " + player.getMP() + "/" + player.getMaxMP() +
" | " + ConsoleColors.YELLOW + "Level: " + player.getLevel() + ConsoleColors.RESET);
}
/**
* Displays the main menu with all available actions.
* Castle option only appears when unlocked.
*/
private void showMainMenu() {
System.out.println("\n" + ConsoleColors.WHITE_BOLD + "What would you like to do?" + ConsoleColors.RESET);
System.out.println("1. ⚔️ Fight the enemy");
System.out.println("2. 🚶 Move to another location");
if (castleUnlocked) {
System.out.println(ConsoleColors.YELLOW_BOLD + "3. 🏰 Go to the Castle to fight the Dragon" + ConsoleColors.RESET);
} else {
System.out.println("3. 🔒 Castle (Locked - Need all 3 keys)");
}
System.out.println("4. 🎒 Use Inventory");
System.out.println("5. 📊 Check Status");
System.out.println("6. ❌ Quit");
// Show key progress if castle is locked
if (!castleUnlocked) {
System.out.println(ConsoleColors.CYAN + "Keys collected: " + collectedKeys.size() + "/3" + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + " - Goblin Key: " + (collectedKeys.contains(Key.GOBLIN_KEY) ? "" : "") + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + " - Skeleton Key: " + (collectedKeys.contains(Key.SKELETON_KEY) ? "" : "") + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + " - Vampire Key: " + (collectedKeys.contains(Key.VAMPIRE_KEY) ? "" : "") + ConsoleColors.RESET);
}
}
/**
* Spawns a random enemy with randomized stats.
* Enemy types: Goblin, Skeleton, Vampire.
*/
private void spawnRandomEnemy() {
int enemyType = random.nextInt(3);
Enemy enemy;
switch (enemyType) {
case 0:
int goblinHP = 40 + random.nextInt(20);
int goblinMP = 15 + random.nextInt(10);
int goblinDamage = 8 + random.nextInt(5);
int goblinXP = 35 + random.nextInt(20);
enemy = new Goblin("Goblin", goblinHP, goblinMP, goblinDamage, goblinXP);
break;
case 1:
int skeletonHP = 50 + random.nextInt(20);
int skeletonMP = 20 + random.nextInt(10);
int skeletonDamage = 10 + random.nextInt(5);
int skeletonXP = 50 + random.nextInt(25);
enemy = new Skeleton("Skeleton", skeletonHP, skeletonMP, skeletonDamage, null, skeletonXP);
break;
case 2:
int vampireHP = 65 + random.nextInt(25);
int vampireMP = 40 + random.nextInt(15);
int vampireDamage = 15 + random.nextInt(5);
int vampireXP = 70 + random.nextInt(30);
enemy = new Vampire("Vampire", vampireHP, vampireMP, vampireDamage, null, vampireXP);
break;
default:
enemy = new Goblin();
}
currentLocation.addEnemy(enemy);
System.out.println(ConsoleColors.RED_BOLD + "💀 A wild " + enemy.getName() + " appears!" + ConsoleColors.RESET);
}
/**
* Checks if all 3 keys have been collected and unlocks the castle.
*/
private void checkCastleUnlock() {
if (!castleUnlocked && collectedKeys.size() >= 3) {
castleUnlocked = true;
System.out.println(ConsoleColors.YELLOW_BOLD + "🔓 The castle gates have opened!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.GREEN + "You have collected all 3 keys! You may now face the Dragon." + ConsoleColors.RESET);
}
}
/**
* Starts combat with the enemy at the current location.
*/
private void fightEnemy() {
if (!currentLocation.hasEnemies()) {
System.out.println(ConsoleColors.YELLOW + "There are no enemies here!" + ConsoleColors.RESET);
return;
}
Enemy enemy = currentLocation.getEnemies().get(0);
combatLoop(enemy);
}
/**
* Moves the player to a connected location.
*/
private void moveToLocation() {
List<Location> connections = currentLocation.getConnectedLocations();
if (connections.isEmpty()) {
System.out.println(ConsoleColors.YELLOW + "There's nowhere to go from here." + ConsoleColors.RESET);
return;
}
System.out.println("\n" + ConsoleColors.CYAN + "Available destinations:" + ConsoleColors.RESET);
for (int i = 0; i < connections.size(); i++) {
System.out.println((i + 1) + ". " + connections.get(i).getName());
}
System.out.println("0. Cancel");
try {
int choice = Integer.parseInt(scanner.nextLine().trim());
if (choice > 0 && choice <= connections.size()) {
currentLocation = connections.get(choice - 1);
System.out.println(ConsoleColors.GREEN + "You travel to " + currentLocation.getName() + "..." + ConsoleColors.RESET);
System.out.println();
}
} catch (NumberFormatException e) {
System.out.println(ConsoleColors.RED + "Invalid input." + ConsoleColors.RESET);
}
}
/**
* Opens the inventory menu for using consumable items.
*/
private void useInventory() {
if (inventory.isEmpty()) {
System.out.println(ConsoleColors.YELLOW + "Your inventory is empty." + ConsoleColors.RESET);
return;
}
System.out.println("\n" + ConsoleColors.CYAN_BOLD + "Inventory:" + ConsoleColors.RESET);
for (int i = 0; i < inventory.size(); i++) {
System.out.println((i + 1) + ". " + inventory.get(i).getName());
}
System.out.println("0. Cancel");
try {
int choice = Integer.parseInt(scanner.nextLine().trim());
if (choice > 0 && choice <= inventory.size()) {
Consumable item = inventory.remove(choice - 1);
item.use(player);
}
} catch (NumberFormatException e) {
System.out.println(ConsoleColors.RED + "Invalid input." + ConsoleColors.RESET);
}
}
/**
* Displays player status including stats, equipment, and key progress.
*/
private void displayStatus() {
System.out.println("\n" + ConsoleColors.YELLOW_BOLD + "=== Player Status ===" + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + "Name: " + player.getName() + ConsoleColors.RESET);
System.out.println(ConsoleColors.YELLOW + "Level: " + player.getLevel() + ConsoleColors.RESET);
System.out.println(ConsoleColors.GREEN + "HP: " + player.getHP() + "/" + player.getMaxHP() + ConsoleColors.RESET);
System.out.println(ConsoleColors.BLUE + "MP: " + player.getMP() + "/" + player.getMaxMP() + ConsoleColors.RESET);
System.out.println(ConsoleColors.RED + "Damage: " + player.getDamage() + ConsoleColors.RESET);
System.out.println(ConsoleColors.PURPLE + "Weapon: " + (player.getWeapon() != null ? player.getWeapon().getName() : "None") + ConsoleColors.RESET);
System.out.println("Inventory Items: " + inventory.size());
System.out.println("\n" + ConsoleColors.YELLOW_BOLD + "=== Keys Collected ===" + ConsoleColors.RESET);
System.out.println("Goblin Key: " + (collectedKeys.contains(Key.GOBLIN_KEY) ? "" : ""));
System.out.println("Skeleton Key: " + (collectedKeys.contains(Key.SKELETON_KEY) ? "" : ""));
System.out.println("Vampire Key: " + (collectedKeys.contains(Key.VAMPIRE_KEY) ? "" : ""));
System.out.println("Total: " + collectedKeys.size() + "/3");
System.out.println("Castle: " + (castleUnlocked ? "🔓 UNLOCKED" : "🔒 LOCKED"));
}
/**
* Turn-based combat loop.
*
* @param enemy The enemy being fought
*/
private void combatLoop(Enemy enemy) {
System.out.println("\n" + ConsoleColors.RED_BOLD + "⚔️ Combat with " + enemy.getName() + "!" + ConsoleColors.RESET);
while (enemy.isAlive() && player.isAlive()) {
// Show combat status
System.out.println("\n" + ConsoleColors.RED + enemy.getName() + " HP: " + enemy.getHP() + "/" + enemy.getMaxHP() + ConsoleColors.RESET);
System.out.println(ConsoleColors.GREEN + "Your HP: " + player.getHP() + "/" + player.getMaxHP() + ConsoleColors.RESET);
System.out.println(ConsoleColors.BLUE + "Your MP: " + player.getMP() + "/" + player.getMaxMP() + ConsoleColors.RESET);
// Player action menu
System.out.println("\n" + ConsoleColors.WHITE_BOLD + "Choose your action:" + ConsoleColors.RESET);
System.out.println("1. ⚔️ Light Attack");
System.out.println("2. 💥 Heavy Attack (8 MP)");
System.out.println("3. ✨ Special Ability");
System.out.println("4. 🛡️ Defend (5 MP)");
System.out.println("5. 💚 Heal (10 MP)");
System.out.println("6. 🎒 Use Item");
String choice = scanner.nextLine().trim();
// Execute player action
switch (choice) {
case "1":
player.lightAttack(enemy);
break;
case "2":
player.heavyAttack(enemy);
break;
case "3":
player.specialAbility(enemy);
break;
case "4":
player.defend();
break;
case "5":
player.heal();
break;
case "6":
useInventory();
break;
default:
System.out.println(ConsoleColors.RED + "Invalid choice." + ConsoleColors.RESET);
continue;
}
// Enemy turn
if (enemy.isAlive()) {
// Knight's Shield Bash stuns the enemy
if (player instanceof Knight && ((Knight) player).shouldStunEnemy()) {
enemy.stun();
((Knight) player).resetStun();
}
enemy.nextTurn();
enemy.attack(player);
} else {
handleEnemyDefeat(enemy);
break;
}
}
}
/**
* Processes enemy defeat: grants XP, handles key drops, and recovers health.
*
* @param enemy The defeated enemy
*/
private void handleEnemyDefeat(Enemy enemy) {
// Grant XP
int xpReward = enemy.getXPReward();
player.gainXP(xpReward);
System.out.println(ConsoleColors.YELLOW_BOLD + "🏆 Defeated " + enemy.getName() + "! Gained " + xpReward + " XP." + ConsoleColors.RESET);
// Remove enemy from location
currentLocation.removeEnemy(enemy);
// Key drop (20% chance)
Key key = getKeyForEnemy(enemy);
if (key != null && !collectedKeys.contains(key)) {
if (random.nextDouble() < 0.2) {
collectedKeys.add(key);
System.out.println(ConsoleColors.GREEN_BOLD + "🔑 You obtained the " + key.getDisplayName() + "!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.CYAN + "Keys collected: " + collectedKeys.size() + "/3" + ConsoleColors.RESET);
checkCastleUnlock();
} else {
System.out.println(ConsoleColors.YELLOW + "No key dropped this time." + ConsoleColors.RESET);
}
} else if (key != null && collectedKeys.contains(key)) {
System.out.println(ConsoleColors.YELLOW + "You already have the " + key.getDisplayName() + "." + ConsoleColors.RESET);
}
// Item drop (30% chance)
if (random.nextDouble() < 0.3) {
Consumable drop = random.nextBoolean() ? new Flask() : new ManaPotion();
inventory.add(drop);
System.out.println(ConsoleColors.GREEN_BOLD + "💎 " + enemy.getName() + " dropped a " + drop.getName() + "!" + ConsoleColors.RESET);
}
// Full recovery after battle
player.heal(player.getMaxHP());
player.fillMana(player.getMaxMP());
System.out.println(ConsoleColors.GREEN + "🔄 You recovered fully after battle!" + ConsoleColors.RESET);
}
/**
* Determines which key an enemy can drop based on its type.
*
* @param enemy The enemy to check
* @return The key type, or null if no key is dropped
*/
private Key getKeyForEnemy(Enemy enemy) {
if (enemy instanceof Goblin) {
return Key.GOBLIN_KEY;
} else if (enemy instanceof Skeleton) {
return Key.SKELETON_KEY;
} else if (enemy instanceof Vampire) {
return Key.VAMPIRE_KEY;
}
return null;
}
/**
* Final boss battle against the Dragon.
* Only accessible after collecting all 3 keys.
*/
private void fightDragon() {
if (!castleUnlocked) {
System.out.println(ConsoleColors.RED + "The castle is locked! You need all 3 keys." + ConsoleColors.RESET);
return;
}
Dragon dragon = new Dragon("Ember, the Ancient Dragon", 300, 120, 35, null, 800);
System.out.println(ConsoleColors.RED_BOLD + "🐉 THE DRAGON EMERGES FROM THE CASTLE!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.RED + "Ember roars: 'You dare challenge me, mortal?'" + ConsoleColors.RESET);
System.out.println(ConsoleColors.YELLOW + "This is the final battle!" + ConsoleColors.RESET);
while (dragon.isAlive() && player.isAlive()) {
System.out.println("\n" + ConsoleColors.RED_BOLD + "🐉 DRAGON BOSS FIGHT!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.RED + "Dragon HP: " + dragon.getHP() + "/" + dragon.getMaxHP() + ConsoleColors.RESET);
System.out.println(ConsoleColors.GREEN + "Your HP: " + player.getHP() + "/" + player.getMaxHP() + ConsoleColors.RESET);
System.out.println(ConsoleColors.BLUE + "Your MP: " + player.getMP() + "/" + player.getMaxMP() + ConsoleColors.RESET);
if (dragon.isEnraged()) {
System.out.println(ConsoleColors.RED_BOLD + "🔥 The Dragon is ENRAGED!" + ConsoleColors.RESET);
}
System.out.println("\n" + ConsoleColors.WHITE_BOLD + "Choose your action:" + ConsoleColors.RESET);
System.out.println("1. ⚔️ Light Attack");
System.out.println("2. 💥 Heavy Attack (8 MP)");
System.out.println("3. ✨ Special Ability");
System.out.println("4. 🛡️ Defend (5 MP)");
System.out.println("5. 💚 Heal (10 MP)");
System.out.println("6. 🎒 Use Item");
String choice = scanner.nextLine().trim();
switch (choice) {
case "1":
player.lightAttack(dragon);
break;
case "2":
player.heavyAttack(dragon);
break;
case "3":
player.specialAbility(dragon);
break;
case "4":
player.defend();
break;
case "5":
player.heal();
break;
case "6":
useInventory();
break;
default:
System.out.println(ConsoleColors.RED + "Invalid choice." + ConsoleColors.RESET);
continue;
}
// Dragon's turn
if (dragon.isAlive()) {
if (player instanceof Knight && ((Knight) player).shouldStunEnemy()) {
dragon.stun();
((Knight) player).resetStun();
}
dragon.nextTurn();
dragon.attack(player);
} else {
dragonDefeated = true;
System.out.println(ConsoleColors.YELLOW_BOLD + "🏆 YOU HAVE DEFEATED THE DRAGON!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.GREEN_BOLD + "🎉 The curse is broken! The land is saved!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.YELLOW + "You are a true hero!" + ConsoleColors.RESET);
}
}
if (!player.isAlive()) {
System.out.println(ConsoleColors.RED_BOLD + "💀 The Dragon has defeated you!" + ConsoleColors.RESET);
System.out.println(ConsoleColors.RED + "Game Over!" + ConsoleColors.RESET);
}
}
// Getters
public Player getPlayer() {
return player;
}
public Location getCurrentLocation() {
return currentLocation;
}
public Set<Key> getCollectedKeys() {
return collectedKeys;
}
public boolean isCastleUnlocked() {
return castleUnlocked;
}
}
@@ -0,0 +1,17 @@
package org.project.game;
public enum Key {
GOBLIN_KEY("Goblin Key"),
SKELETON_KEY("Skeleton Key"),
VAMPIRE_KEY("Vampire Key");
private final String displayName;
Key(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
@@ -2,10 +2,25 @@ package org.project.item;
import org.project.entity.Entity;
public interface Item {
void use(Entity target);
public abstract class Item {
protected String name;
protected int value;
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
public Item(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
@Override
public String toString() {
return name;
}
}
@@ -1,42 +1,17 @@
package org.project.item.armors;
// TODO: UPDATE IMPLEMENTATION
public abstract class Armor {
private int defense;
private int maxDefense;
private int durability;
private int maxDurability;
import org.project.item.Item;
private boolean isBroke;
public abstract class Armor extends Item {
public Armor(int defense, int durability) {
protected int defense;
public Armor(String name, int defense, int value) {
super(name, value);
this.defense = defense;
this.durability = durability;
}
public void checkBreak() {
if (durability <= 0) {
isBroke = true;
defense = 0;
}
}
// TODO: (BONUS) UPDATE THE REPAIR METHOD
public void repair() {
isBroke = false;
defense = maxDefense;
durability = maxDurability;
}
public int getDefense() {
return defense;
}
public int getDurability() {
return durability;
}
public boolean isBroke() {
return isBroke;
}
}
@@ -0,0 +1,8 @@
package org.project.item.armors;
public class AssassinArmor extends Armor {
public AssassinArmor() {
super("Shadow Armor", 8, 145);
}
}
@@ -1,6 +1,8 @@
package org.project.item.armors;
// TODO: UPDATE IMPLEMENTATION
public class KnightArmor {
// TODO: DESIGN ARMOR'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
public class KnightArmor extends Armor {
public KnightArmor() {
super("Knight Armor", 12, 150);
}
}
@@ -0,0 +1,8 @@
package org.project.item.armors;
public class WizardArmor extends Armor {
public WizardArmor() {
super("Wizard Robe", 6, 140);
}
}
@@ -1,8 +1,10 @@
package org.project.item.consumables;
import org.project.entity.players.Player;
import org.project.item.Item;
// TODO: UPDATE IMPLEMENTATION
public abstract class Consumable {
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
}
public abstract class Consumable extends Item{
public Consumable(String name, int value) {
super(name, value);
}
public abstract void use(Player player); }
@@ -1,16 +1,19 @@
package org.project.item.consumables;
import org.project.entity.Entity;
import org.project.entity.players.Player;
import org.project.utils.ConsoleColors;
// TODO: UPDATE IMPLEMENTATION
public class Flask {
/*
THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN.
*/
public class Flask extends Consumable {
private final int healAmount = 30;
public Flask() {
super("Healing Flask", 40);
}
// TODO: UPDATE USE METHOD
@Override
public void use(Entity target) {
target.heal(target.getMaxHP() / 10);
public void use(Player player) {
player.heal(healAmount);
System.out.println(ConsoleColors.GREEN + player.getName() +
" recovered " + healAmount + " HP." + ConsoleColors.RESET);
}
}
@@ -0,0 +1,19 @@
package org.project.item.consumables;
import org.project.entity.players.Player;
import org.project.utils.ConsoleColors;
public class ManaPotion extends Consumable {
private final int mana = 30;
public ManaPotion() {
super("Mana Potion", 45);
}
@Override
public void use(Player player) {
player.fillMana(mana);
System.out.println(ConsoleColors.BLUE + player.getName() +
" recovered " + mana + " Mana." + ConsoleColors.RESET);
}
}
@@ -0,0 +1,7 @@
package org.project.item.weapons;
public class Dagger extends Weapon{
public Dagger() {
super("Dagger", 7, 80);
}
}
@@ -0,0 +1,7 @@
package org.project.item.weapons;
public class Staff extends Weapon {
public Staff() {
super("Magic Staff", 8, 120);
}
}
@@ -5,22 +5,12 @@ 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;
public Sword() {
// TODO: DESIGN SWORD'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
}
// TODO: (BONUS) UPDATE THE UNIQUE ABILITY
public void uniqueAbility(ArrayList<Entity> targets) {
abilityCharge += 2;
for (Entity target : targets) {
target.takeDamage(getDamage());
}
super("Sword", 10, 100);
}
}
@@ -1,35 +1,18 @@
package org.project.item.weapons;
import org.project.entity.Entity;
import org.project.item.Item;
// TODO: UPDATE IMPLEMENTATION
public abstract class Weapon {
private int damage;
private int manaCost;
public abstract class Weapon extends Item {
protected int damage;
/*
TODO: ADD OTHER REQUIRED AND BONUS ATTRIBUTES
*/
public Weapon(int damage, int manaCost) {
public Weapon(String name, int damage, int value) {
super(name, value);
this.damage = damage;
this.manaCost = manaCost;
}
@Override
public void use(Entity target) {
target.takeDamage(damage);
}
public int getDamage() {
return damage;
}
public int getManaCost() {
return manaCost;
}
/*
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/
}
@@ -1,28 +1,56 @@
package org.project.location;
import org.project.entity.enemies.Enemy;
import java.util.ArrayList;
import java.util.List;
public class Location {
private String name;
private String description;
private List<Location> connectedLocations;
private List<Enemy> enemies;
private ArrayList<Enemy> enemies;
public Location(String name, String description) {
this.name = name;
this.description = description;
this.connectedLocations = new ArrayList<>();
this.enemies = new ArrayList<>();
}
public Location(ArrayList<Location> locations, ArrayList<Enemy> enemies) {
this.locations = locations;
this.enemies = enemies;
public Location(String name, String description, List<Enemy> enemies) {
this(name, description);
this.enemies = enemies != null ? enemies : new ArrayList<>();
}
public String getName() {
return name;
}
public ArrayList<Location> getLocations() {
return locations;
public String getDescription() {
return description;
}
public ArrayList<Enemy> getEnemies() {
public List<Location> getConnectedLocations() {
return connectedLocations;
}
public void addConnectedLocation(Location location) {
connectedLocations.add(location);
}
public List<Enemy> getEnemies() {
return enemies;
}
public void addEnemy(Enemy enemy) {
enemies.add(enemy);
}
public void removeEnemy(Enemy enemy) {
enemies.remove(enemy);
}
public boolean hasEnemies() {
return !enemies.isEmpty();
}
}
@@ -0,0 +1,55 @@
package org.project.utils;
/**
* ConsoleColors - Utility class for ANSI color codes
* provides colorful console output
*/
public class ConsoleColors {
// Reset
public static final String RESET = "\u001B[0m";
// Regular Colors
public static final String BLACK = "\u001B[30m";
public static final String RED = "\u001B[31m";
public static final String GREEN = "\u001B[32m";
public static final String YELLOW = "\u001B[33m";
public static final String BLUE = "\u001B[34m";
public static final String PURPLE = "\u001B[35m";
public static final String CYAN = "\u001B[36m";
public static final String WHITE = "\u001B[37m";
// Bold
public static final String BLACK_BOLD = "\u001B[1;30m";
public static final String RED_BOLD = "\u001B[1;31m";
public static final String GREEN_BOLD = "\u001B[1;32m";
public static final String YELLOW_BOLD = "\u001B[1;33m";
public static final String BLUE_BOLD = "\u001B[1;34m";
public static final String PURPLE_BOLD = "\u001B[1;35m";
public static final String CYAN_BOLD = "\u001B[1;36m";
public static final String WHITE_BOLD = "\u001B[1;37m";
// Background
public static final String BLACK_BACKGROUND = "\u001B[40m";
public static final String RED_BACKGROUND = "\u001B[41m";
public static final String GREEN_BACKGROUND = "\u001B[42m";
public static final String YELLOW_BACKGROUND = "\u001B[43m";
public static final String BLUE_BACKGROUND = "\u001B[44m";
public static final String PURPLE_BACKGROUND = "\u001B[45m";
public static final String CYAN_BACKGROUND = "\u001B[46m";
public static final String WHITE_BACKGROUND = "\u001B[47m";
// High Intensity
public static final String RED_BRIGHT = "\u001B[91m";
public static final String GREEN_BRIGHT = "\u001B[92m";
public static final String YELLOW_BRIGHT = "\u001B[93m";
public static final String BLUE_BRIGHT = "\u001B[94m";
public static final String PURPLE_BRIGHT = "\u001B[95m";
public static final String CYAN_BRIGHT = "\u001B[96m";
public static final String WHITE_BRIGHT = "\u001B[97m";
// Special
public static final String BOLD = "\u001B[1m";
public static final String ITALIC = "\u001B[3m";
public static final String UNDERLINE = "\u001B[4m";
public static final String BLINK = "\u001B[5m";
}
Binary file not shown.
-175
View File
@@ -1,175 +0,0 @@
# Fourth Assignment - Java Knight ⚔️
A turn-based RPG with Roguelike elements which can be run in the terminal.
### **Prologue: The Legend of Javanest**
*For centuries, the land of Javanest lived in peace, until a magical Dragon attacked, plunging the realm into absolute darkness. With a wicked curse, the Dragon transformed the innocent people into horrific monsters: Goblins, Skeletons, and Vampires. Retreating to its impenetrable Castle, the Dragon divided the three keys to its lair and hid them among these cursed creatures. Now, it is your duty to step up and save the land. You must battle these monsters, recover the unique key from each monster type, and finally slay the Dragon to break the curse and restore peace to Javanest!*
### **Introduction**
Welcome to **Java knight**, a turn-based RPG inspired by Roguelike games! In this assignment, you will develop a **text-based role-playing game (RPG)**. This project is designed to rigorously test your understanding of **Object-Oriented Programming (OOP) principles**.
⚠️ **REQUIREMENT:** You **must** utilize all the OOP concepts you have learned so far—including *Inheritance, Interfaces, Abstract Classes, Encapsulation, Polymorphism, Overloading, and Overriding*. It is extremely important that you use everything in its right place. Your design and architecture will be graded based on how well you apply these principles to avoid code duplication and maintain a clean structure.
🎯 **Your goal is not just to complete the assignment but to learn and apply OOP effectively!**
### **What is a Turn-Based Game?**
In this combat system, two sides - which are usually the player's side and the enemy's side - attack each other in turns. The side which is not attacking can perform actions to avoid or deflect the enemy's attack.
### **Core Mechanics:**
- **Turn-based combat** Players and monsters take turns attacking each other.
- **Character classes with Unique Traits** Players can choose from archetypes like **Knight, Assassin, or Wizard**, each starting with distinctly different base stats.
- **Unified Mana/Stamina System** All player classes use a unified resource (Mana/Stamina) to perform actions.
- **Standardized Action Set** Every player character has exactly 5 specific actions available during their turn.
- **Experience & Leveling System** Earn XP based on enemy strength to automatically level up and increase your base stats.
- **Progression System** You cannot fight the Dragon immediately. You must farm enemies for a chance to drop their specific key, collect all three, and grow stronger first.
---
## Tasks 📝
### 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 🌲
A well-structured OOP hierarchy is crucial. Avoid duplicating code by placing shared logic in abstract classes.
- **Entities & Locations:** You have `Entity`, `Item`(Bonus) , and `Location`.
- **Players:** `Player` is an abstract class implementing `Entity`. Subclasses: `Wizard`, `Knight`, `Assassin`.
- **Base Stat Differences:** Each class must have distinct starting stats. For example:
- **Knight:** Highest Base Damage.
- **Wizard:** Highest Max Health (HP).
- **Assassin:** Highest Max Stamina/Mana.
- **Enemies:** `Enemy` is an abstract class implementing `Entity`. Subclasses: `Skeleton`, `Goblin`, `Vampire`, and **`Dragon`**.
- **The Boss:** Even though `Dragon` is the final boss, it **must** be a subclass of `Enemy` to inherit common combat properties, while possessing extremely high stats and unique mechanics.
- **Item (Bonus):** `Consumable`, `Armor`, `Weapon` are abstract classes implementing `Item`. example :
- KnightArmor extends Armor - you can add more subclasses of Armor for extra score
- Sword extends Weapon - you can add more subclasses of Weapon for extra score
- Flask extends Consumable - you can add more subclasses of Consumable for extra score
![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]
---
Your Turn:
1. Light Attack | 2. Heavy Attack (-8 Mana) | 3. Defend (-6 Mana) | 4. Heal (-12 Mana) | 5. Shield Bash (-15 Mana)
```
```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).
---
## Evaluation Criteria ⚖
| **Criteria** | **Points** |
|-------------------------------------------------------------|------------|
| Proper use of OOP principles | **50** |
| Working combat mechanics, Leveling System & Enemy abilities | **20** |
| Clear and Comprehensive `README.md` | **20** |
| Code readability, documentation, and comments | **10** |
| Meaningful, interactive, & colored console outputs | **10** |
| Inventory (item) and Merchant System (bonus) | **20** |
| Other Extra features (bonus tasks) | **20** |
| **Total Score** | **150** |
## Tips 🚀
- **Follow OOP principles**: Avoid redundant code by using inheritance properly. Think carefully about what belongs in an abstract class vs. a specific subclass. Make sure you use overriding and overloading correctly.
- **Test your code**: Run different scenarios (fighting, running out of mana, leveling up, dying) to ensure everything works as expected.
- **Ask for help**: If you're stuck, reach out to your classmates or mentors.
## Submission ⌛
- **Deadline**: Submit your assignment before **21 Ordibehesht (May 11th, 2026)**.
- **Submission Format**: Push your code to your forked repository, create a PR, and ensure your comprehensive `README.md` is included in the root directory.
![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.
+64
View File
@@ -0,0 +1,64 @@
# 🐉 The Curse of the Dragon - Complete RPG Game
## Table of Contents
1. [Project Overview](#project-overview)
2. [Game Story](#game-story)
3. [Technical Architecture](#technical-architecture)
---
## Project Overview
**The Curse of the Dragon** is a fully-featured Java RPG (Role-Playing Game) built with Object-Oriented Programming principles. The game features turn-based combat, item collection, leveling systems, and a complete progression arc from humble beginnings to an epic dragon boss battle.
### Key Features
-**3 Playable Classes**: Knight, Assassin, Wizard
-**4 Enemy Types**: Goblin, Skeleton, Vampire, Dragon
-**Key Collection System**: 3 unique keys with 20% drop rates
-**Leveling System**: XP-based progression with stat increases
-**Item System**: Healing potions and mana potions
-**ANSI Color Output**: Beautiful console visuals
-**Complete Game Loop**: Exploration, combat, progression
-**Boss Battle**: Epic dragon fight with unique mechanics
---
## Game Story
> *"A dark curse has befallen the land. The Ancient Dragon Ember has risen from its slumber, spreading fear and darkness across the realm. The only way to break the curse is to collect the three ancient keys - held by the Goblin King, the Skeleton Lord, and the Vampire Count - and confront the Dragon in its castle. Will you be the hero who saves the kingdom?"*
---
## Technical Architecture
### Design Patterns Used
#### 1. Strategy Pattern
Each enemy type implements its own attack strategy:
```java
// Goblin - Basic attack
@Override
public void attack(Entity target) {
System.out.println(name + " swings a crude club!");
target.receiveAttack(damage, false);
}
// Skeleton - Can miss
@Override
public void attack(Entity target) {
if (Math.random() < 0.1) {
System.out.println(name + " attacks but misses!");
return;
}
super.attack(target);
}
// Vampire - Lifesteal
@Override
public void attack(Entity target) {
target.receiveAttack(totalDamage, false);
int healAmount = (int)(totalDamage * 0.3);
heal(healAmount);
}