1 Commits
Author SHA1 Message Date
Saba_frm fa56a58cea develop 2026-05-19 11:48:51 +03:30
51 changed files with 1735 additions and 170 deletions
+1 -3
View File
@@ -8,7 +8,5 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="25" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
<output url="file://$PROJECT_DIR$/out" />
</component>
</project> </project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/HW-04-JAVA-KNIGHT.iml" filepath="$PROJECT_DIR$/.idea/HW-04-JAVA-KNIGHT.iml" />
</modules>
</component>
</project>
@@ -0,0 +1,25 @@
package org.project;
import org.project.entity.players.Player;
import org.project.location.Location;
import java.io.Serializable;
public class GameState implements Serializable {
private static final long serialVersionUID = 1L;
private Player player;
private Location currentLocation;
public GameState(Player player, Location currentLocation) {
this.player = player;
this.currentLocation = currentLocation;
}
public Player getPlayer() {
return player;
}
public Location getCurrentLocation() {
return currentLocation;
}
}
+292 -6
View File
@@ -1,15 +1,301 @@
package org.project; package org.project;
import org.project.combat.CombatSystem;
import org.project.entity.Entity;
import org.project.entity.enemies.Dragon;
import org.project.entity.enemies.Goblin;
import org.project.entity.enemies.Skeleton;
import org.project.entity.enemies.Vampire;
import org.project.entity.players.*;
import org.project.location.Location; import org.project.location.Location;
import java.util.ArrayList; import java.util.Random;
import java.util.List; import java.util.Scanner;
public class Main { public class Main {
public static void main(String[] args) {
// TODO: ADD LOCATIONS TO YOUR GAME
List<Location> locations = new ArrayList<>();
// TODO: IMPLEMENT GAMEPLAY private static final Scanner scanner = new Scanner(System.in);
private static Location currentLocation;
public static final String RESET = "\u001B[0m";
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 void main(String[] args) {
initializeMap();
while (true) {
System.out.println(CYAN + "\n===============================" + RESET);
System.out.println(YELLOW + " ⚔ JAVA KNIGHT ⚔" + RESET);
System.out.println(CYAN + "===============================" + RESET);
System.out.println("1. Start Game");
System.out.println("2. How To Play");
System.out.println("3. Exit");
System.out.print("> ");
int option = scanner.nextInt();
switch (option) {
case 1 -> startGame();
case 2 -> showHelpMenu();
case 3 -> {
System.out.println(GREEN + "Farewell hero!" + RESET);
System.exit(0);
}
default -> System.out.println(RED + "Invalid option!" + RESET);
}
}
}
private static void startGame()
{
showIntro();
System.out.print("Enter your name: ");
String name = scanner.next();
System.out.println("\nChoose your class:");
System.out.println(BLUE + "1. Knight 🛡" + RESET);
System.out.println(PURPLE + "2. Wizard 🔮" + RESET);
System.out.println(YELLOW + "3. Assassin 🗡" + RESET);
int choice = scanner.nextInt();
Player player;
switch (choice) {
case 1 -> player = new Knight(name);
case 2 -> player = new Wizard(name);
case 3 -> player = new Assassin(name);
default -> player = new Knight(name);
}
player.resetKeys();
System.out.println(GREEN + "\nWelcome " + name + " to Javanest!" + RESET);
currentLocation.enter();
gameLoop(player);
}
private static void gameLoop(Player player)
{
Random random = new Random();
while (player.isAlive())
{
showPlayerStatus(player);
System.out.println(CYAN + "\n===== LOCATION: " + currentLocation.getName() + " =====" + RESET);
System.out.println("1. Fight Enemy");
System.out.println("2. Move Location");
if (player.hasAllKeys() && currentLocation.getName().equals("Vampire Crypt"))
{
System.out.println(RED + "3. Enter Dragon Castle 🐉" + RESET);
}
System.out.print("> ");
int option = scanner.nextInt();
if (option == 1) {
Entity enemy = currentLocation.spawnEnemy();
System.out.println(RED + "\nA wild " + enemy.getName() + " appears!" + RESET);
CombatSystem combat = new CombatSystem();
combat.startBattle(player, enemy);
if (!player.isAlive()) {
System.out.println(RED + "\nYou have fallen..." + RESET);
break;
}
tryToDropKey(player, enemy, random);
recoverPlayer(player);
} else if (option == 2) {
moveLocation();
} else if (option == 3 &&
player.hasAllKeys() &&
currentLocation.getName().equals("Vampire Crypt")) {
System.out.println(RED + "\nYou enter the Dragon Castle..." + RESET);
CombatSystem combat = new CombatSystem();
combat.startBattle(player, new Dragon());
if (player.isAlive()) {
System.out.println(GREEN + "\n🏆 YOU SAVED JAVANEST!" + RESET);
} else {
System.out.println(RED + "\nThe Dragon has defeated you..." + RESET);
}
showProjectFeatures();
break;
} else {
System.out.println(RED + "Invalid option!" + RESET);
}
}
}
private static void showPlayerStatus(Player player) {
System.out.println(YELLOW + "\n===== PLAYER STATUS =====" + RESET);
printBar("HP ", player.getHP(), player.getMaxHP(), RED);
printBar("MP ", player.getMP(), player.getMaxMP(), BLUE);
System.out.println("XP: " + player.getXP());
}
private static void printBar(String label, int value, int max, String color)
{
int totalBars = 20;
int filled = (int)((double)value / max * totalBars);
StringBuilder bar = new StringBuilder();
for (int i = 0; i < filled; i++) bar.append("");
for (int i = filled; i < totalBars; i++) bar.append("");
System.out.println(label + " " + color + bar + RESET + " " + value + "/" + max);
}
private static void moveLocation()
{
System.out.println(YELLOW + "\nWhere do you want to go?" + RESET);
int index = 1;
for (Location loc : currentLocation.getConnectedLocations())
{
System.out.println(index + ". " + loc.getName());
index++;
}
System.out.print("> ");
int choice = scanner.nextInt();
if (choice < 1 || choice > currentLocation.getConnectedLocations().size())
{
System.out.println(RED + "Invalid location!" + RESET);
return;
}
currentLocation = currentLocation.getConnectedLocations().get(choice - 1);
currentLocation.enter();
}
private static void tryToDropKey(Player player, Entity enemy, Random random)
{
int chance = random.nextInt(100) + 1;
if (chance <= 20)
{
if (enemy instanceof Goblin && !player.hasGoblinKey())
{
player.obtainGoblinKey();
System.out.println(YELLOW + "You obtained the Goblin Key!" + RESET);
} else if (enemy instanceof Skeleton && !player.hasSkeletonKey()) {
player.obtainSkeletonKey();
System.out.println(YELLOW + "You obtained the Skeleton Key!" + RESET);
} else if (enemy instanceof Vampire && !player.hasVampireKey()) {
player.obtainVampireKey();
System.out.println(YELLOW + "You obtained the Vampire Key!" + RESET);
}
}
}
private static void recoverPlayer(Player player)
{
player.setHealth(player.getMaxHP());
player.setMana(player.getMaxMP());
System.out.println(GREEN + "You recovered your HP and Mana." + RESET);
}
private static void showIntro()
{
System.out.println(PURPLE + "\n====================================" + RESET);
System.out.println(YELLOW + " LEGEND OF JAVANEST" + RESET);
System.out.println(PURPLE + "====================================" + RESET);
System.out.println("Defeat enemies, collect keys,");
System.out.println("and face the mighty Dragon!");
}
private static void showHelpMenu()
{
System.out.println(YELLOW + "\nHOW TO PLAY:" + RESET);
System.out.println("- Fight enemies to gain XP.");
System.out.println("- Collect 3 keys.");
System.out.println("- Unlock Dragon Castle.");
System.out.println("- Defeat the Dragon to win.");
}
private static void showProjectFeatures()
{
System.out.println(CYAN + "\n====================================" + RESET);
System.out.println(YELLOW + " PROJECT FEATURES" + RESET);
System.out.println(CYAN + "====================================" + RESET);
System.out.println(GREEN + "✔ Multiple Player Classes" + RESET);
System.out.println(GREEN + "✔ Location System" + RESET);
System.out.println(GREEN + "✔ Key Collection System" + RESET);
System.out.println(GREEN + "✔ Combat System" + RESET);
System.out.println(GREEN + "✔ Final Boss Battle" + RESET);
}
private static void initializeMap()
{
Location forest = new Location(
"Forest",
"A dark forest full of goblins.",
1
);
Location graveyard = new Location(
"Graveyard",
"An abandoned graveyard with skeletons.",
2
);
Location vampireCrypt = new Location(
"Vampire Crypt",
"A cursed crypt where vampires sleep.",
3
);
Location dragonCastle = new Location(
"Dragon Castle",
"The castle of the ancient dragon.",
4
);
forest.connectLocation(graveyard);
graveyard.connectLocation(forest);
graveyard.connectLocation(vampireCrypt);
vampireCrypt.connectLocation(graveyard);
vampireCrypt.connectLocation(dragonCastle);
currentLocation = forest;
} }
} }
@@ -0,0 +1,166 @@
package org.project.combat;
import org.project.entity.players.Player;
import org.project.entity.Entity;
import org.project.entity.enemies.Goblin;
import org.project.entity.enemies.Skeleton;
import org.project.entity.enemies.Vampire;
import java.util.Scanner;
public class CombatSystem
{
private static final String RESET = "\u001B[0m";
private static final String RED = "\u001B[31m";
private static final String GREEN = "\u001B[32m";
private static final String YELLOW = "\u001B[33m";
private static final String BLUE = "\u001B[34m";
private Scanner scanner = new Scanner(System.in);
public void startBattle(Player player, Entity enemy)
{
System.out.println(RED + "\n⚔️ A battle has started!" + RESET);
System.out.println(YELLOW + "Enemy: " + enemy.getName() + RESET);
while (player.isAlive() && enemy.isAlive())
{
System.out.println("\n" + BLUE + "----- YOUR TURN -----" + RESET);
System.out.println("1. Light Attack");
System.out.println("2. Heavy Attack");
System.out.println("3. Defend");
System.out.println("4. Heal");
System.out.println("5. Special Ability");
System.out.println("6. Exit Game");
while (!scanner.hasNextInt())
{
scanner.next();
System.out.println("❌ Please enter a number between 1-6");
}
int choice = scanner.nextInt();
switch (choice)
{
case 1:
player.lightAttack(enemy);
break;
case 2:
player.heavyAttack(enemy);
break;
case 3:
player.defend();
break;
case 4:
player.specialAbility(enemy);
break;
case 5:
if (player.getMP() >= 6)
{
player.heal(10);
player.fillMana(-6);
System.out.println("💚 You restored 10 HP at the cost of 6 MP!");
} else {
System.out.println("❌ Not enough MP to heal!");
}
break;
case 6:
if (player.getXP() >= 10)
{
player.gainXP(-10);
player.fillMana(20);
System.out.println("🔮 +20 MP restored (Cost: 10 XP)");
} else {
System.out.println("❌ Not enough XP to restore mana!");
}
break;
case 7:
if (player.getXP() >= 50)
{
player.gainXP(-50);
player.heal(30);
System.out.println("❤️ +30 HP restored (Cost: 50 XP)");
} else {
System.out.println("❌ Not enough XP to restore HP!");
}
break;
case 8:
System.out.println("👋 Exiting game...");
System.exit(0);
default:
System.out.println("❌ Invalid choice!");
}
if (!enemy.isAlive())
{
System.out.println(GREEN + "\n✅ Enemy defeated!" + RESET);
player.gainXP(50);
dropKey(player, enemy);
recoverPlayer(player);
break;
}
System.out.println(RED + "\n👹 ENEMY TURN!" + RESET);
enemy.attack(player);
if (!player.isAlive())
{
System.out.println(RED + "\n💀 You were defeated..." + RESET);
}
}
}
private void dropKey(Player player, Entity enemy)
{
double chance = Math.random();
if (chance > 0.20) return;
if (enemy instanceof Goblin && !player.hasGoblinKey())
{
System.out.println(YELLOW + "🗝️ Goblin Key obtained!" + RESET);
player.obtainGoblinKey();
}
else if (enemy instanceof Skeleton && !player.hasSkeletonKey())
{
System.out.println(YELLOW + "🗝️ Skeleton Key obtained!" + RESET);
player.obtainSkeletonKey();
}
else if (enemy instanceof Vampire && !player.hasVampireKey())
{
System.out.println(YELLOW + "🗝️ Vampire Key obtained!" + RESET);
player.obtainVampireKey();
}
}
private void recoverPlayer(Player player)
{
player.setHealth(player.getMaxHP());
player.setMana(player.getMaxMP());
System.out.println("\u001B[32m✨ Your HP and Mana have been fully restored!\u001B[0m");
}
}
@@ -1,6 +1,7 @@
package org.project.entity; package org.project.entity;
public interface Entity { public interface Entity
{
void attack(Entity target); void attack(Entity target);
void defend(); void defend();
@@ -11,9 +12,22 @@ public interface Entity {
void takeDamage(int damage); void takeDamage(int damage);
int getHP();
int getMP();
int getMaxHP(); int getMaxHP();
int getMaxMP(); int getMaxMP();
int getLevel();
String getName();
void setStunned(boolean value);
boolean isStunned();
void setInvisible(boolean value);
boolean isInvisible();
boolean isAlive();
void gainXP(int xp);
/* /*
TODO: ADD OTHER REQUIRED AND BONUS METHODS TODO: ADD OTHER REQUIRED AND BONUS METHODS
@@ -0,0 +1,38 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.BasicWeapon;
import java.util.Random;
public class Dragon extends Enemy
{
private Random random = new Random();
public Dragon()
{
super("Dragon", 200, 50, new BasicWeapon("Flame Breath", 25));
}
@Override
public void attack(Entity target)
{
int damage;
if (random.nextBoolean())
{
damage = weapon.getDamage() + 20;
System.out.println(RED + "🔥 Dragon uses FIRE BREATH!" + RESET);
} else {
damage = weapon.getDamage();
System.out.println(YELLOW + "🐉 Dragon claws viciously!" + RESET);
}
target.takeDamage(damage);
System.out.println(RED + "💥 Dragon deals "
+ damage + " damage!" + RESET);
}
}
@@ -1,34 +1,140 @@
package org.project.entity.enemies; package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.Weapon; import org.project.item.weapons.Weapon;
// TODO: UPDATE IMPLEMENTATION public abstract class Enemy implements Entity
public abstract class Enemy { {
Weapon weapon;
private int hp;
private int mp;
public Enemy(int hp, int mp, Weapon weapon) { protected String name;
protected int hp;
protected int mp;
protected int maxHp;
protected int maxMp;
protected int level;
protected boolean stunned;
protected boolean invisible;
protected Weapon weapon;
protected static final String RESET = "\u001B[0m";
protected static final String RED = "\u001B[31m";
protected static final String YELLOW = "\u001B[33m";
protected static final String PURPLE = "\u001B[35m";
public Enemy(String name, int hp, int mp, Weapon weapon)
{
this.name = name;
this.hp = hp; this.hp = hp;
this.mp = mp; this.mp = mp;
this.maxHp = hp;
this.maxMp = mp;
this.level = 1;
this.stunned = false;
this.invisible = false;
this.weapon = weapon; this.weapon = weapon;
} }
@Override @Override
public void takeDamage(int damage) { public void defend()
hp -= damage; {
System.out.println(name + " defends.");
} }
public int getHp() { @Override
public void heal(int health)
{
if (health <= 0) return;
hp += health;
if (hp > maxHp) hp = maxHp;
}
@Override
public void fillMana(int mana)
{
if (mana <= 0) return;
mp += mana;
if (mp > maxMp) mp = maxMp;
}
@Override
public void takeDamage(int damage)
{
if (damage <= 0) return;
hp -= damage;
if (hp < 0) hp = 0;
}
@Override
public int getHP()
{
return hp; return hp;
} }
public int getMp() { @Override
public int getMP()
{
return mp; return mp;
} }
public Weapon getWeapon() { @Override
return weapon; public int getMaxHP()
{
return maxHp;
} }
@Override
public int getMaxMP()
{
return maxMp;
}
@Override
public int getLevel()
{
return level;
}
@Override
public String getName()
{
return name;
}
@Override
public void setStunned(boolean value)
{
this.stunned = value;
}
@Override
public boolean isStunned()
{
return stunned;
}
@Override
public void setInvisible(boolean value)
{
this.invisible = value;
}
@Override
public boolean isInvisible()
{
return invisible;
}
@Override
public boolean isAlive()
{
return hp > 0;
}
@Override
public void gainXP(int xp)
{
}
@Override
public abstract void attack(Entity target);
} }
@@ -0,0 +1,37 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.BasicWeapon;
import java.util.Random;
public class Goblin extends Enemy
{
private Random random = new Random();
private static final String RESET = "\u001B[0m";
private static final String RED = "\u001B[31m";
private static final String PURPLE = "\u001B[35m";
public Goblin() {
super("Goblin", 60, 20, new BasicWeapon("Rusty Dagger", 12));
}
@Override
public void attack(Entity target)
{
int damage = weapon.getDamage();
if (random.nextInt(100) < 25)
{
damage *= 2;
System.out.println(PURPLE + "💀 Goblin CRITICAL strike!" + RESET);
}
target.takeDamage(damage);
System.out.println(RED + "👹 Goblin attacks for " + damage + " damage!" + RESET);
}
}
@@ -1,6 +1,46 @@
package org.project.entity.enemies; package org.project.entity.enemies;
// TODO: UPDATE IMPLEMENTATION import org.project.entity.Entity;
public class Skeleton { import org.project.item.weapons.BasicWeapon;
// TODO: DESIGN ENEMY AND IMPLEMENT THE CONSTRUCTOR
} public class Skeleton extends Enemy
{
private boolean revived = false;
private static final String RESET = "\u001B[0m";
private static final String RED = "\u001B[31m";
private static final String YELLOW = "\u001B[33m";
public Skeleton()
{
super("Skeleton", 70, 10, new BasicWeapon("Bone Sword", 15));
}
@Override
public void attack(Entity target)
{
int damage = weapon.getDamage();
target.takeDamage(damage);
System.out.println(RED + "☠ Skeleton slashes for " + damage + " damage!" + RESET);
}
@Override
public void takeDamage(int damage)
{
super.takeDamage(damage);
if (!isAlive() && !revived)
{
revived = true;
this.hp = 40;
System.out.println(YELLOW + "⚠ Skeleton reassembles itself!" + RESET);
}
}
}
@@ -0,0 +1,30 @@
package org.project.entity.enemies;
import org.project.entity.Entity;
import org.project.item.weapons.BasicWeapon;
public class Vampire extends Enemy
{
public Vampire()
{
super("Vampire", 90, 30, new BasicWeapon("Dark Claws", 18));
}
@Override
public void attack(Entity target)
{
int damage = weapon.getDamage();
target.takeDamage(damage);
int healAmount = damage / 2;
this.hp += healAmount;
System.out.println(RED + "🧛 Vampire drains "
+ damage + " HP!" + RESET);
System.out.println(YELLOW + "🩸 Vampire heals "
+ healAmount + " HP!" + RESET);
}
}
@@ -0,0 +1,99 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.item.weapons.BasicWeapon;
import java.util.Random;
public class Assassin extends Player
{
private static final int HEAVY_COST = 15;
private static final int SPECIAL_COST = 25;
private static final String RESET = "\u001B[0m";
private static final String RED = "\u001B[31m";
private static final String GREEN = "\u001B[32m";
private static final String YELLOW = "\u001B[33m";
private static final String BLUE = "\u001B[34m";
private static final String PURPLE = "\u001B[35m";
private Random random = new Random();
public Assassin(String name) {
super(name, 90, 80, new BasicWeapon("Dagger", 18));
}
@Override
public void lightAttack(Entity target)
{
int dmg = weapon.getDamage() + 12;
// 25% critical chance
if (random.nextInt(100) < 25)
{
dmg *= 2;
System.out.println(PURPLE + "💀 CRITICAL HIT!" + RESET);
}
target.takeDamage(dmg);
System.out.println(YELLOW + "🗡️ " + name +
" strikes for " + dmg + " damage!" + RESET);
}
@Override
public void heavyAttack(Entity target)
{
if (mp < HEAVY_COST) {
System.out.println(RED + "❌ Not enough MP!" + RESET);
return;
}
mp -= HEAVY_COST;
int dmg = weapon.getDamage() + 30;
target.takeDamage(dmg);
System.out.println(PURPLE + "⚡ Shadow Slash deals " +
dmg + " damage!" + RESET);
}
@Override
public void defendAction()
{
defend();
System.out.println(BLUE + "🛡️ Quick dodge stance!" + RESET);
}
@Override
public void healAction()
{
heal(20);
System.out.println(GREEN + "💖 " + name +
" uses bandage restoring 20 HP!" + RESET);
}
@Override
public void specialAbility(Entity target)
{
if (mp < SPECIAL_COST)
{
System.out.println(RED + "❌ Not enough MP!" + RESET);
return;
}
mp -= SPECIAL_COST;
setInvisible(true);
int dmg = weapon.getDamage() + 50;
target.takeDamage(dmg);
System.out.println(BLUE + "🌑 SHADOW STRIKE deals " +
dmg + " damage!" + RESET);
System.out.println(YELLOW + "👤 " + name +
" becomes INVISIBLE!" + RESET);
}
}
@@ -0,0 +1,22 @@
package org.project.entity.players;
import org.project.entity.Entity;
public interface ICombatActions
{
// 1. Light Attack
void lightAttack(Entity target);
// 2. Heavy Attack
void heavyAttack(Entity target);
// 3. Defend
void defendAction();
// 4. Heal
void healAction();
// 5. Special Ability
void specialAbility(Entity target);
}
@@ -1,6 +1,101 @@
package org.project.entity.players; package org.project.entity.players;
// TODO: UPDATE IMPLEMENTATION import org.project.entity.Entity;
public class Knight { import org.project.item.armors.KnightArmor;
// TODO: DESIGN KNIGHT'S WEAPON AND ARMOR AND IMPLEMENT THE CONSTRUCTOR import org.project.item.weapons.Sword;
}
public class Knight extends Player {
private static final int LIGHT_BONUS = 15;
private static final int HEAVY_COST = 10;
private static final int SPECIAL_COST = 25;
private static final String RESET = "\u001B[0m";
private static final String RED = "\u001B[31m";
private static final String GREEN = "\u001B[32m";
private static final String YELLOW = "\u001B[33m";
private static final String BLUE = "\u001B[34m";
private static final String PURPLE = "\u001B[35m";
public Knight(String name)
{
super(name, 150, 40, new Sword());
new KnightArmor();
}
@Override
public void lightAttack(Entity target)
{
int dmg = weapon.getDamage() + LIGHT_BONUS;
target.takeDamage(dmg);
((Sword) weapon).addCharge(1);
System.out.println(
YELLOW + "⚔️ " + name +
" slashes the enemy for " + dmg + " damage!" +
RESET
);
}
@Override
public void heavyAttack(Entity target)
{
if (mp < HEAVY_COST)
{
System.out.println(RED + "❌ Not enough MP for heavy attack!" + RESET);
return;
}
mp -= HEAVY_COST;
int dmg = weapon.getDamage() + 30;
target.takeDamage(dmg);
((Sword) weapon).addCharge(2);
System.out.println(PURPLE + "💥 " + name +
" performs a HEAVY STRIKE for " + dmg + " damage!" + RESET);
}
@Override
public void defendAction()
{
defend();
System.out.println(BLUE + "🛡️ " + name +
" raises his shield!" + RESET);
}
@Override
public void healAction()
{
heal(30);
System.out.println(GREEN + "💖 " + name +
" heals for 30 HP!" + RESET);
}
@Override
public void specialAbility(Entity target)
{
if (mp < SPECIAL_COST)
{
System.out.println(RED + "❌ Not enough MP!" + RESET);
return;
}
mp -= SPECIAL_COST;
int dmg = weapon.getDamage() + 45;
target.takeDamage(dmg);
target.setStunned(true);
((Sword) weapon).addCharge(3);
System.out.println(
BLUE + "🌋 " + name +
" uses EARTH SHATTERING STRIKE dealing " + dmg + " damage!" +
RESET
);
System.out.println(YELLOW + "⚠️ The enemy is STUNNED!" + RESET);
}
}
@@ -1,89 +1,159 @@
package org.project.entity.players; package org.project.entity.players;
import org.project.entity.Entity; import org.project.entity.Entity;
import org.project.item.armors.Armor;
import org.project.item.weapons.Weapon; import org.project.item.weapons.Weapon;
// TODO: UPDATE IMPLEMENTATION import java.io.Serializable;
public abstract class Player {
public abstract class Player implements Entity, ICombatActions, Serializable
{
private static final long serialVersionUID = 1L;
protected String name; protected String name;
Weapon weapon; protected int hp, maxHP;
Armor armor; protected int mp, maxMP;
private int hp; protected int level = 1;
private int maxHP; protected int xp = 0;
private int mp;
private int maxMP;
public Player(String name, int hp, int mp, Weapon weapon, Armor armor) { protected boolean stunned = false;
protected boolean invisible = false;
protected boolean defending = false;
private boolean goblinKey = false;
private boolean skeletonKey = false;
private boolean vampireKey = false;
protected Weapon weapon;
private static final String RESET = "\u001B[0m";
private static final String RED = "\u001B[31m";
private static final String GREEN = "\u001B[32m";
private static final String YELLOW = "\u001B[33m";
private static final String BLUE = "\u001B[34m";
private static final String PURPLE = "\u001B[35m";
public Player(String name, int maxHP, int maxMP, Weapon weapon)
{
this.name = name; this.name = name;
this.hp = hp; this.maxHP = maxHP;
this.mp = mp; this.hp = maxHP;
this.maxMP = maxMP;
this.mp = maxMP;
this.weapon = weapon; this.weapon = weapon;
this.armor = armor;
} }
@Override @Override
public void attack(Entity target) { public void attack(Entity target)
target.takeDamage(weapon.getDamage()); {
lightAttack(target);
} }
@Override @Override
public void defend() { public void defend()
// TODO {
} defending = true;
System.out.println(BLUE + "🛡️ " + name + " is defending!" + RESET);
@Override
public void takeDamage(int damage) {
hp -= damage - armor.getDefense();
} }
@Override @Override
public void heal(int health) { public void heal(int amount)
hp += health; {
if (hp > maxHP) { hp = Math.min(maxHP, hp + amount);
}
@Override
public void fillMana(int amount)
{
mp = Math.min(maxMP, mp + amount);
}
@Override
public void takeDamage(int damage)
{
if (defending)
{
damage /= 2;
defending = false;
}
hp = Math.max(0, hp - damage);
System.out.println(RED + "💥 " + name + " took " + damage + " damage!" + RESET);
}
@Override
public int getHP() { return hp; }
@Override
public int getMP() { return mp; }
@Override
public int getMaxHP() { return maxHP; }
@Override
public int getMaxMP() { return maxMP; }
@Override
public int getLevel() { return level; }
@Override
public String getName() { return name; }
@Override
public void setStunned(boolean v) { stunned = v; }
@Override
public boolean isStunned() { return stunned; }
@Override
public void setInvisible(boolean v) { invisible = v; }
@Override
public boolean isInvisible() { return invisible; }
@Override
public boolean isAlive() { return hp > 0; }
@Override
public void gainXP(int xp)
{
this.xp += xp;
if (this.xp >= level * 100)
{
level++;
this.xp = 0;
maxHP += 10;
maxMP += 5;
hp = maxHP; hp = maxHP;
}
}
@Override
public void fillMana(int mana) {
mp += mana;
if (mp > maxMP) {
mp = maxMP; mp = maxMP;
System.out.println(GREEN + "" + name + " leveled up to level " + level + "!" + RESET);
} }
} }
public boolean hasGoblinKey() { return goblinKey; }
public boolean hasSkeletonKey() { return skeletonKey; }
public boolean hasVampireKey() { return vampireKey; }
public String getName() { public void obtainGoblinKey() { goblinKey = true; }
return name; public void obtainSkeletonKey() { skeletonKey = true; }
public void obtainVampireKey() { vampireKey = true; }
public boolean hasAllKeys()
{
return goblinKey && skeletonKey && vampireKey;
} }
public int getHp() { public void resetKeys()
return hp; {
goblinKey = false;
skeletonKey = false;
vampireKey = false;
} }
@Override public void setHealth(int hp)
public int getMaxHP() { {
return maxHP; this.hp = Math.min(hp, maxHP);
} }
public int getMp() { public void setMana(int mp)
return mp; {
this.mp = Math.min(mp, maxMP);
} }
@Override public int getXP()
public int getMaxMP() { {
return maxMP; return xp;
} }
}
public Weapon getWeapon() {
return weapon;
}
public Armor getArmor() {
return armor;
}
}
@@ -0,0 +1,96 @@
package org.project.entity.players;
import org.project.entity.Entity;
import org.project.item.weapons.BasicWeapon;
public class Wizard extends Player
{
private static final int LIGHT_COST = 5;
private static final int HEAVY_COST = 15;
private static final int SPECIAL_COST = 30;
private static final String RESET = "\u001B[0m";
private static final String RED = "\u001B[31m";
private static final String GREEN = "\u001B[32m";
private static final String YELLOW = "\u001B[33m";
private static final String BLUE = "\u001B[34m";
private static final String PURPLE = "\u001B[35m";
public Wizard(String name) {
super(name, 100, 120, new BasicWeapon("Magic Staff", 20));
}
@Override
public void lightAttack(Entity target)
{
if (mp < LIGHT_COST)
{
System.out.println(RED + "❌ Not enough MP!" + RESET);
return;
}
mp -= LIGHT_COST;
int dmg = weapon.getDamage() + 10;
target.takeDamage(dmg);
System.out.println(YELLOW + "" + name + " casts Magic Bolt for " + dmg + " damage!" + RESET);
}
@Override
public void heavyAttack(Entity target)
{
if (mp < HEAVY_COST)
{
System.out.println(RED + "❌ Not enough MP!" + RESET);
return;
}
mp -= HEAVY_COST;
int dmg = weapon.getDamage() + 35;
target.takeDamage(dmg);
System.out.println(PURPLE + "🔥 " + name +
" casts FIREBALL for " + dmg + " damage!" + RESET);
}
@Override
public void defendAction()
{
defend();
System.out.println(BLUE + "🔮 Magical barrier activated!" + RESET);
}
@Override
public void healAction()
{
if (mp < 20)
{
System.out.println(RED + "❌ Not enough MP to heal!" + RESET);
return;
}
mp -= 20;
heal(40);
System.out.println(GREEN + "💖 " + name +
" casts HEAL restoring 40 HP!" + RESET);
}
@Override
public void specialAbility(Entity target)
{
if (mp < SPECIAL_COST)
{
System.out.println(RED + "❌ Not enough MP!" + RESET);
return;
}
mp -= SPECIAL_COST;
int dmg = weapon.getDamage() + 60;
target.takeDamage(dmg);
System.out.println(BLUE + "🌪️ ARCANE BURST hits for " +
dmg + " damage!" + RESET);
}
}
@@ -2,10 +2,14 @@ package org.project.item;
import org.project.entity.Entity; import org.project.entity.Entity;
public interface Item { public interface Item
{
void use(Entity target); void use(Entity target);
/* String getName();
TODO: ADD OTHER REQUIRED AND BONUS METHODS
*/ String getDescription();
int getValue();
} }
@@ -7,36 +7,67 @@ public abstract class Armor {
private int durability; private int durability;
private int maxDurability; private int maxDurability;
private boolean isBroke; private boolean isBroken;
public Armor(int defense, int durability) { public Armor(int defense, int durability) {
this.defense = defense; this.defense = defense;
this.maxDefense = defense;
this.durability = durability; this.durability = durability;
this.maxDurability = durability;
this.isBroken = false;
} }
public void checkBreak() { public void takeHit(int damage)
{
durability -= damage;
if (durability < 0)
{
durability = 0;
}
checkBroken();
}
public void checkBroken() {
if (durability <= 0) { if (durability <= 0) {
isBroke = true; isBroken = true;
defense = 0; defense = 0;
} }
} }
// TODO: (BONUS) UPDATE THE REPAIR METHOD // TODO: (BONUS) UPDATE THE REPAIR METHOD
public void repair() { public void repair()
isBroke = false; {
isBroken = false;
defense = maxDefense; defense = maxDefense;
durability = maxDurability; durability = maxDurability;
} }
public int getDefense() { public int getDefense()
{
return defense; return defense;
} }
public int getDurability() { public int getDurability()
{
return durability; return durability;
} }
public boolean isBroke() { public int getMaxDefense()
return isBroke; {
return maxDefense;
} }
public int getMaxDurability()
{
return maxDurability;
}
public boolean isBroken()
{
return isBroken;
}
public abstract String getArmorName();
} }
@@ -1,6 +1,16 @@
package org.project.item.armors; package org.project.item.armors;
// TODO: UPDATE IMPLEMENTATION public class KnightArmor extends Armor
public class KnightArmor { {
// TODO: DESIGN ARMOR'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
} public KnightArmor()
{
super(30, 100);
}
@Override
public String getArmorName()
{
return "Knight's Plate Armor";
}
}
@@ -1,8 +1,50 @@
package org.project.item.consumables; package org.project.item.consumables;
// TODO: UPDATE IMPLEMENTATION import org.project.entity.Entity;
public abstract class Consumable {
/* public abstract class Consumable
TODO: ADD OTHER REQUIRED AND BONUS METHODS {
*/
protected String name;
protected String description;
protected int restoreAmount;
protected boolean percentageBased;
public Consumable(String name, String description, int restoreAmount, boolean percentageBased)
{
this.name = name;
this.description = description;
this.restoreAmount = restoreAmount;
this.percentageBased = percentageBased;
}
public abstract void use(Entity target);
public String getName()
{
return name;
}
public int getRestoreAmount()
{
return restoreAmount;
}
public String getDescription()
{
return description;
}
public boolean isPercentageBased()
{
return percentageBased;
}
@Override
public String toString()
{
return name + " (" + description + ")";
}
} }
@@ -2,15 +2,44 @@ package org.project.item.consumables;
import org.project.entity.Entity; import org.project.entity.Entity;
// TODO: UPDATE IMPLEMENTATION public class Flask extends Consumable
public class Flask { {
/*
THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN. private static final int HEAL_AMOUNT = 30;
*/
public Flask()
{
super(
"Health Flask",
"A small flask that restores health.",
HEAL_AMOUNT,
false
);
}
// TODO: UPDATE USE METHOD
@Override @Override
public void use(Entity target) { public void use(Entity target)
target.heal(target.getMaxHP() / 10); {
int healValue;
if (percentageBased)
{
healValue = target.getMaxHP() * restoreAmount / 100;
} else
{
healValue = restoreAmount;
}
target.heal(healValue);
System.out.println(
target.getName() +
" used " +
name +
" and restored " +
healValue +
" HP!"
);
} }
} }
@@ -0,0 +1,20 @@
package org.project.item.weapons;
import org.project.entity.Entity;
import java.util.ArrayList;
import java.io.Serializable;
public class BasicWeapon extends Weapon implements Serializable
{
private static final long serialVersionUID = 1L;
public BasicWeapon(String name, int damage)
{
super(name, damage, 0);
}
@Override
public void uniqueAbility(ArrayList<Entity> targets) {
}
}
@@ -1,26 +1,64 @@
package org.project.item.weapons; package org.project.item.weapons;
import org.project.entity.Entity; import org.project.entity.Entity;
import java.util.ArrayList; import java.util.ArrayList;
import java.io.Serializable;
// TODO: UPDATE IMPLEMENTATION public class Sword extends Weapon implements Serializable
public class Sword { {
/*
THIS IS AN EXAMPLE OF A WEAPON DESIGN.
*/
int abilityCharge; private static final long serialVersionUID = 1L;
public Sword() { private int abilityCharge;
// TODO: DESIGN SWORD'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR private static final int MAX_CHARGE = 10;
private static final int REQUIRED_CHARGE = 5;
public Sword()
{
super("Knight Sword", 20, 0);
this.abilityCharge = 0;
} }
// TODO: (BONUS) UPDATE THE UNIQUE ABILITY @Override
public void uniqueAbility(ArrayList<Entity> targets) { public void uniqueAbility(ArrayList<Entity> targets)
abilityCharge += 2; {
for (Entity target : targets) { if (abilityCharge < REQUIRED_CHARGE)
target.takeDamage(getDamage()); {
System.out.println("⚠️ Not enough charge for special ability! (" + abilityCharge + "/" + REQUIRED_CHARGE + ")");
return;
} }
if (isBroken)
{
System.out.println("⚠️ " + name + " is broken and can't use its special ability!");
return;
}
System.out.println("⚔️ " + name + " special ability: Cleave!");
for (Entity target : targets)
{
if (target != null && target.isAlive())
{
target.takeDamage((int) (getDamage() * 1.5));
}
}
abilityCharge = 0;
reduceDurability(5);
} }
}
public void addCharge(int amount)
{
this.abilityCharge = Math.min(this.abilityCharge + amount, MAX_CHARGE);
}
public int getAbilityCharge()
{
return abilityCharge;
}
public int getMaxCharge()
{
return MAX_CHARGE;
}
}
@@ -1,35 +1,87 @@
package org.project.item.weapons; package org.project.item.weapons;
import org.project.entity.Entity; import org.project.entity.Entity;
import java.util.ArrayList;
import java.io.Serializable;
// TODO: UPDATE IMPLEMENTATION public abstract class Weapon implements Serializable
public abstract class Weapon { {
private int damage;
private int manaCost;
/* private static final long serialVersionUID = 1L;
TODO: ADD OTHER REQUIRED AND BONUS ATTRIBUTES
*/
public Weapon(int damage, int manaCost) { protected String name;
protected int damage;
protected int manaCost;
protected int level;
protected int durability;
protected boolean isBroken;
public Weapon(String name, int damage, int manaCost)
{
this.name = name;
this.damage = damage; this.damage = damage;
this.manaCost = manaCost; this.manaCost = manaCost;
this.level = 1;
this.durability = 100;
this.isBroken = false;
} }
@Override public void use(Entity target)
public void use(Entity target) { {
if (isBroken)
{
System.out.println("⚠️ " + name + " is broken and can't be used!");
return;
}
target.takeDamage(damage); target.takeDamage(damage);
reduceDurability(1);
} }
public int getDamage() { public abstract void uniqueAbility(ArrayList<Entity> targets);
protected void reduceDurability(int amount)
{
durability = Math.max(0, durability - amount);
if (durability == 0)
{
isBroken = true;
}
}
public void upgrade()
{
level++;
damage += 5;
System.out.println("🛠️ " + name + " upgraded to level " + level + "!");
}
public String getName() {
return name;
}
public int getDamage()
{
return damage; return damage;
} }
public int getManaCost() { public int getManaCost()
{
return manaCost; return manaCost;
} }
/* public int getLevel()
TODO: ADD OTHER REQUIRED AND BONUS METHODS {
*/ return level;
} }
public int getDurability()
{
return durability;
}
public boolean isBroken()
{
return isBroken;
}
}
@@ -1,28 +1,64 @@
package org.project.location; package org.project.location;
import org.project.entity.enemies.Enemy; import org.project.entity.enemies.*;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
public class Location { public class Location implements Serializable
{
private static final long serialVersionUID = 1L;
private String name; private String name;
private String description;
private int difficulty; // 1 = easy, 2 = medium, 3 = hard
private ArrayList<Location> connectedLocations;
private ArrayList<Enemy> enemies; public Location(String name, String description, int difficulty)
{
public Location(ArrayList<Location> locations, ArrayList<Enemy> enemies) { this.name = name;
this.locations = locations; this.description = description;
this.enemies = enemies; this.difficulty = difficulty;
this.connectedLocations = new ArrayList<>();
} }
public String getName() { public void connectLocation(Location location)
{
connectedLocations.add(location);
}
public ArrayList<Location> getConnectedLocations()
{
return connectedLocations;
}
public String getName()
{
return name; return name;
} }
public ArrayList<Location> getLocations() { public int getDifficulty()
return locations; {
return difficulty;
} }
public ArrayList<Enemy> getEnemies() { public void enter()
return enemies; {
System.out.println("\n==============================");
System.out.println("📍 You arrived at: " + name);
System.out.println(description);
System.out.println("Difficulty: " + difficulty);
System.out.println("==============================\n");
}
public Enemy spawnEnemy()
{
return switch (name)
{
case "Forest" -> new Goblin();
case "Graveyard" -> new Skeleton();
case "Vampire Crypt" -> new Vampire();
case "Dragon Castle" -> new Dragon();
default -> new Goblin();
};
} }
} }
@@ -0,0 +1,27 @@
package org.project.utils;
import org.project.GameState;
import java.io.*;
public class SaveLoadManager {
public static void saveGame(GameState gameState, String filename) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
oos.writeObject(gameState);
System.out.println("\u001B[34m\u2714\u001B[0m Game saved successfully.");
} catch (IOException e) {
System.out.println("\u001B[31m\u2716\u001B[0m Error saving game: " + e.getMessage());
}
}
public static GameState loadGame(String filename) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) {
GameState loadedState = (GameState) ois.readObject();
System.out.println("\u001B[32m\u2714\u001B[0m Game loaded successfully.");
return loadedState;
} catch (IOException | ClassNotFoundException e) {
System.out.println("\u001B[31m\u2716\u001B[0m Error loading game: " + e.getMessage());
return null;
}
}
}
Binary file not shown.
+162
View File
@@ -0,0 +1,162 @@
# ⚔️ Java Knight: The Legend of Javanest
![Java Version](https://img.shields.io/badge/Java-17%2B-orange?style=for-the-badge&logo=java)
![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)
![Game Type](https://img.shields.io/badge/Game-Text--Based%20RPG-blue?style=for-the-badge)
**Java Knight** is a text-based RPG game developed in Java.
Embark on an epic adventure through the lands of **Javanest**, battle dangerous enemies, collect ancient keys, and face the ultimate **Dragon Boss** to save the kingdom.
---
## 🌟 Core Features
- 🎭 **Multiple Playable Classes** (Knight, Wizard, Assassin)
- 🗺️ **Location-Based Exploration System**
- ⚔️ **Turn-Based Combat Mechanics**
- 🔑 **Key Collection & Progression System**
- 💾 **Save / Load Ready (Java Serialization)**
- 📊 **RPG Systems**: HP, MP, XP, Levels
- 🛡️ **Weapons, Armor & Durability**
- 🐉 **Final Boss Battle**
---
## 🛡️ Playable Classes
| Class | HP | MP | Starting Weapon | Playstyle |
|------|----|----|----------------|-----------|
| **Knight** | High | Low | Sword | Tank / Physical Damage |
| **Wizard** | Medium | High | Magic Staff | Magic & Healing |
| **Assassin** | Medium | Medium | Daggers | Stealth & Burst Damage |
Each class has **unique abilities**, different mana costs, and a distinct combat style.
---
## 🗺️ World Map
Explore the dangerous regions of Javanest:
1. **Forest (Difficulty 1)**
_A dark forest full of Goblins._
2. **Graveyard (Difficulty 2)**
_An abandoned graveyard haunted by Skeletons._
3. **Vampire Crypt (Difficulty 3)**
_A cursed crypt ruled by Vampires._
4. **Dragon Castle (Difficulty 4)**
_The final destination and home of the Dragon._
Locations are connected, allowing the player to move freely between unlocked areas.
---
## 🎮 Game Flow
1. Start the game
2. Enter your player name
3. Choose a class
4. Begin in the **Forest**
5. Fight enemies and gain XP
6. Collect special keys
7. Unlock the **Dragon Castle**
8. Defeat the Dragon and save Javanest
---
## 🧭 Main Menu
At launch, the player is presented with the main menu:
```text
===============================
⚔ JAVA KNIGHT ⚔
===============================
1. Start Game
2. How To Play
3. Exit
Menu Options
Start Game Begin a new adventure
How To Play Learn the rules and objective
Exit Close the game
🏰 In-Game Menu
While exploring a location, the player can choose:
text
1. Fight Enemy
2. Move Location
3. Enter Dragon Castle (if unlocked)
Fight Enemy Starts a battle based on the current location
Move Location Travel to connected locations
Enter Dragon Castle Available only after collecting all keys
⚔️ Combat System
Combat is turn-based and class-dependent.
Available Actions
Light Attack
Heavy Attack
Defend
Heal
Special Ability
Exit Battle
Combat Rules
Attacks may consume MP
Special abilities are unique per class
Enemies attack after the players turn
Winning a battle grants XP
Enemies may drop keys
🔑 Key System
To unlock the final area, the player must collect:
🗝️ Goblin Key
🗝️ Skeleton Key
🗝️ Vampire Key
Each key drops from enemies in its respective location.
Once all keys are collected, the Dragon Castle becomes accessible.
📈 Progression System
HP (Health Points)
MP (Mana Points)
XP (Experience Points)
Level System
Weapon Durability
Progression rewards strategic combat and exploration.
💾 Save & Load System
The project is prepared for game saving using Java Serialization.
Serializable Components
Player
Location
Weapon
GameState
This allows the game to be saved and restored at any point.
🚀 Getting Started
Requirements
Java JDK 17 or higher
Terminal or Java IDE (IntelliJ IDEA, Eclipse, VS Code)
Run the Game
bash
javac -d out src/org/project/**/*.java
java -cp out org.project.Main
Or simply run Main.java from your IDE.
🛠️ Project Architecture
Object-Oriented Design
Clean separation of concerns
Interfaces & abstract classes
Easily extendable structure
Console-based UI with ANSI colors
🔮 Future Improvements
Inventory management menu
More weapons and consumables
Enhanced armor mechanics
Expanded world map
Graphical UI (JavaFX / Swing)
📜 License
This project is licensed under the MIT License.