Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04a4f393ed | ||
|
|
265f638aeb | ||
|
|
38d72be138 | ||
|
|
aac9687228 | ||
|
|
0e04d8a1a2 | ||
|
|
b03aad9420 | ||
|
|
ababde553b | ||
|
|
9db7c5cc3c |
Generated
+5
@@ -16,5 +16,10 @@
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://maven.myket.ir/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,15 +1,308 @@
|
||||
package org.project;
|
||||
|
||||
import org.project.entity.enemies.*;
|
||||
import org.project.entity.players.Assassin;
|
||||
import org.project.entity.players.Knight;
|
||||
import org.project.entity.players.Player;
|
||||
import org.project.entity.players.Wizard;
|
||||
import org.project.location.Location;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
// TODO: ADD LOCATIONS TO YOUR GAME
|
||||
List<Location> locations = new ArrayList<>();
|
||||
|
||||
// TODO: IMPLEMENT GAMEPLAY
|
||||
public static final String RESET = "\u001B[0m";
|
||||
public static final String RED_TEXT = "\u001B[31m";
|
||||
public static final String YELLOW_TEXT = "\u001B[33m";
|
||||
public static final String BLUE_TEXT = "\u001B[34m";
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
|
||||
// Enemies
|
||||
Enemy goblin = new Goblin();
|
||||
Enemy skeleton = new Skeleton();
|
||||
Enemy vampire = new Vampire();
|
||||
Enemy[] enemies = {goblin, vampire, skeleton}; // An array of the enemies except Dragon which is the final boss
|
||||
|
||||
// Locations, one for each enemy
|
||||
Location forest = new Location("Whispering forest", goblin);
|
||||
Location catacombs = new Location("forgotten catacombs", skeleton);
|
||||
Location castle = new Location("crimson castle", vampire);
|
||||
Location[] locations = {forest, catacombs, castle}; // An array of the location except peak where is the Dragon(final boss) located
|
||||
|
||||
System.out.println(BLUE_TEXT + "\t=============== Java Knight ===============\n" + RESET);
|
||||
boolean running = true;
|
||||
while(running){
|
||||
|
||||
System.out.println(YELLOW_TEXT + "\tChoose your option:");
|
||||
System.out.println("\t1. Start game" +
|
||||
" 2. Exit" + RESET);
|
||||
int option = scanner.nextInt();
|
||||
if(option == 1){
|
||||
startGame(locations);
|
||||
} else if (option == 2) {
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static void startGame(Location[] locations){
|
||||
//
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
Random random = new Random();
|
||||
|
||||
// Set up
|
||||
System.out.print("\tEnter your name: ");
|
||||
String name = scanner.next();
|
||||
Player player = null;
|
||||
int option = 0;
|
||||
while(option > 3 || option < 1){
|
||||
System.out.println("\n Select your character: (Enter number)");
|
||||
System.out.println("\t1. ⚔\uFE0F Knight (fights with a sword)\n" +
|
||||
"\t2. \uD83D\uDD2A Assassin (fights with dual daggers)\n" +
|
||||
"\t3. \uD83E\uDDD9 Wizard (fights with his magic staff)");
|
||||
if (scanner.hasNextInt()) {
|
||||
option = scanner.nextInt();
|
||||
} else {
|
||||
System.out.println(RED_TEXT + "\tInvalid input! Please enter a number." + RESET);
|
||||
scanner.next();
|
||||
}
|
||||
}
|
||||
switch (option){
|
||||
case 1:
|
||||
player = new Knight(name);
|
||||
System.out.printf("\n\tYour Character: %s, Your Name: %s\n\n", " ⚔\uFE0F Knight", player.getName());
|
||||
break;
|
||||
case 2:
|
||||
player = new Assassin(name);
|
||||
System.out.printf("\n\tYour Character: %s, Your Name: %s\n\n", " \uD83D\uDD2A Assassin", player.getName());
|
||||
break;
|
||||
case 3:
|
||||
player = new Wizard(name);
|
||||
System.out.printf("\n\tYour Character: %s, Your Name: %s\n\n", " \uD83E\uDDD9 Wizard", player.getName());
|
||||
break;
|
||||
default:
|
||||
player = null;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Choosing location and enemy
|
||||
Location loc = locations[random.nextInt(locations.length)];
|
||||
loc = setLocation(loc, locations, player, random, scanner);
|
||||
Enemy enemy = loc.getEnemy();
|
||||
|
||||
System.out.println(BLUE_TEXT + "\t=============== Prepare to fight ===============" + RESET);
|
||||
|
||||
while(player.isAlive()){
|
||||
|
||||
while(enemy.isAlive()){
|
||||
playerMove(player, enemy, scanner);
|
||||
if(!enemy.isStunned() && enemy.getAlive()) {
|
||||
enemyMove(enemy, player, random);
|
||||
}
|
||||
else{
|
||||
enemy.stun(false);
|
||||
}
|
||||
}
|
||||
|
||||
player.addXP(30);
|
||||
enemy.heal();
|
||||
// Key dropping (50% chance)
|
||||
boolean[] logics = {false, true};
|
||||
boolean drop = logics[random.nextInt(logics.length)];
|
||||
if(enemy.hasKey() && drop){
|
||||
player.addKey(enemy);
|
||||
}
|
||||
|
||||
// resetting player and enemy for the next battle
|
||||
player.fillMana();
|
||||
enemy.fillHP();
|
||||
|
||||
// the final battle, Dragon fight
|
||||
if(player.getKeys() == 3){
|
||||
System.out.println(BLUE_TEXT + "\t=============== Entering Ashen Peak to fight the Dragon \uD83D\uDC09 ===============" + RESET);
|
||||
enemy = new Dragon();
|
||||
loc = new Location("ashen peak", enemy);
|
||||
|
||||
while(enemy.isAlive() && player.isAlive()){
|
||||
playerMove(player, enemy, scanner);
|
||||
if(!enemy.isStunned() && enemy.isAlive()) {
|
||||
enemyMove(enemy, player, random);
|
||||
}
|
||||
else{
|
||||
enemy.stun(false);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else{
|
||||
loc = setLocation(loc, locations, player, random, scanner);
|
||||
enemy = loc.getEnemy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Location setLocation(Location currentLocation, Location[] locations, Player player, Random random, Scanner scanner){
|
||||
System.out.printf("\t%s should fight %s who fights with %s in %s\n",
|
||||
player.getName(), currentLocation.getEnemy().getName(), currentLocation.getEnemy().getWeapon().getName(), currentLocation.getName());
|
||||
int option = 0;
|
||||
while(option > 2 || option < 1){
|
||||
System.out.println(YELLOW_TEXT + "\tChoose your option: ");
|
||||
System.out.println("\t1. Fight" + currentLocation.getEnemy().getName() +
|
||||
"\t\t2. Change location" + RESET);
|
||||
if (scanner.hasNextInt()) {
|
||||
option = scanner.nextInt();
|
||||
} else {
|
||||
System.out.println(RED_TEXT + "\tInvalid input! Please enter a number." + RESET);
|
||||
scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
if(option == 1){
|
||||
return currentLocation;
|
||||
}
|
||||
else{
|
||||
Location[] temp_locations = new Location[locations.length - 1];
|
||||
int counter = 0;
|
||||
for(Location l : locations){
|
||||
if (l != currentLocation) {
|
||||
temp_locations[counter] = l;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
currentLocation = temp_locations[random.nextInt(temp_locations.length)];
|
||||
System.out.println("\tChanging locations...\n");
|
||||
setLocation(currentLocation, locations, player, random, scanner);
|
||||
}
|
||||
return currentLocation;
|
||||
}
|
||||
|
||||
public static void playerMove(Player player, Enemy enemy, Scanner scanner){
|
||||
|
||||
player.defend(false);
|
||||
|
||||
int option = 0;
|
||||
while(option > 5 || option < 1){
|
||||
System.out.println("\n Your turn. Choose your move: \n" +
|
||||
" 1. Light attack" +
|
||||
" 2. Heavy attack" +
|
||||
" 3. Defend" +
|
||||
" 4. Heal" +
|
||||
" 5. Special Ability");
|
||||
if (scanner.hasNextInt()) {
|
||||
option = scanner.nextInt();
|
||||
|
||||
if(option != 1 && player.getMp() <= 0){ // when mana runs out the only available action is light attack
|
||||
System.out.println(RED_TEXT + "\tYou are out of Mana. You can only choose \"1. Light attack\"" + RESET);
|
||||
option = 0;
|
||||
}
|
||||
else if(option == 4 && player.getHp() == player.getMaxHP()){
|
||||
System.out.println(RED_TEXT + "\tYour HP is at its maximum. Choose another option." + RESET);
|
||||
option = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
System.out.println(RED_TEXT + "\tInvalid input! Please enter a number." + RESET);
|
||||
scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
switch (option){
|
||||
case 1: // light attack
|
||||
if(!enemy.isDefending()){
|
||||
player.lightAttack(enemy);
|
||||
}
|
||||
break;
|
||||
case 2: // heavy attack
|
||||
if(player.getMp() >= player.getWeapon().getManaCost()) {
|
||||
player.heavyAttack(enemy);
|
||||
}
|
||||
else{
|
||||
System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
|
||||
playerMove(player, enemy, scanner);
|
||||
}
|
||||
break;
|
||||
case 3: // defend
|
||||
if(player.getMp() >= player.getDefendMP()) {
|
||||
player.defend(true);
|
||||
}
|
||||
else{
|
||||
System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
|
||||
playerMove(player, enemy, scanner);
|
||||
}
|
||||
break;
|
||||
case 4: // heal
|
||||
if(player.getMp() >= player.getDefendMP()) {
|
||||
player.heal( (int)((0.2)*player.getMaxHP()) );
|
||||
}
|
||||
else{
|
||||
System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
|
||||
playerMove(player, enemy, scanner);
|
||||
}
|
||||
break;
|
||||
case 5: // special ability
|
||||
if(player.getMp() >= player.getSpecialAbilityMP()) {
|
||||
player.specialAbility(enemy);
|
||||
}
|
||||
else{
|
||||
System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
|
||||
playerMove(player, enemy, scanner);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
showStatus(player, enemy);
|
||||
|
||||
}
|
||||
|
||||
public static void enemyMove(Enemy enemy, Player player, Random random){
|
||||
|
||||
enemy.defend(false);
|
||||
enemy.stun(false); // have been checked before calling this method
|
||||
|
||||
System.out.println("\t" + enemy.getName() + "'s turn: ");
|
||||
|
||||
int move_id = random.nextInt(5) + 1;
|
||||
switch (move_id){
|
||||
case 1: // attack (light attack)
|
||||
enemy.attack(player);
|
||||
break;
|
||||
case 2: // special ability (heavy attack)
|
||||
if(enemy instanceof Skeleton && enemy.isRevived()){
|
||||
enemyMove(enemy, player, random);
|
||||
}
|
||||
enemy.specialAbility(player);
|
||||
break;
|
||||
case 3: // defend
|
||||
enemy.defend(true);
|
||||
break;
|
||||
case 4: // heal
|
||||
enemy.heal( (int)((0.2)*player.getMaxHP()) );
|
||||
break;
|
||||
}
|
||||
|
||||
showStatus(player, enemy);
|
||||
|
||||
}
|
||||
|
||||
public static void showStatus(Player player, Enemy enemy){
|
||||
System.out.printf("\n\t[%s - %d/%d HP | %d/%d Mana]\n\t[%s - %d/%d HP]\n\n",
|
||||
player.getName(), player.getHp(), player.getMaxHP(), player.getMp(), player.getMaxMP(),
|
||||
enemy.getName(), enemy.getHp(), enemy.getMaxHP()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
package org.project.entity;
|
||||
|
||||
public interface Entity {
|
||||
void attack(Entity target);
|
||||
|
||||
void defend();
|
||||
void specialAbility(Entity target);
|
||||
|
||||
void defend(boolean value);
|
||||
|
||||
boolean isDefending();
|
||||
|
||||
void heal(int health);
|
||||
|
||||
void fillMana(int mana);
|
||||
// void fillMana(int mana);
|
||||
|
||||
void takeDamage(int damage);
|
||||
|
||||
int getMaxHP();
|
||||
void stun(boolean value);
|
||||
|
||||
int getMaxMP();
|
||||
String getName();
|
||||
|
||||
boolean isAlive();
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS METHODS
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.Flame;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Dragon extends Enemy{
|
||||
|
||||
private static int maxHP = 220;
|
||||
private int hp = maxHP;
|
||||
|
||||
public static final String RESET = "\u001B[0m";
|
||||
public static final String GREEN_TEXT = "\u001B[32m";
|
||||
|
||||
public Dragon(){
|
||||
Weapon DragonWeapon = new Flame();
|
||||
super(maxHP, DragonWeapon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attack(Entity target) {
|
||||
System.out.printf("\t%s attacks!\n", getName());
|
||||
target.takeDamage(28);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
System.out.printf("\t%s breathes fire! \uD83D\uDD25\n", getName());
|
||||
target.takeDamage(weapon.getDamage());
|
||||
}
|
||||
|
||||
public int getMaxHP() {
|
||||
return maxHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
return " \uD83D\uDC09 Dragon";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
if(hp <= 0){
|
||||
System.out.printf(GREEN_TEXT + "\t=============== Victory! ===============\n" + RESET, getName());
|
||||
alive = false;
|
||||
}
|
||||
return alive;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +1,124 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Enemy {
|
||||
public abstract class Enemy implements Entity {
|
||||
Weapon weapon;
|
||||
private int hp;
|
||||
private int mp;
|
||||
protected int hp;
|
||||
protected int maxHP;
|
||||
protected boolean defending = false;
|
||||
private boolean stunned = false;
|
||||
protected boolean revived = false;
|
||||
protected boolean alive = true;
|
||||
protected boolean key = true;
|
||||
|
||||
public Enemy(int hp, int mp, Weapon weapon) {
|
||||
public static final String BLUE_TEXT = "\u001B[34m";
|
||||
public static final String RESET = "\u001B[0m";
|
||||
|
||||
public Enemy(int hp, Weapon weapon) {
|
||||
this.hp = hp;
|
||||
this.mp = mp;
|
||||
|
||||
maxHP = hp; // TODO: fix this from superclasses
|
||||
this.weapon = weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage) {
|
||||
hp -= damage;
|
||||
System.out.printf("\t%s took %d damage \uD83D\uDCA5\n", getName(), damage);
|
||||
}
|
||||
|
||||
public void attack(Entity target) {
|
||||
System.out.printf("\t%s used Light Attack!\n", getName());
|
||||
if(target.isDefending()){
|
||||
System.out.printf("\t%s is defending. %s lost his attack.\n", target.getName(), getName());
|
||||
return;
|
||||
}
|
||||
target.takeDamage(weapon.getDamage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defend(boolean value) {
|
||||
if(value) {
|
||||
System.out.printf("\t%s used Defend, You cannot attack in your next turn! \uD83D\uDEE1\uFE0F\n", getName());
|
||||
defending = true;
|
||||
return;
|
||||
}
|
||||
defending = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heal(int health) {
|
||||
System.out.printf("\t%s used Heal! ❤\uFE0F\n", getName());
|
||||
hp += health;
|
||||
if (hp > maxHP) {
|
||||
hp = maxHP;
|
||||
}
|
||||
}
|
||||
|
||||
public void heal(){
|
||||
hp = maxHP;
|
||||
alive = true;
|
||||
}
|
||||
|
||||
public void fillHP(){
|
||||
hp = maxHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stun(boolean value) {
|
||||
stunned = value;
|
||||
}
|
||||
|
||||
public void dropKey(){
|
||||
key = false;
|
||||
}
|
||||
|
||||
public int getHp() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
public int getMp() {
|
||||
return mp;
|
||||
public int getMaxHP() {
|
||||
return maxHP;
|
||||
}
|
||||
|
||||
public Weapon getWeapon() {
|
||||
return weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefending() {
|
||||
return defending;
|
||||
}
|
||||
|
||||
public boolean isStunned() {
|
||||
return stunned;
|
||||
}
|
||||
|
||||
public boolean isRevived() {
|
||||
return revived;
|
||||
}
|
||||
|
||||
public boolean hasKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
if(hp <= 0){
|
||||
System.out.printf(BLUE_TEXT + "\t=============== You defeated %s =============== " + RESET + "\n\tGoing to the next Location...", getName());
|
||||
|
||||
alive = false;
|
||||
}
|
||||
return alive;
|
||||
}
|
||||
|
||||
public boolean getAlive(){
|
||||
if(hp <= 0){
|
||||
alive = false;
|
||||
}
|
||||
return alive;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.Dagger;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Goblin extends Enemy{
|
||||
|
||||
private static int maxHP = 65;
|
||||
private int hp = maxHP;
|
||||
private int criticalHit_damage = 20;
|
||||
|
||||
public Goblin(){
|
||||
Weapon goblinWeapon = new Dagger();
|
||||
super(maxHP, goblinWeapon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
if(target.isDefending()){
|
||||
System.out.printf("\t%s is defending. %s lost his attack. \uD83D\uDEE1\uFE0F\n", target.getName(), getName());
|
||||
return;
|
||||
}
|
||||
System.out.printf("\t%s used critical hit! \uD83D\uDCA5\n", getName());
|
||||
target.takeDamage(criticalHit_damage);
|
||||
}
|
||||
|
||||
public int getMaxHP() {
|
||||
return maxHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
return " \uD83D\uDC7A Goblin";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,33 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class Skeleton {
|
||||
// TODO: DESIGN ENEMY AND IMPLEMENT THE CONSTRUCTOR
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.BoneClub;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Skeleton extends Enemy{
|
||||
|
||||
private static int maxHP = 80;
|
||||
private int hp = maxHP;
|
||||
|
||||
public Skeleton(){
|
||||
Weapon SkeletonWeapon = new BoneClub();
|
||||
super(maxHP, SkeletonWeapon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
System.out.printf("\t%s revived itself! ❤\uFE0F\n", getName());
|
||||
hp = maxHP/2; // revive (once per battle)
|
||||
revived = true;
|
||||
}
|
||||
|
||||
public int getMaxHP() {
|
||||
return maxHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
return " \uD83D\uDC80 Skeleton";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.project.entity.enemies;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.Bite;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
public class Vampire extends Enemy{
|
||||
|
||||
private static int maxHP = 95;
|
||||
private int hp = maxHP;
|
||||
|
||||
public Vampire(){
|
||||
Weapon VampireWeapon = new Bite();
|
||||
super(maxHP, VampireWeapon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
System.out.printf("\t%s used life steal ability! \uD83E\uDDEB\n", getName());
|
||||
target.takeDamage((int)((0.8)*(weapon.getDamage())));
|
||||
hp += (int)((0.2)*(weapon.getDamage()));
|
||||
}
|
||||
|
||||
public int getMaxHP() {
|
||||
return maxHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
return " \uD83E\uDDDB Vampire";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.project.entity.players;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.DualDagger;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
|
||||
public class Assassin extends Player {
|
||||
|
||||
private static final int maxHP = 110;
|
||||
private static final int maxMP = 110;
|
||||
|
||||
public Assassin(String name){
|
||||
Weapon AssassinWeapon = new DualDagger();
|
||||
super(name, maxHP, maxMP, AssassinWeapon);
|
||||
specialAbilityMP = 30;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightAttack(Entity target) {
|
||||
super.lightAttack(target);
|
||||
System.out.printf("\t%s used Light Attack! (%d Mana)\n", name, 0);
|
||||
target.takeDamage(18);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
System.out.printf("\t%s used Special Ability \uD83D\uDCA5! Enemy gets hit critically (%d Mana)\n", name, 30);
|
||||
target.takeDamage(52); // critical attack
|
||||
this.mp -= specialAbilityMP;
|
||||
defending = true; // dodging (becomes invisible), technically does what Defend does.
|
||||
}
|
||||
}
|
||||
@@ -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.Sword;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
|
||||
public class Knight extends Player {
|
||||
|
||||
private static final int maxHP = 130;
|
||||
private static final int maxMP = 70;
|
||||
|
||||
public Knight(String name){
|
||||
Weapon knightWeapon = new Sword();
|
||||
super(name, maxHP, maxMP, knightWeapon);
|
||||
specialAbilityMP = 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightAttack(Entity target) {
|
||||
super.lightAttack(target);
|
||||
System.out.printf("\t%s used Light Attack! (%d Mana)\n", name, 0);
|
||||
target.takeDamage(22);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
System.out.printf("\t%s used Special Ability! Enemy misses its next turn. \uD83E\uDDF1 (%d Mana)\n", name, 25);
|
||||
target.takeDamage(30);
|
||||
this.mp -= specialAbilityMP;
|
||||
target.stun(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,61 +1,106 @@
|
||||
package org.project.entity.players;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.armors.Armor;
|
||||
import org.project.entity.enemies.Enemy;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Player {
|
||||
public abstract class Player implements Entity {
|
||||
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 boolean defending = false;
|
||||
private boolean stunned = false;
|
||||
protected int keys;
|
||||
protected boolean alive = true;
|
||||
|
||||
public Player(String name, int hp, int mp, Weapon weapon, Armor armor) {
|
||||
protected final int defendMP = 20;
|
||||
protected int specialAbilityMP;
|
||||
|
||||
public static final String RESET = "\u001B[0m";
|
||||
public static final String RED_TEXT = "\u001B[31m";
|
||||
|
||||
|
||||
public Player(String name, int hp, int mp, Weapon weapon) {
|
||||
this.name = name;
|
||||
this.hp = hp;
|
||||
maxHP = hp;
|
||||
this.mp = mp;
|
||||
|
||||
maxMP = mp;
|
||||
this.weapon = weapon;
|
||||
this.armor = armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attack(Entity target) {
|
||||
public void lightAttack(Entity target){
|
||||
if(target.isDefending()){ // check if enemy is defending or not
|
||||
System.out.printf("\t%s is Defending. You missed your attack. \uD83D\uDEE1\uFE0F \n", target.getName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void heavyAttack(Entity target){ // Uses weapons
|
||||
if(target.isDefending()){ // check if enemy is defending or not
|
||||
System.out.printf("\t%s is Defending. You missed your attack. \uD83D\uDEE1\uFE0F\n", target.getName());
|
||||
return;
|
||||
}
|
||||
System.out.printf("\t%s used Heavy Attack \uD83D\uDCA5! (%d Mana)\n", name, weapon.getManaCost());
|
||||
target.takeDamage(weapon.getDamage());
|
||||
mp -= weapon.getManaCost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defend() {
|
||||
// TODO
|
||||
public void defend(boolean value) {
|
||||
if(value){
|
||||
System.out.printf("\t%s used Defend, Enemy cannot attack in his next turn \uD83D\uDEE1\uFE0F! (%d Mana)\n", name, 20);
|
||||
mp -= 20;
|
||||
defending = true;
|
||||
return;
|
||||
}
|
||||
defending = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void takeDamage(int damage) {
|
||||
hp -= damage - armor.getDefense();
|
||||
hp -= damage;
|
||||
System.out.printf("\t%s took %d damage \uD83D\uDCA5\n", name, damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heal(int health) {
|
||||
System.out.printf("\t%s used Heal ❤\uFE0F! (%d Mana)\n", name, 20);
|
||||
hp += health;
|
||||
if (hp > maxHP) {
|
||||
hp = maxHP;
|
||||
}
|
||||
mp -= 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillMana(int mana) {
|
||||
mp += mana;
|
||||
if (mp > maxMP) {
|
||||
mp = maxMP;
|
||||
}
|
||||
public void stun(boolean value) {
|
||||
stunned = value;
|
||||
}
|
||||
|
||||
|
||||
public void fillMana() {
|
||||
mp = maxMP;
|
||||
hp = maxHP;
|
||||
}
|
||||
|
||||
public void addKey(Enemy target){
|
||||
keys++;
|
||||
target.dropKey();
|
||||
System.out.printf("\t%s gained %s's key \uD83D\uDDDD\uFE0F! gained keys: %d\n\n", getName(), target.getName(), keys);
|
||||
}
|
||||
|
||||
public void addXP(int xp){
|
||||
System.out.printf("\n\t%d XP added.\n", xp);
|
||||
maxMP += xp;
|
||||
maxHP += xp;
|
||||
}
|
||||
|
||||
|
||||
// Getter Methods
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -64,7 +109,6 @@ public abstract class Player {
|
||||
return hp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHP() {
|
||||
return maxHP;
|
||||
}
|
||||
@@ -73,7 +117,6 @@ public abstract class Player {
|
||||
return mp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMP() {
|
||||
return maxMP;
|
||||
}
|
||||
@@ -82,8 +125,29 @@ public abstract class Player {
|
||||
return weapon;
|
||||
}
|
||||
|
||||
public Armor getArmor() {
|
||||
return armor;
|
||||
@Override
|
||||
public boolean isDefending() {
|
||||
return defending;
|
||||
}
|
||||
|
||||
public int getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public int getDefendMP() {
|
||||
return defendMP;
|
||||
}
|
||||
|
||||
public int getSpecialAbilityMP() {
|
||||
return specialAbilityMP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
if(hp <= 0){
|
||||
System.out.println(RED_TEXT + "\t=============== Game Over ===============" + RESET);
|
||||
alive = false;
|
||||
}
|
||||
return alive;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.project.entity.players;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.weapons.MagicStaff;
|
||||
import org.project.item.weapons.Weapon;
|
||||
|
||||
|
||||
public class Wizard extends Player {
|
||||
|
||||
private static final int maxHP = 150;
|
||||
private static final int maxMP = 85;
|
||||
|
||||
public Wizard(String name){
|
||||
Weapon WizardWeapon = new MagicStaff();
|
||||
super(name, maxHP, maxMP, WizardWeapon);
|
||||
specialAbilityMP = 28;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightAttack(Entity target) {
|
||||
super.lightAttack(target);
|
||||
System.out.printf("\t%s used Light Attack! (%d Mana)\n", name, 0);
|
||||
target.takeDamage(16);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void specialAbility(Entity target) {
|
||||
System.out.printf("\t%s used Special Ability! Casting spell. \uD83E\uDE84 (%d Mana)\n", name, 28);
|
||||
target.takeDamage(32); // casts spell
|
||||
this.mp -= specialAbilityMP;
|
||||
heal(15);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,4 @@ import org.project.entity.Entity;
|
||||
public interface Item {
|
||||
void use(Entity target);
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS METHODS
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.project.item.armors;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Armor {
|
||||
private int defense;
|
||||
private int maxDefense;
|
||||
private int durability;
|
||||
private int maxDurability;
|
||||
|
||||
private boolean isBroke;
|
||||
|
||||
public Armor(int defense, int durability) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package org.project.item.armors;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class KnightArmor {
|
||||
// TODO: DESIGN ARMOR'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package org.project.item.consumables;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Consumable {
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS METHODS
|
||||
*/
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.project.item.consumables;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class Flask {
|
||||
/*
|
||||
THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN.
|
||||
*/
|
||||
|
||||
// TODO: UPDATE USE METHOD
|
||||
@Override
|
||||
public void use(Entity target) {
|
||||
target.heal(target.getMaxHP() / 10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Bite is used by Vampire
|
||||
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class Bite extends Weapon {
|
||||
|
||||
private static final int damage = 16;
|
||||
|
||||
public Bite() {
|
||||
super(damage, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Bite";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// BoneClub is used by Skeleton
|
||||
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class BoneClub extends Weapon {
|
||||
|
||||
private static final int damage = 14;
|
||||
|
||||
public BoneClub() {
|
||||
super(damage, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Bone Club";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Dagger is used by Goblin
|
||||
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class Dagger extends Weapon {
|
||||
|
||||
private static final int damage = 12;
|
||||
|
||||
public Dagger() {
|
||||
super(damage, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Daggers";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// DualDagger is used by Assassin
|
||||
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class DualDagger extends Weapon {
|
||||
|
||||
private static final int damage = 30;
|
||||
private static final int manaCost = 15;
|
||||
|
||||
public DualDagger() {
|
||||
super(damage, manaCost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Dual Daggers";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Flame is used by Dragon
|
||||
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class Flame extends Weapon {
|
||||
|
||||
private static final int damage = 36;
|
||||
|
||||
public Flame() {
|
||||
super(damage, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Flame";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Maggic Staff is used by Wizard
|
||||
|
||||
package org.project.item.weapons;
|
||||
|
||||
public class MagicStaff extends Weapon {
|
||||
|
||||
private static final int damage = 28;
|
||||
private static final int manaCost = 16;
|
||||
|
||||
public MagicStaff() {
|
||||
super(damage, manaCost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Magic Staff";
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,18 @@
|
||||
// Sword is used by Knight
|
||||
|
||||
package org.project.item.weapons;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
public class Sword extends Weapon {
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public class Sword {
|
||||
/*
|
||||
THIS IS AN EXAMPLE OF A WEAPON DESIGN.
|
||||
*/
|
||||
|
||||
int abilityCharge;
|
||||
private static final int damage = 34;
|
||||
private static final int manaCost = 15;
|
||||
|
||||
public Sword() {
|
||||
// TODO: DESIGN SWORD'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR
|
||||
super(damage, manaCost);
|
||||
}
|
||||
|
||||
// TODO: (BONUS) UPDATE THE UNIQUE ABILITY
|
||||
public void uniqueAbility(ArrayList<Entity> targets) {
|
||||
abilityCharge += 2;
|
||||
for (Entity target : targets) {
|
||||
target.takeDamage(getDamage());
|
||||
}
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Sword";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package org.project.item.weapons;
|
||||
|
||||
import org.project.entity.Entity;
|
||||
import org.project.item.Item;
|
||||
|
||||
// TODO: UPDATE IMPLEMENTATION
|
||||
public abstract class Weapon {
|
||||
public abstract class Weapon implements Item {
|
||||
private int damage;
|
||||
private int manaCost;
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS ATTRIBUTES
|
||||
*/
|
||||
|
||||
public Weapon(int damage, int manaCost) {
|
||||
this.damage = damage;
|
||||
@@ -29,7 +26,6 @@ public abstract class Weapon {
|
||||
return manaCost;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: ADD OTHER REQUIRED AND BONUS METHODS
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,23 +6,19 @@ import java.util.ArrayList;
|
||||
|
||||
public class Location {
|
||||
private String name;
|
||||
private Enemy enemy;
|
||||
|
||||
private ArrayList<Enemy> enemies;
|
||||
|
||||
public Location(ArrayList<Location> locations, ArrayList<Enemy> enemies) {
|
||||
this.locations = locations;
|
||||
this.enemies = enemies;
|
||||
public Location(String name, Enemy enemy) {
|
||||
this.name = name;
|
||||
this.enemy = enemy;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ArrayList<Location> getLocations() {
|
||||
return locations;
|
||||
public Enemy getEnemy() {
|
||||
return enemy;
|
||||
}
|
||||
|
||||
public ArrayList<Enemy> getEnemies() {
|
||||
return enemies;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,175 +1,68 @@
|
||||
# Fourth Assignment - Java Knight ⚔️
|
||||
# Java Knight
|
||||
A turn-based RPG with Roguelike elements which can be run in the terminal.
|
||||
An assignment for AP course.
|
||||
|
||||
### **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!*
|
||||
The goal of this assignment is to demonstrate OOP concepts in Java.
|
||||
|
||||
### **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**.
|
||||
### **Game Story**
|
||||
*You are stock in land of Javanest where is under attack of a magical dragon and his little monsters (Goblin, Skeleton and Vampire).
|
||||
Each monster holds a key with itself, and you are supposed to fight them and gain their key.
|
||||
Once you got all 3 keys, you go to the ashen peaks where the dragon is waiting for you for the final battle.
|
||||
If you kill the dragon, Javanest is rescued.*
|
||||
|
||||
⚠️ **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.
|
||||
### **How to play**
|
||||
First, you choose a character to play with. The characters are as followed:
|
||||
1. **Knight** : Uses a sword and has a high base damage
|
||||
2. **Assassin** : Uses dual daggers and is a die hard
|
||||
3. **Wizard** : Uses his magic staff and has high stamina
|
||||
|
||||
🎯 **Your goal is not just to complete the assignment but to learn and apply OOP effectively!**
|
||||
After you chose your character, you fight little monsters. It is your call which one.
|
||||
1. **Goblin** : Uses a dagger and is located in the whispering forest
|
||||
2. **Skeleton** : Uses his bone club and is located in the forgotten catacombs
|
||||
3. **Vampire** : Bites and is located in the crimson castle
|
||||
4. **Dragon (Final Boss)** : Breathes fire and is in the ashen peak
|
||||
|
||||
### **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
|
||||
|
||||

|
||||
|
||||
### 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.
|
||||
In each battle you and your enemy take turns to choose among five options:
|
||||
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.
|
||||
- **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.
|
||||
After defeating each monster, there is a 50 percent chance that it will drop its key.
|
||||
You can only fight the dragon if you gain all 3 keys.
|
||||
|
||||
🔹 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)
|
||||
### **OOP Structure**
|
||||
**Entities**
|
||||
```Class Hierarchy
|
||||
Entity (abstract)
|
||||
│
|
||||
├── Player (abstract)
|
||||
│ ├── Knight
|
||||
│ ├── Assassin
|
||||
│ └── Wizard
|
||||
│
|
||||
└── Enemy (abstract)
|
||||
├── Goblin
|
||||
├── Skeleton
|
||||
├── Vampire
|
||||
└── Dragon
|
||||
```
|
||||
|
||||
```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
|
||||
**Items**
|
||||
```Class Hierarchy
|
||||
Item (abstract)
|
||||
│
|
||||
└── Weapon (abstract)
|
||||
├── Sword
|
||||
├── MagicStaff
|
||||
├── DualDagger
|
||||
├── Dagger
|
||||
├── BoneClub
|
||||
├── Bite
|
||||
└── Flame
|
||||
```
|
||||
```
|
||||
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.
|
||||
|
||||

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