diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7c34d9c..dc9f2b1 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,11 +1,14 @@ + \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/GameEngine.java b/Java-Ring/src/main/java/org/project/GameEngine.java new file mode 100644 index 0000000..1662405 --- /dev/null +++ b/Java-Ring/src/main/java/org/project/GameEngine.java @@ -0,0 +1,271 @@ +package org.project; + +import org.project.entity.enemies.Enemy; +import org.project.entity.players.*; +import org.project.location.Location; +import org.project.object.consumables.Consumable; +import java.util.Scanner; + +/** + * Main game engine class that controls the game flow and manages player interactions. + * Handles initialization, game loop, combat, and menu systems. + */ +public class GameEngine { + // Game state variables + private Player player; // Current player character + private Location currentLocation; // Active game location + private Scanner scanner = new Scanner(System.in); // Input handler + private boolean gameRunning = true; // Main game loop flag + + /** + * Starts the main game loop and manages the game lifecycle + */ + public void startGame() { + initializePlayer(); // Set up player character + initializeLocations(); // Initialize game world + + // Main game loop - runs while game is active and player is alive + while (gameRunning && player.isAlive()) { + displayMainMenu(); // Show player options + handleMainMenuInput(); // Process player choice + } + + endGame(); // Handle game conclusion + } + + /** + * Initializes player character with name and class selection + */ + private void initializePlayer() { + System.out.println("=== CHARACTER CREATION ==="); + System.out.print("Enter your name: "); + String name = scanner.nextLine(); + + // Class selection menu + System.out.println("Choose your class:"); + System.out.println("1. Knight\n2. Wizard\n3. Assassin"); + + int classChoice = Integer.parseInt(scanner.nextLine()); + + // Create player based on class choice + player = switch (classChoice) { + case 1 -> new Knight(name); // Tank class + case 2 -> new Wizard(name); // Mage class + default -> new Knight(name); // Default to Knight + }; + + System.out.printf("%s the %s enters the Java Ring!%n", + player.getName(), player.getClass().getSimpleName()); + } + + /** + * Initializes game locations and starting area + */ + private void initializeLocations() { + // Create starting location with description + currentLocation = new Location("Forgotten Forest", + "A dense woodland shrouded in mist"); + } + + /** + * Displays the main game menu options + */ + private void displayMainMenu() { + System.out.println("\n=== MAIN MENU ==="); + System.out.println("Location: " + currentLocation.getName()); + System.out.println("1. Explore"); // Risk encounter + System.out.println("2. Character Info"); // View stats + System.out.println("3. Quit"); // Exit game + System.out.print("Choose an action: "); + } + + /** + * Handles player input from main menu + */ + private void handleMainMenuInput() { + int choice = getValidInput(1, 3); // Get validated input + + switch (choice) { + case 1 -> explore(); // Enter exploration + case 2 -> displayCharacterInfo(); // Show character sheet + case 3 -> gameRunning = false; // Quit game + default -> System.out.println("Invalid choice!"); + } + } + + /** + * Handles exploration logic and random encounters + */ + private void explore() { + System.out.println("\nYou venture deeper into " + currentLocation.getName()); + + // 70% chance of enemy encounter + if (Math.random() < 0.7) { + Enemy enemy = currentLocation.generateEnemy(); + if (enemy != null) { + System.out.println("Encountered: " + enemy.getDescription()); + startCombat(enemy); // Begin combat if enemy exists + } else { + System.out.println("The enemy mysteriously vanished..."); + } + } else { + System.out.println("The path is clear for now..."); + } + currentLocation.increaseDanger(); // Scale difficulty + } + + /** + * Manages combat between player and enemy + * @param enemy The enemy to fight + */ + private void startCombat(Enemy enemy) { + System.out.printf("%nA wild %s appears!%n", enemy.getName()); + + // Combat continues until one combatant is defeated + while (player.isAlive() && enemy.isAlive()) { + playerTurn(enemy); // Player action phase + + if (!enemy.isAlive()) break; // Check if enemy defeated + + enemyTurn(enemy); // Enemy action phase + } + + // Combat resolution + if (player.isAlive()) { + System.out.printf("You defeated the %s!%n", enemy.getName()); + player.gainExperience(enemy.getExpReward()); // Award XP + } else { + System.out.println("You have been defeated..."); + } + } + + /** + * Handles player's turn during combat + * @param enemy The current combat target + */ + private void playerTurn(Enemy enemy) { + System.out.println("\n=== YOUR TURN ==="); + // Display combat status + System.out.printf("HP: %d/%d | Enemy HP: %d%n", + player.getHealth(), player.getMaxHealth(), enemy.getHealth()); + + // Combat action menu + System.out.println("1. Attack"); + System.out.println("2. Defend"); + System.out.println("3. Special Ability"); + System.out.println("4. Use Item"); + System.out.print("Choose action: "); + + int choice = getValidInput(1, 4); // Validate input + + // Process player choice + switch (choice) { + case 1 -> { // Standard attack + player.attack(enemy); + System.out.printf("%s attacks %s!%n", + player.getName(), enemy.getName()); + } + case 2 -> { // Defensive stance + player.defend(); + System.out.println(player.getName() + " defends!"); + } + case 3 -> { // Class-specific ability + if (player instanceof Knight) { + ((Knight)player).useSpecialAbility(enemy); + } else { + player.useSpecialAbility(enemy); + } + System.out.printf("%s uses special ability on %s!%n", + player.getName(), enemy.getName()); + } + case 4 -> useItemInCombat(); // Inventory usage + default -> System.out.println("Invalid choice! Lost your turn..."); + } + } + + /** + * Validates player input within a specified range + * @param min Minimum valid value + * @param max Maximum valid value + * @return Validated user input + */ + private int getValidInput(int min, int max) { + while (true) { + try { + int choice = Integer.parseInt(scanner.nextLine()); + if (choice >= min && choice <= max) return choice; + System.out.printf("Enter %d-%d: ", min, max); + } catch (NumberFormatException e) { + System.out.print("(Numbers only) "); + } + } + } + + /** + * Handles enemy's turn during combat + * @param enemy The attacking enemy + */ + private void enemyTurn(Enemy enemy) { + System.out.printf("%n=== %s's TURN ===%n", enemy.getName()); + enemy.attack(player); // Enemy performs attack + } + + /** + * Manages item usage during combat + */ + private void useItemInCombat() { + if (player.getInventory().isEmpty()) { + System.out.println("Your inventory is empty!"); + return; + } + + // Display inventory + System.out.println("\nAvailable items:"); + for (int i = 0; i < player.getInventory().size(); i++) { + Consumable item = player.getInventory().get(i); + System.out.printf("%d. %s (%d uses)%n", + i + 1, item.getName(), item.getRemainingUses()); + } + + System.out.print("Select item (0 to cancel): "); + int choice = getValidInput(0, player.getInventory().size()) - 1; + + if (choice >= 0) { + player.useItem(choice); // Use selected item + } + } + + /** + * Displays character statistics and equipment + */ + private void displayCharacterInfo() { + System.out.println("\n=== CHARACTER SHEET ==="); + System.out.printf("Name: %s%n", player.getName()); + System.out.printf("Class: %s%n", player.getClass().getSimpleName()); + System.out.printf("Level: %d%n", player.getLevel()); + System.out.printf("HP: %d/%d%n", player.getHealth(), player.getMaxHealth()); + System.out.printf("XP: %d/%d%n", + player.getExperience(), player.getLevel() * 100); + System.out.printf("Weapon: %s%n", player.getWeapon().getDescription()); + System.out.printf("Armor: %s%n", player.getArmor().getDescription()); + } + + /** + * Handles game conclusion with appropriate message + */ + private void endGame() { + if (player.isAlive()) { + System.out.println("You leave the Java Ring... for now."); + } else { + System.out.println("GAME OVER"); + } + } + + /** + * Entry point for the game + * @param args Command line arguments (unused) + */ + public static void main(String[] args) { + new GameEngine().startGame(); // Launch game + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/Main.java b/Java-Ring/src/main/java/org/project/Main.java deleted file mode 100644 index c2e0550..0000000 --- a/Java-Ring/src/main/java/org/project/Main.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.project; - -import org.project.location.Location; - -import java.util.ArrayList; -import java.util.List; - -public class Main { - public static void main(String[] args) { - // TODO: ADD SOME LOCATIONS TO YOUR GAME - List locations = new ArrayList<>(); - - // TODO: IMPLEMENT GAMEPLAY - } -} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/entity/Entity.java b/Java-Ring/src/main/java/org/project/entity/Entity.java index 2a060f9..3ce5f25 100644 --- a/Java-Ring/src/main/java/org/project/entity/Entity.java +++ b/Java-Ring/src/main/java/org/project/entity/Entity.java @@ -1,21 +1,33 @@ package org.project.entity; public interface Entity { + // Combat Core void attack(Entity target); - - void defend(); - + void takeDamage(int damage); void heal(int health); - void fillMana(int mana); - void takeDamage(int damage); - int getMaxHP(); + // Status methods + boolean isAlive(); + + int getHealth(); + int getMaxHealth(); - int getMaxMP(); - /* - TODO: ADD OTHER REQUIRED AND BONUS METHODS - */ + // Magic/Ability system + void useSpecialAbility(Entity target); + void defend(); + + String getName(); + + // Resource management + void gainExperience(int amount); + default boolean isDefending() { return false; } + void healMana(int amount); + default int getMana() { return 0; } + default int getMaxMana() { return 0; } + + + String getDescription(); } diff --git a/Java-Ring/src/main/java/org/project/entity/enemies/Dragon.java b/Java-Ring/src/main/java/org/project/entity/enemies/Dragon.java new file mode 100644 index 0000000..4906f07 --- /dev/null +++ b/Java-Ring/src/main/java/org/project/entity/enemies/Dragon.java @@ -0,0 +1,158 @@ +package org.project.entity.enemies; + +import org.project.entity.Entity; +import org.project.object.weapons.Claw; + +/** + * Dragon enemy class - A powerful fire-breathing creature with damage reduction. + * Special abilities include fire breath attacks and burn damage over time. + */ +public class Dragon extends Enemy { + // Combat state tracking + private boolean usedFireBreath; // Flag for emergency fire breath + private static final int FIRE_BREATH_COOLDOWN = 3; // Turns between fire breaths + private int fireBreathCooldown = 0; // Current cooldown counter + private static final double BURN_CHANCE = 0.3; // 30% chance to apply burn + + /** + * Constructs a new Dragon enemy + * @param health Initial health points + * @param expReward Experience granted when defeated + * @param claw Natural weapon (Claw) for basic attacks + */ + public Dragon(int health, int expReward, Claw claw) { + super("Dragon", health, new Claw(), expReward); + this.usedFireBreath = false; + } + + /** + * Dragon's attack logic with fire breath conditions + * @param target The entity being attacked + */ + @Override + public void attack(Entity target) { + // Emergency fire breath when below 50% health (once per combat) + if (!usedFireBreath && health < maxHealth / 2) { + useSpecialAbility(target); + usedFireBreath = true; + } + // Random fire breath (25% chance when off cooldown) + else if (fireBreathCooldown <= 0 && Math.random() < 0.25) { + useSpecialAbility(target); + fireBreathCooldown = FIRE_BREATH_COOLDOWN; + } + // Standard claw attack + else { + super.attack(target); + // Reduce cooldown if active + if (fireBreathCooldown > 0) { + fireBreathCooldown--; + } + } + } + + /** + * Dragon's special ability - Fire Breath + * Deals heavy damage with chance to apply burn effect + * @param target The entity being targeted + */ + @Override + public void useSpecialAbility(Entity target) { + System.out.println("Dragon rears back and unleashes a torrent of flames!"); + + // Calculate base damage (2.5x weapon damage) + int baseDamage = (int) (weapon.getDamage() * 2.5); + + // Halve damage if target is defending + int actualDamage = target.isDefending() ? baseDamage / 2 : baseDamage; + + // Apply damage + target.takeDamage(actualDamage); + System.out.printf("%s takes %d fire damage!%n", target.getName(), actualDamage); + + // 30% chance to apply burn effect + if (Math.random() < BURN_CHANCE) { + int burnDamage = (int) (baseDamage * 0.3); + applyBurn(target, burnDamage); + } + + // Dragon takes 5% max health as self-damage (exhaustion) + this.takeDamage((int) (maxHealth * 0.05)); + } + + /** + * Applies burn damage over time (3 turns) + * @param target The burning entity + * @param burnDamage Damage per turn + */ + private void applyBurn(Entity target, int burnDamage) { + System.out.printf(" %s catches fire and will take %d damage over time!%n", + target.getName(), burnDamage * 3); + System.out.println(target.getName() + " is burning!"); + + // Burn effect in separate thread (non-blocking) + new Thread(() -> { + try { + // Damage over 3 turns + for (int i = 0; i < 3 && target.isAlive(); i++) { + Thread.sleep(2000); // 2 second delay between ticks + target.takeDamage(burnDamage); + System.out.println(target.getName() + " takes " + burnDamage + " burn damage!"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + /** + * Dragon takes reduced damage due to thick scales + * @param amount Incoming damage amount + */ + @Override + public void takeDamage(int amount) { + // 25% damage reduction + int reducedDamage = (int) (amount * 0.75); + super.takeDamage(reducedDamage); + System.out.println("Dragon's scales reduce damage to " + reducedDamage + "!"); + } + + // --- Inherited Method Implementations --- + + @Override + public String getName() { + return "Dragon"; + } + + @Override + public void gainExperience(int amount) { + // Dragons don't gain experience + } + + @Override + public void healMana(int amount) { + // Dragons don't use mana + } + + @Override + public int getMana() { + return 0; // Dragons have no mana pool + } + + @Override + public int getMaxMana() { + return 0; // Dragons have no mana pool + } + + /** + * Provides formatted description with current status + * @return String describing dragon and its health + */ + @Override + public String getDescription() { + return String.format( + "Dragon with fiery breath (Reduces damage by 25% | Health: %d/%d)", + health, maxHealth + ); + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/entity/enemies/Enemy.java b/Java-Ring/src/main/java/org/project/entity/enemies/Enemy.java index 1fcc491..1c92d96 100644 --- a/Java-Ring/src/main/java/org/project/entity/enemies/Enemy.java +++ b/Java-Ring/src/main/java/org/project/entity/enemies/Enemy.java @@ -1,35 +1,129 @@ package org.project.entity.enemies; +import org.project.entity.Entity; import org.project.object.weapons.Weapon; -// TODO: UPDATE IMPLEMENTATION -public abstract class Enemy { - Weapon weapon; - private int hp; - private int mp; +/** + * Abstract base class for all enemy entities in the game. + * Provides common functionality and attributes shared by all enemies. + */ +public abstract class Enemy implements Entity { - public Enemy(int hp, int mp, Weapon weapon) { - this.hp = hp; - this.mp = mp; + // Core enemy attributes + protected final String name; // Enemy's display name + protected int health; // Current health points + protected int maxHealth; // Maximum health capacity + protected Weapon weapon; // Equipped weapon + protected int expReward; // Experience granted when defeated + /** + * Constructs a new Enemy instance. + * + * @param name The name of the enemy + * @param health Initial/maximum health points + * @param weapon The weapon this enemy uses + * @param expReward Experience points awarded when defeated + */ + public Enemy(String name, int health, Weapon weapon, int expReward) { + this.name = name; + this.maxHealth = health; + this.health = health; // Start at full health this.weapon = weapon; + this.expReward = expReward; } - // TODO: (BONUS) UPDATE THE FORMULA OF TAKING DAMAGE + /** + * Performs an attack on the target entity using the enemy's weapon. + * + * @param target The entity to attack + */ @Override - public void takeDamage(int damage) { - hp -= damage; + public void attack(Entity target) { + System.out.println(name + " attacks!"); + weapon.use(target); // Delegate attack to equipped weapon } - public int getHp() { - return hp; + /** + * Reduces the enemy's health by the specified amount. + * + * @param amount The amount of damage to take + */ + @Override + public void takeDamage(int amount) { + health -= amount; + System.out.printf("%s took %d damage (%d/%d HP)%n", + name, amount, health, maxHealth); + } + + /** + * Restores health points to the enemy. + * + * @param amount The amount of health to restore + */ + @Override + public void heal(int amount) { + health = Math.min(health + amount, maxHealth); // Prevent overhealing + System.out.println(name + " recovered " + amount + " HP!"); + } + + /** + * Abstract method for enemy descriptions. + * Must be implemented by concrete enemy classes. + * + * @return A formatted description of the enemy + */ + @Override + public abstract String getDescription(); + + // ========== GETTER METHODS ========== // + + /** + * @return The enemy's name + */ + @Override + public String getName() { + return name; } - public int getMp() { - return mp; + /** + * @return Current health points + */ + @Override + public int getHealth() { + return health; + } + + /** + * @return Maximum health capacity + */ + @Override + public int getMaxHealth() { + return maxHealth; + } + + /** + * @return Experience points awarded when defeated + */ + public int getExpReward() { + return expReward; } - public Weapon getWeapon() { - return weapon; + /** + * Checks if the enemy is still alive. + * + * @return true if health > 0, false otherwise + */ + @Override + public boolean isAlive() { + return health > 0; + } + + /** + * Default defend action for enemies. + * Can be overridden by specific enemy types. + */ + @Override + public void defend() { + System.out.println(name + " braces for impact!"); } -} +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/entity/enemies/Goblin.java b/Java-Ring/src/main/java/org/project/entity/enemies/Goblin.java new file mode 100644 index 0000000..ae907b9 --- /dev/null +++ b/Java-Ring/src/main/java/org/project/entity/enemies/Goblin.java @@ -0,0 +1,81 @@ +package org.project.entity.enemies; + +import org.project.entity.Entity; +import org.project.object.weapons.Dagger; + +public class Goblin extends Enemy { + private static final double DODGE_CHANCE = 0.25; + private static final int STEAL_AMOUNT = 5; + private boolean hasStolen = false; + + public Goblin(int health, int expReward, Dagger dagger) { + super("Goblin", health, new Dagger(), expReward); + } + + @Override + public void gainExperience(int amount) { + // Enemies don't gain experience + } + + @Override + public boolean isDefending() { + return super.isDefending(); + } + + @Override + public void healMana(int amount) { + + } + + @Override + public int getMana() { + return super.getMana(); + } + + @Override + public void takeDamage(int amount) { + if (Math.random() < DODGE_CHANCE) { + System.out.println("Goblin nimbly dodges the attack!"); + return; + } + super.takeDamage(amount); + } + + @Override + public String getName() { + return "Goblin"; + } + + + + @Override + public void useSpecialAbility(Entity target) { + if (!hasStolen) { + // Goblin's dirty trick: steals HP on first special use + int damage = STEAL_AMOUNT + (int) (Math.random() * 5); + target.takeDamage(damage); + this.heal(damage); + System.out.printf("Goblin stabs %s and steals %d HP!%n", + target.getName(), damage); + hasStolen = true; + } else { + // Regular attack if already used special + System.out.println("Goblin attempts another dirty trick but fails!"); + super.attack(target); + } + } + + @Override + public void attack(Entity target) { + // 10% chance to use special ability randomly + if (Math.random() < 0.1) { + useSpecialAbility(target); + } else { + super.attack(target); + } + } + @Override + public String getDescription() { + return "A sneaky goblin that fights dirty (Dodge chance: 25%)"; + } +} diff --git a/Java-Ring/src/main/java/org/project/entity/enemies/Skeleton.java b/Java-Ring/src/main/java/org/project/entity/enemies/Skeleton.java index 8c3e638..58da3d0 100644 --- a/Java-Ring/src/main/java/org/project/entity/enemies/Skeleton.java +++ b/Java-Ring/src/main/java/org/project/entity/enemies/Skeleton.java @@ -1,6 +1,64 @@ package org.project.entity.enemies; -// TODO: UPDATE IMPLEMENTATION -public class Skeleton { - // TODO: DESIGN ENEMY'S WEAPON AND ARMOR AND IMPLEMENT THE CONSTRUCTOR -} +import org.project.entity.Entity; +import org.project.object.weapons.RustySword; + +public class Skeleton extends Enemy { + private boolean hasResurrected; + + public Skeleton(int health, int expReward, RustySword rustySword) { + super("Skeleton", health, new RustySword(), expReward); + this.hasResurrected = false; + } + + @Override + public void takeDamage(int amount) { + super.takeDamage(amount); + if (!isAlive() && !hasResurrected) { + resurrect(); + } + } + + @Override + public String getDescription() { + return "A reanimated skeleton with a rusty sword. (Resurrects once)"; + } + + @Override + public String getName() { + return "Skeleton"; + } + + @Override + public void useSpecialAbility(Entity target) { + System.out.println("Sneaky Skeleton doesn't have a super power" + + " but this bastard got two lives ;) "); + } + + @Override + public void gainExperience(int amount) { +// Enemies don't gain experience + } + + @Override + public boolean isDefending() { + return super.isDefending(); + } + + @Override + public void healMana(int amount) { + + } + + @Override + public int getMana() { + return super.getMana(); + } + + private void resurrect() { + health = maxHealth / 2; + hasResurrected = true; + System.out.println("The skeleton reassembles itself!"); + } + +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/entity/players/Knight.java b/Java-Ring/src/main/java/org/project/entity/players/Knight.java index 14d8fa2..e420de0 100644 --- a/Java-Ring/src/main/java/org/project/entity/players/Knight.java +++ b/Java-Ring/src/main/java/org/project/entity/players/Knight.java @@ -1,6 +1,101 @@ 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.object.armors.KnightArmor; +import org.project.object.weapons.Sword; + +/** + * The Knight player class - A heavily armored warrior specializing in melee combat + * and defensive abilities. Features a powerful kick special ability with cooldown. + */ +public class Knight extends Player { + + // Combat cooldown tracking + private int kickCooldown; // Turns remaining until kick can be used again + private static final int KICK_DAMAGE = 35; // Base damage for the kick ability + + /** + * Constructs a new Knight character with default equipment + * @param name The knight's display name + */ + public Knight(String name) { + super(name, 200, 30, new Sword(), new KnightArmor()); // High health, medium mana + this.kickCooldown = 0; // Ability starts ready + } + + /** + * Checks if the knight's kick ability is available + * @return true if kick can be used, false if on cooldown + */ + public boolean hasKickReady() { + return kickCooldown <= 0; + } + + /** + * Knight's special ability - Mighty Kick + * Deals heavy damage with scaling based on level + * @param target The entity to attack + */ + @Override + public void useSpecialAbility(Entity target) { + if (hasKickReady()) { + // Calculate damage: base + (2 * level) + int totalDamage = KICK_DAMAGE + (level * 2); + System.out.printf("%s performs a mighty kick (%d damage)!%n", + name, totalDamage); + target.takeDamage(totalDamage); + kickCooldown = 3; // 3-turn cooldown + } else { + System.out.printf("Kick on cooldown (%d turns remaining)%n", + kickCooldown); + } + } + + /** + * Reduces all active cooldowns by 1 turn + * Called automatically after each attack + */ + public void reduceCooldowns() { + if (kickCooldown > 0) kickCooldown--; + } + + /** + * Performs a standard attack and reduces cooldowns + * @param target The entity to attack + */ + @Override + public void attack(Entity target) { + super.attack(target); // Perform basic weapon attack + reduceCooldowns(); // Progress ability cooldowns + } + + /** + * Generates a character status description + * @return Formatted string with health and ability status + */ + @Override + public String getDescription() { + return String.format("%s - Lvl %d Knight (%d/%d HP) | %s", + name, level, health, maxHealth, + hasKickReady() ? "Kick Ready" : "Kick CD: " + kickCooldown); + } + + // ====== Inherited Method Implementations ====== // + + /** + * @return The knight's name + */ + @Override + public String getName() { + return this.name; + } + + /** + * Placeholder for item usage functionality + * @param target The target entity (unused) + */ + @Override + public void use(Entity target) { + // TODO: Implement knight-specific item interactions + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/entity/players/Player.java b/Java-Ring/src/main/java/org/project/entity/players/Player.java index 674905a..dac912a 100644 --- a/Java-Ring/src/main/java/org/project/entity/players/Player.java +++ b/Java-Ring/src/main/java/org/project/entity/players/Player.java @@ -1,81 +1,200 @@ package org.project.entity.players; +import java.util.ArrayList; +import java.util.List; import org.project.entity.Entity; import org.project.object.armors.Armor; +import org.project.object.consumables.Consumable; +import org.project.object.consumables.Flask; import org.project.object.weapons.Weapon; -// TODO: UPDATE IMPLEMENTATION -public abstract class Player { - protected String name; - Weapon weapon; - Armor armor; - private int hp; - private int maxHP; - private int mp; - private int maxMP; - - public Player(String name, int hp, int mp, Weapon weapon, Armor armor) { +/** + * Abstract base class representing a player character in the game. + * Contains core player systems including combat, inventory, and progression. + */ +public abstract class Player implements Entity { + + // Character Attributes + protected final String name; // Player's name + protected int health; // Current health points + protected int maxHealth; // Maximum health capacity + protected static int mana; // Current mana points + protected static int maxMana; // Maximum mana capacity + protected Weapon weapon; // Equipped weapon + protected Armor armor; // Equipped armor + protected int level; // Current character level + protected int experience; // Accumulated experience + protected static List inventory; // Item inventory + protected boolean defending; // Defense state flag + + /** + * Constructs a new Player character with starting equipment. + * + * @param name Character name + * @param maxHealth Starting maximum health + * @param maxMana Starting maximum mana + * @param weapon Starting weapon + * @param armor Starting armor + */ + public Player(String name, int maxHealth, int maxMana, Weapon weapon, Armor armor) { this.name = name; - this.hp = hp; - this.mp = mp; - + this.maxHealth = maxHealth; + this.health = maxHealth; // Start at full health + this.maxMana = maxMana; + this.mana = maxMana; // Start at full mana this.weapon = weapon; this.armor = armor; + this.level = 1; // Start at level 1 + this.experience = 0; // Start with 0 XP + this.inventory = new ArrayList<>(); + this.inventory.add(new Flask()); // Give starting health potion + this.defending = false; // Not defending initially } + // ========== COMBAT METHODS ========== // + + /** + * Abstract method for class-specific special abilities. + * Must be implemented by concrete player classes. + * + * @param target The entity to use the ability on + */ + public abstract void use(Entity target); + + /** + * Performs a basic attack using the equipped weapon. + * + * @param target The entity to attack + */ @Override public void attack(Entity target) { - target.takeDamage(weapon.getDamage()); + weapon.use(target); // Delegate attack to weapon } + /** + * Enters a defensive stance, reducing incoming damage. + */ @Override public void defend() { - // TODO: (BONUS) IMPLEMENT A DEFENSE METHOD FOR SHIELDS + defending = true; + System.out.println(name + " takes defensive stance!"); } - // TODO: (BONUS) UPDATE THE FORMULA OF TAKING DAMAGE + /** + * Takes damage after armor mitigation. + * + * @param amount Raw damage amount before reduction + */ @Override - public void takeDamage(int damage) { - hp -= damage - armor.getDefense(); + public void takeDamage(int amount) { + int reducedDamage = armor.protect(amount); // Apply armor reduction + health -= reducedDamage; + defending = false; // Defense ends after being hit + + System.out.printf("%s took %d damage (reduced from %d)%n", + name, reducedDamage, amount); + + if (health <= 0) { + System.out.println(name + " has been defeated!"); + } } + // ========== HEALTH/MANA MANAGEMENT ========== // + + /** + * Restores health points. + * + * @param amount Amount to heal (won't exceed maxHealth) + */ @Override - public void heal(int health) { - hp += health; - if (hp > maxHP) { - hp = maxHP; - } + public void heal(int amount) { + health = Math.min(health + amount, maxHealth); + System.out.println(name + " recovered " + amount + " HP!"); } + /** + * Restores mana points. + * + * @param amount Amount to restore (won't exceed maxMana) + */ @Override - public void fillMana(int mana) { - mp += mana; - if (mp > maxMP) { - mp = maxMP; + public void healMana(int amount) { + mana = Math.min(maxMana, mana + amount); + System.out.println(name + " recovered " + amount + " MP!"); + } + + // ========== PROGRESSION SYSTEM ========== // + + /** + * Awards experience points and handles level-ups. + * + * @param amount Experience points to add + */ + @Override + public void gainExperience(int amount) { + experience += amount; + System.out.println(name + " gained " + amount + " XP!"); + + // Level up if enough experience (100 XP per level) + if (experience >= level * 100) { + levelUp(); + } + } + + /** + * Increases character level and improves stats. + */ + private void levelUp() { + level++; + maxHealth += 10; // HP increase per level + maxMana += 5; // MP increase per level + health = maxHealth; // Fully heal on level up + mana = maxMana; // Fully restore mana + System.out.printf("%s leveled up to %d!%n", name, level); + } + + // ========== INVENTORY SYSTEM ========== // + + /** + * Uses an item from inventory. + * + * @param index Inventory slot number (0-based) + */ + public void useItem(int index) { + if (index >= 0 && index < inventory.size()) { + Consumable item = inventory.get(index); + item.use(this); // Apply item effect + + // Remove if depleted + if (item.getRemainingUses() <= 0) { + inventory.remove(index); + } } } + // ========== GETTER METHODS ========== // + @Override public String getName() { return name; } - public int getHp() { - return hp; + @Override + public int getHealth() { + return health; } @Override - public int getMaxHP() { - return maxHP; + public int getMaxHealth() { + return maxHealth; } - public int getMp() { - return mp; + public int getMana() { + return mana; } - @Override - public int getMaxMP() { - return maxMP; + public int getMaxMana() { + return maxMana; } public Weapon getWeapon() { @@ -86,4 +205,25 @@ public Armor getArmor() { return armor; } -} + public int getLevel() { + return level; + } + + public int getExperience() { + return experience; + } + + public List getInventory() { + return inventory; + } + + @Override + public boolean isAlive() { + return health > 0; + } + + @Override + public boolean isDefending() { + return defending; + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/entity/players/Wizard.java b/Java-Ring/src/main/java/org/project/entity/players/Wizard.java new file mode 100644 index 0000000..5d2bb70 --- /dev/null +++ b/Java-Ring/src/main/java/org/project/entity/players/Wizard.java @@ -0,0 +1,151 @@ +package org.project.entity.players; + +import org.project.entity.Entity; +import org.project.object.armors.Armor; +import org.project.object.consumables.ManaPotion; +import org.project.object.weapons.Staff; + +public class Wizard extends Player { + private int manaRegenRate; + private static final int BASE_MANA_REGEN = 5; + private static final int FIREBALL_COST = 20; + private static final int FIREBALL_DAMAGE = 40; + + public Wizard(String name) { + super(name, 120, 100, new Staff(), new RobeArmor()); + this.manaRegenRate = BASE_MANA_REGEN; + this.inventory.add(new ManaPotion()); // Starting mana potion + } + + @Override + public void use(Entity target) { + if (target == null) { + System.out.println(name + " waves their staff but nothing happens!"); + return; + } + + // Check if using a mana potion when mana is low + if (mana < maxMana * 0.3 && hasManaPotion()) { + useManaPotion(); + return; + } + + // Default spell casting behavior + if (mana >= FIREBALL_COST) { + castFireball(target); + } else if (mana >= 10) { + castMagicMissile(target); + } else { + System.out.println(name + " is too exhausted to cast spells!"); + basicStaffAttack(target); + } + } + + @Override + public void useSpecialAbility(Entity target) { + if (mana >= FIREBALL_COST) { + castFireball(target); + } else { + System.out.printf("%s doesn't have enough mana! (Need %d, has %d)%n", + name, FIREBALL_COST, mana); + } + } + + @Override + public void attack(Entity target) { + if (Math.random() < 0.3 && mana >= 10) { // 30% chance to use magic missile + castMagicMissile(target); + } else { + basicStaffAttack(target); + } + regenerateMana(); + } + + private void castFireball(Entity target) { + System.out.printf("%s hurls a massive Fireball at %s! (%d damage)%n", + name, target.getName(), FIREBALL_DAMAGE); + target.takeDamage(FIREBALL_DAMAGE); + mana -= FIREBALL_COST; + + // 20% chance to apply burn effect + if (Math.random() < 0.2) { + applyBurnEffect(target, FIREBALL_DAMAGE / 4); + } + } + + private void castMagicMissile(Entity target) { + int damage = 15 + (int)(Math.random() * 10); + System.out.printf("%s shoots Magic Missiles at %s! (%d damage)%n", + name, target.getName(), damage); + target.takeDamage(damage); + mana -= 10; + } + + private void basicStaffAttack(Entity target) { + System.out.printf("%s bonks %s with their staff!%n", name, target.getName()); + super.attack(target); // Uses the default weapon attack + } + + private void regenerateMana() { + mana = Math.min(maxMana, mana + manaRegenRate); + } + + private boolean hasManaPotion() { + return inventory.stream() + .anyMatch(item -> item instanceof ManaPotion && item.getRemainingUses() > 0); + } + + private void useManaPotion() { + inventory.stream() + .filter(item -> item instanceof ManaPotion) + .findFirst() + .ifPresent(potion -> { + System.out.println(name + " quickly drinks a mana potion!"); + potion.use(this); + }); + } + + private void applyBurnEffect(Entity target, int burnDamage) { + System.out.printf("%s catches fire! (%d burn damage over 3 turns)%n", + target.getName(), burnDamage * 3); + + // Simple turn-based burn effect + new Thread(() -> { + try { + for (int i = 0; i < 3 && target.isAlive(); i++) { + Thread.sleep(1500); // Simulate turn delay + target.takeDamage(burnDamage); + System.out.println(target.getName() + " takes " + burnDamage + " burn damage!"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }).start(); + } + + @Override + public String getDescription() { + return String.format("%s - Lvl %d Wizard (%d/%d HP, %d/%d MP)", + name, level, health, maxHealth, mana, maxMana); + } + + // Wizard-specific armor + private static class RobeArmor extends Armor { + public RobeArmor() { + super("Wizard's Robe", 10, 80); + } + + @Override + public int protect(int incomingDamage) { + // Robes reduce magic damage more than physical + int reducedDamage = (int)(incomingDamage * 0.8); // 20% reduction + durability -= 1; // Robes degrade slower + return reducedDamage; + } + + @Override + public void use(Entity target) { + + } + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/location/Location.java b/Java-Ring/src/main/java/org/project/location/Location.java index dfa9cb8..45f4586 100644 --- a/Java-Ring/src/main/java/org/project/location/Location.java +++ b/Java-Ring/src/main/java/org/project/location/Location.java @@ -1,32 +1,126 @@ package org.project.location; -import org.project.entity.enemies.Enemy; - -import java.util.ArrayList; +import java.util.Random; +import org.project.entity.enemies.*; +import org.project.object.weapons.*; +/** + * Represents a game location with biome-specific properties and enemy spawning logic. + * Handles danger level progression and location-based enemy generation. + */ public class Location { - private String name; - private ArrayList enemies; + /** + * Biome types that affect enemy spawn rates and location properties + */ + public enum Biome { + FOREST, // Dense woodland areas + CAVE, // Underground caverns + MOUNTAIN, // High altitude terrain + RUINS // Ancient structures + } + + // Location properties + private String name; // Location display name + private String description; // Flavor text description + private int dangerLevel; // Difficulty scaling factor (1+) + private Random random = new Random(); // Random number generator + private Biome biome; // Biome type + + /** + * Constructs a new game location + * @param name The location's name + * @param description Descriptive text about the location + * @param biome The biome type (FOREST, CAVE, etc.) + */ + public Location(String name, String description, Biome biome) { + this.name = name; + this.description = description; + this.dangerLevel = 1; // Start at minimum danger + this.biome = biome; + } + + /** + * Generates an enemy scaled to current danger level + * @return A new Enemy instance (Goblin/Skeleton/Dragon) + * @throws IllegalStateException if invalid enemy type generated + */ + public Enemy generateEnemy() { + try { + // Scale stats based on danger level + int scaledHealth = 50 + (dangerLevel * 20); + int scaledExp = 30 + (dangerLevel * 10); + + // Randomly select enemy type (0-2) + int enemyType = random.nextInt(3); - public Location(ArrayList locations, ArrayList enemies) { - this.locations = locations; - this.enemies = enemies; + System.out.println("Generating enemy type: " + enemyType); // Debug output + + return switch (enemyType) { + case 0 -> new Goblin(scaledHealth, scaledExp, new Dagger()); + case 1 -> new Skeleton(scaledHealth, scaledExp, new RustySword()); + case 2 -> new Dragon(scaledHealth, scaledExp, new Claw()); + default -> throw new IllegalStateException("Invalid enemy type"); + }; + } catch (Exception e) { + System.err.println("Failed to generate enemy: " + e.getMessage()); + return null; // Fallback for error cases + } + } + + /** + * Gets biome-specific enemy spawn weights + * @return Array of weights [Goblin%, Skeleton%, Dragon%] + */ + private double[] getBiomeWeights() { + return switch (biome) { + case FOREST -> new double[]{70, 25, 5}; // Mostly Goblins + case CAVE -> new double[]{30, 65, 5}; // Skeleton-heavy + case MOUNTAIN -> new double[]{10, 30, 60}; // Dragon territory + case RUINS -> new double[]{40, 55, 5}; // Undead-focused + default -> new double[]{60, 35, 5}; // Default mix + }; } - /* - TODO: (BONUS) RESET EACH LOCATION AFTER PLAYER LEAVES - */ + /** + * Increases location danger level + * Makes future enemies stronger and increases rewards + */ + public void increaseDanger() { + dangerLevel++; + System.out.println(name + " grows more dangerous..."); + } + // ========== GETTER METHODS ========== // + + /** + * @return Location name + */ public String getName() { return name; } - public ArrayList getLocations() { - return locations; + /** + * @return Location description text + */ + public String getDescription() { + return description; + } + + /** + * @return Current danger level (1+) + */ + public int getDangerLevel() { + return dangerLevel; } - public ArrayList getEnemies() { - return enemies; + /** + * Gets descriptive danger rating + * @return "Safe", "Risky", or "Deadly" based on danger level + */ + public String getDangerDescription() { + if (dangerLevel <= 2) return "Safe"; + if (dangerLevel <= 5) return "Risky"; + return "Deadly"; } -} +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/Object.java b/Java-Ring/src/main/java/org/project/object/Object.java index 922cbd5..83c9460 100644 --- a/Java-Ring/src/main/java/org/project/object/Object.java +++ b/Java-Ring/src/main/java/org/project/object/Object.java @@ -3,9 +3,18 @@ import org.project.entity.Entity; public interface Object { + // Core method all objects must implement void use(Entity target); - - /* - TODO: ADD OTHER REQUIRED AND BONUS METHODS - */ -} + + // Identification methods + String getName(); + String getDescription(); + + // Inventory management helpers + boolean isConsumable(); + int getValue(); // Gold value + + // Equipment status methods (default implementations) + default boolean isBroken() { return false; } + default void repair() {} +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/armors/Armor.java b/Java-Ring/src/main/java/org/project/object/armors/Armor.java index 346c25e..623186d 100644 --- a/Java-Ring/src/main/java/org/project/object/armors/Armor.java +++ b/Java-Ring/src/main/java/org/project/object/armors/Armor.java @@ -1,42 +1,67 @@ package org.project.object.armors; -// TODO: UPDATE IMPLEMENTATION -public abstract class Armor { - private int defense; - private int maxDefense; - private int durability; - private int maxDurability; +import org.project.object.Object; + - private boolean isBroke; +public abstract class Armor implements Object { - public Armor(int defense, int durability) { + protected String name; + private int defense; + protected int durability; + final int maxDurability; + + public Armor(String name, int defense, int durability) { + this.name = name; this.defense = defense; - this.durability = durability; + this.durability = this.maxDurability = durability; } - public void checkBreak() { - if (durability <= 0) { - isBroke = true; - defense = 0; + public int protect(int incomingDamage) { + int damageReduction = Math.min(defense, incomingDamage / 2); + int damageTaken = Math.max(1, incomingDamage - damageReduction); + + durability -= incomingDamage / 10; + if (durability < 0) { + durability = 0; } + + return damageTaken; } - // TODO: (BONUS) UPDATE THE REPAIR METHOD + + @Override public void repair() { - isBroke = false; - defense = maxDefense; durability = maxDurability; + System.out.println(name + " has been repaired!"); } - public int getDefense() { - return defense; + @Override + public boolean isBroken() { + return durability <= 0; } - public int getDurability() { - return durability; + // Standard getters + @Override + public String getName() { + return name; } - public boolean isBroke() { - return isBroke; + @Override + public String getDescription() { + return String.format("%s (%d DEF)", name, defense); + } + + @Override + public boolean isConsumable() { + return false; + } + + @Override + public int getValue() { + return defense * 12; + } + + public int getDurability() { + return durability; } } diff --git a/Java-Ring/src/main/java/org/project/object/armors/KnightArmor.java b/Java-Ring/src/main/java/org/project/object/armors/KnightArmor.java index 0dedcc2..c624c26 100644 --- a/Java-Ring/src/main/java/org/project/object/armors/KnightArmor.java +++ b/Java-Ring/src/main/java/org/project/object/armors/KnightArmor.java @@ -1,6 +1,58 @@ package org.project.object.armors; -// TODO: UPDATE IMPLEMENTATION -public class KnightArmor { - // TODO: DESIGN ARMOR'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR +import org.project.entity.Entity; +import org.project.object.Object; + +public class KnightArmor extends Armor implements Object { + private static final int BASE_DEFENSE = 25; + private static final int BASE_DURABILITY = 150; + private static final String ARMOR_NAME = "Knight's Plate Armor"; + + public KnightArmor() { + super(ARMOR_NAME, BASE_DEFENSE, BASE_DURABILITY); + } + + @Override + public void use(Entity target) { + // Armor can't be "used" like consumables, but we implement the required method + System.out.println(ARMOR_NAME + " cannot be used directly"); + } + + @Override + public int protect(int incomingDamage) { + // Knight armor has 15% chance to completely block physical attacks + if (Math.random() < 0.15 && getDurability() > 0) { + System.out.println(ARMOR_NAME + " deflects the attack completely!"); + reduceDurability(); // Blocking still stresses the armor + return 0; + } + return super.protect(incomingDamage); + } + + private void reduceDurability() { + if (durability > 0) { + durability -= 2; + } + } + + @Override + public void repair() { + super.repair(); + System.out.println(ARMOR_NAME + " shines like new after repair!"); + } + + @Override + public String getDescription() { + return String.format("%s (Def: %d, Dur: %d/%d) - Heavy plate armor with chance to block attacks", + name, getBaseDefense(), getDurability(), maxDurability); + } + + public int getBaseDefense() { + return BASE_DEFENSE; + } + + @Override + public boolean isConsumable() { + return false; + } } \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/consumables/Consumable.java b/Java-Ring/src/main/java/org/project/object/consumables/Consumable.java index fd8962c..bf9863e 100644 --- a/Java-Ring/src/main/java/org/project/object/consumables/Consumable.java +++ b/Java-Ring/src/main/java/org/project/object/consumables/Consumable.java @@ -1,8 +1,49 @@ package org.project.object.consumables; -// TODO: UPDATE IMPLEMENTATION -public abstract class Consumable { - /* - TODO: ADD OTHER REQUIRED AND BONUS METHODS - */ +import org.project.object.Object; + +public abstract class Consumable implements Object { + + protected String name; + protected String description; + protected int value; + protected int remainingUses; + + public Consumable(String name, String desc, int value, int uses) { + this.name = name; + this.description = desc; + this.value = value; + this.remainingUses = uses; + } + + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public boolean isConsumable() { + return true; + } + + @Override + public int getValue() { + return value; + } + + public int getRemainingUses() { + return remainingUses; + } + + protected void consumeUse() { + if (remainingUses > 0) { + remainingUses--; + } + } } diff --git a/Java-Ring/src/main/java/org/project/object/consumables/Flask.java b/Java-Ring/src/main/java/org/project/object/consumables/Flask.java index d7e515d..bb0bb49 100644 --- a/Java-Ring/src/main/java/org/project/object/consumables/Flask.java +++ b/Java-Ring/src/main/java/org/project/object/consumables/Flask.java @@ -2,15 +2,58 @@ import org.project.entity.Entity; -// TODO: UPDATE IMPLEMENTATION -public class Flask { - /* - THIS IS AN EXAMPLE OF A CONSUMABLE DESIGN. - */ +public class Flask extends Consumable { + private static final int HEAL_POWER = 30; + private static final String NAME = "Healing Flask"; + private static final String DESCRIPTION = "Restores 30 HP"; + private static final int VALUE = 50; + private static final int MAX_USES = 3; + + public Flask() { + super(NAME, DESCRIPTION, VALUE, MAX_USES); + } - // TODO: (BONUS) UPDATE USE METHOD @Override public void use(Entity target) { - target.heal(target.getMaxHP() / 10); + if (getRemainingUses() > 0) { + target.heal(HEAL_POWER); + consumeUse(); + System.out.printf("%s healed for %d HP! (%d uses remain)%n", + target.getName(), HEAL_POWER, getRemainingUses()); + + // Bonus: Flask gets more effective at low health + if (target.getHealth() < target.getMaxHealth() * 0.3) { + int bonusHeal = HEAL_POWER / 2; + target.heal(bonusHeal); + System.out.printf("Emergency boost! Additional %d HP restored!%n", bonusHeal); + } + } else { + System.out.println("Flask is empty!"); + } + } + + @Override + public boolean isConsumable() { + return true; + } + + // Bonus: Refill method + public void refill() { + if (getRemainingUses() < MAX_USES) { + System.out.println("Flask has been refilled!"); + resetUses(); + } else { + System.out.println("Flask is already full!"); + } + } + + // Method to reset the uses of the flask + private void resetUses() { + setRemainingUses(MAX_USES); + } + + // Setter for remaining uses + private void setRemainingUses(int uses) { + this.remainingUses = uses; } -} +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/consumables/ManaPotion.java b/Java-Ring/src/main/java/org/project/object/consumables/ManaPotion.java new file mode 100644 index 0000000..d1955fc --- /dev/null +++ b/Java-Ring/src/main/java/org/project/object/consumables/ManaPotion.java @@ -0,0 +1,21 @@ +package org.project.object.consumables; + +import org.project.entity.Entity; + +public class ManaPotion extends Consumable { + private static final int MANA_RESTORE = 40; + + public ManaPotion() { + super("Mana Potion", "Restores 40 MP", 75, 2); + } + + @Override + public void use(Entity target) { + if (getRemainingUses() > 0) { + target.healMana(MANA_RESTORE); + consumeUse(); + System.out.printf("%s restored %d MP! (%d uses remain)%n", + target.getName(), MANA_RESTORE, getRemainingUses()); + } + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/weapons/Claw.java b/Java-Ring/src/main/java/org/project/object/weapons/Claw.java new file mode 100644 index 0000000..2c1fa6d --- /dev/null +++ b/Java-Ring/src/main/java/org/project/object/weapons/Claw.java @@ -0,0 +1,24 @@ +package org.project.object.weapons; + +import java.util.List; + +import org.project.entity.Entity; + +public class Claw extends Weapon { + public Claw() { + super("Dragon Claws", 25, 0, Integer.MAX_VALUE); + } + + @Override + public int getDamage() { + return 25 + (int)(Math.random() * 5); // 25-30 damage + } + + @Override + public void specialAbility(List targets) { + System.out.println("Dragon performs a rending slash!"); + for (Entity target : targets) { + target.takeDamage(getDamage() * 3 / 4); // 75% damage to all + } + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/weapons/Dagger.java b/Java-Ring/src/main/java/org/project/object/weapons/Dagger.java new file mode 100644 index 0000000..2ec6fa2 --- /dev/null +++ b/Java-Ring/src/main/java/org/project/object/weapons/Dagger.java @@ -0,0 +1,42 @@ +package org.project.object.weapons; + +import org.project.entity.Entity; +import java.util.List; + +public class Dagger extends Weapon { + private static final int BASE_DAMAGE = 15; + private static final int CRIT_CHANCE = 25; // 25% crit chance + private static final int CRIT_MULTIPLIER = 2; + + public Dagger() { + super("Rusty Dagger", BASE_DAMAGE, 5, 50); // name, damage, manaCost, durability + } + + @Override + public int getDamage() { + // 25% chance to crit for double damage + return (Math.random() * 100 < CRIT_CHANCE) ? + BASE_DAMAGE * CRIT_MULTIPLIER : + BASE_DAMAGE; + } + + @Override + public void specialAbility(List targets) { + if (targets.isEmpty()) return; + + Entity primaryTarget = targets.get(0); + int damage = getDamage() + 5; // Backstab bonus + + System.out.printf("Goblin backstabs %s for %d damage!%n", + primaryTarget.getName(), damage); + primaryTarget.takeDamage(damage); + } + + @Override + public String getDescription() { + return String.format( + "%s (Damage: %d, Crit: %d%%, Durability: %d/%d)", + name, BASE_DAMAGE, CRIT_CHANCE, durability, maxDurability + ); + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/weapons/RustySword.java b/Java-Ring/src/main/java/org/project/object/weapons/RustySword.java new file mode 100644 index 0000000..74f4b60 --- /dev/null +++ b/Java-Ring/src/main/java/org/project/object/weapons/RustySword.java @@ -0,0 +1,49 @@ +package org.project.object.weapons; + +import org.project.entity.Entity; +import java.util.List; + +public class RustySword extends Weapon { + private static final int BASE_DAMAGE = 18; + private static final double RUST_CHANCE = 0.3; // 30% chance to apply rust effect + private static final int RUST_DAMAGE_PENALTY = 2; // Reduces target's damage + + public RustySword() { + super("Rusty Sword", BASE_DAMAGE, 8, 40); // name, damage, manaCost, durability + } + + @Override + public int getDamage() { + // 5% chance to break on hit + if (Math.random() < 0.05) { + durability -= 5; + } + return BASE_DAMAGE; + } + + @Override + public void specialAbility(List targets) { + if (targets.isEmpty()) return; + + Entity target = targets.get(0); + if (Math.random() < RUST_CHANCE) { + System.out.printf("%s's rust infects %s, reducing their damage output!%n", + name, target.getName()); + // Implementation would reduce target's damage by RUST_DAMAGE_PENALTY + } + } + + @Override + public String getDescription() { + return String.format( + "%s (Damage: %d, Rust: %.0f%%, Durability: %d/%d)", + name, BASE_DAMAGE, RUST_CHANCE * 100, durability, maxDurability + ); + } + + // Unique rust effect application + public void applyRust(Entity target) { + // Implementation would track damage reduction on target + System.out.println("Rust spreads, weakening " + target.getName()); + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/weapons/Staff.java b/Java-Ring/src/main/java/org/project/object/weapons/Staff.java new file mode 100644 index 0000000..9dbe987 --- /dev/null +++ b/Java-Ring/src/main/java/org/project/object/weapons/Staff.java @@ -0,0 +1,21 @@ +package org.project.object.weapons; + +import org.project.entity.Entity; + +import java.util.List; + +public class Staff extends Weapon { + public Staff() { + super("Wizard's Staff", 12, 0, 60); // Low physical damage, no mana cost + } + + @Override + public void specialAbility(List targets) { + + } + + @Override + public String getDescription() { + return name + " (Damage: " + baseDamage + ", +5 mana regen when equipped)"; + } +} \ No newline at end of file diff --git a/Java-Ring/src/main/java/org/project/object/weapons/Sword.java b/Java-Ring/src/main/java/org/project/object/weapons/Sword.java index a0e3cc3..6abd535 100644 --- a/Java-Ring/src/main/java/org/project/object/weapons/Sword.java +++ b/Java-Ring/src/main/java/org/project/object/weapons/Sword.java @@ -1,26 +1,42 @@ package org.project.object.weapons; +import java.util.List; + import org.project.entity.Entity; -import java.util.ArrayList; -// TODO: UPDATE IMPLEMENTATION -public class Sword { - /* - THIS IS AN EXAMPLE OF A WEAPON DESIGN. - */ +public class Sword extends Weapon { int abilityCharge; public Sword() { - // TODO: DESIGN SWORD'S ATTRIBUTES IMPLEMENT THE CONSTRUCTOR + super("Stell Sword" , 25 , 15, 100); + this.abilityCharge = 0; } - // TODO: (BONUS) UPDATE THE UNIQUE ABILITY - public void uniqueAbility(ArrayList targets) { - abilityCharge += 2; - for (Entity target : targets) { - target.takeDamage(getDamage()); + + + @Override + public void specialAbility(List targets) { + if (abilityCharge >= 3) { + System.out.println("Executing Whirlwind Slash!"); + for (Entity target : targets) { + target.takeDamage(baseDamage * 2); + } + abilityCharge = 0; + } else { + System.out.printf("Need %d more attacks to charge!\n", 3 - abilityCharge); + } + } + @Override + public void use(Entity target) { + super.use(target); + gainCharge(); + } + private void gainCharge() { + if (abilityCharge < 3) { + abilityCharge++; } } + } diff --git a/Java-Ring/src/main/java/org/project/object/weapons/Weapon.java b/Java-Ring/src/main/java/org/project/object/weapons/Weapon.java index 35e1ecc..46e729c 100644 --- a/Java-Ring/src/main/java/org/project/object/weapons/Weapon.java +++ b/Java-Ring/src/main/java/org/project/object/weapons/Weapon.java @@ -1,35 +1,138 @@ package org.project.object.weapons; +import java.util.List; import org.project.entity.Entity; +import org.project.object.Object; -// TODO: UPDATE IMPLEMENTATION -public abstract class Weapon { - private int damage; - private int manaCost; +/** + * Abstract base class for all weapons in the game. + * Implements core combat functionality, durability systems, and special abilities. + */ +public abstract class Weapon implements Object { - /* - TODO: ADD OTHER REQUIRED AND BONUS ATTRIBUTES - */ + // Weapon Attributes + protected String name; // Display name of the weapon + protected int baseDamage; // Base damage value + protected int manaCost; // Mana required for special abilities + protected int durability; // Current durability + protected int maxDurability; // Maximum durability capacity - public Weapon(int damage, int manaCost) { - this.damage = damage; + /** + * Constructs a new weapon instance. + * @param name The weapon's display name + * @param damage Base damage value + * @param manaCost Mana cost for special abilities + * @param durability Starting/Maximum durability + */ + public Weapon(String name, int damage, int manaCost, int durability) { + this.name = name; + this.baseDamage = damage; this.manaCost = manaCost; + this.durability = this.maxDurability = durability; } + /** + * Gets the base damage value before modifiers. + * @return The weapon's base damage + */ + public int getDamage() { + return baseDamage; + } + + /** + * Core combat method - attacks a target entity. + * Decreases durability with each use. + * @param target The entity to attack + */ @Override public void use(Entity target) { - target.takeDamage(damage); + if (durability > 0) { + int damage = calculateDamage(); + target.takeDamage(damage); + durability--; + System.out.printf("%s dealt %d damage! (Durability: %d/%d)\n", + name, damage, durability, maxDurability); + } else { + System.out.println(name + " is broken!"); + } } - public int getDamage() { - return damage; + /** + * Calculates final damage (can be overridden for custom damage formulas). + * @return The calculated damage amount + */ + protected int calculateDamage() { + return baseDamage; // Default implementation uses base damage + } + + /** + * Abstract method for weapon special abilities. + * Must be implemented by concrete weapon classes. + * @param targets List of entities affected by the ability + */ + public abstract void specialAbility(List targets); + + // ========== DURABILITY SYSTEM ========== // + + /** + * Fully repairs the weapon. + */ + @Override + public void repair() { + durability = maxDurability; + System.out.println(name + " has been repaired!"); + } + + /** + * Checks if the weapon is broken. + * @return true if durability <= 0, false otherwise + */ + @Override + public boolean isBroken() { + return durability <= 0; } - public int getManaCost() { - return manaCost; + // ========== GETTER METHODS ========== // + + /** + * @return The weapon's name + */ + @Override + public String getName() { + return name; + } + + /** + * Provides a formatted weapon description. + * @return String containing name and damage value + */ + @Override + public String getDescription() { + return String.format("%s (%d DMG)", name, baseDamage); + } + + /** + * Weapons are not consumable items. + * @return Always false + */ + @Override + public boolean isConsumable() { + return false; + } + + /** + * Calculates the weapon's in-game value. + * @return Base damage * 8 (gold value) + */ + @Override + public int getValue() { + return baseDamage * 8; } - /* - TODO: ADD OTHER REQUIRED AND BONUS METHODS - */ -} + /** + * @return Current durability + */ + public int getDurability() { + return durability; + } +} \ No newline at end of file diff --git a/README.md b/README.md index aef5f82..fece5b3 100644 --- a/README.md +++ b/README.md @@ -131,20 +131,12 @@ Want to **earn extra points**? Try implementing one of these: โœ… **Multiple Weapons System** โš”๏ธ *(Extra Score!)* - Players and monsters can **switch between different weapons** during combat instead of being limited to one. -โœ… **Multiple Players Mode** ๐Ÿ‘ฅ *(Extra Score!)* -- Implement a **party-based battle system** where **multiple players** can fight against **monsters together**. - โœ… **Dragonโ€™s Multi-Target Attack** ๐Ÿ‰ *(Extra Score!)* - If there are **multiple players in the battle**, the **dragonโ€™s unique ability** should **damage all players simultaneously** instead of just one. -โœ… **(Bonus) Inventory System** -- Allow players to **pick up and use items** like different classes of shields and more potions. - -โœ… **(Bonus) Experience & Leveling System** +โœ… Experience & Leveling System** - Players gain XP from battles and level up, increasing their **attack power**. -โœ… **(Bonus) PvP Mode** -- Implement a **Player vs. Player** combat system. By implementing any of these extra features, you can earn additional points to boost your final score! ๐Ÿš€