0
\$\begingroup\$

I'm currently in the process of creating a RPG in Java in order to practice and learn the ins and outs of the language. I have been learning to code for around 1 month through Code HS, and coding within their Sandbox tool. Any general help would be greatly appreciated.

Specifically, I am not satisfied with the Inventory system, and how it deals with items of different sub-classes such as Weapons or Consumables. I am not sure if a HashMap is the best collection to go about achieving this, as I need to store and retrieve the objects themselves and the quantity of them.

This is the main class that handles inputs and outputs as well as the various events a user can choose to do:

//Fishing/hunting for consumables import java.util.Scanner; import java.util.HashMap; public class MyProgram extends ConsoleProgram{ public String ind = "-----"; public String header = "+++++++++++++++++"; Scanner s = new Scanner(System.in); public void run(){ String userName = prompt("What is your name: "); Player user = new Player(userName); Weapon starter1 = new Weapon(2); Weapon starter2 = new Weapon(3); Consumable chicken = new Consumable("Chicken Breast", 20, 1); user.addToInventory(starter1, 1); user.addToInventory(starter2, 1); user.addToInventory(chicken, 3); String userInput = prompt("\nWhat would you like to do (Type Commands to see a list): "); while(!userInput.equalsIgnoreCase("DONE")){ switch(userInput.toUpperCase()){ case "COMMANDS" : System.out.println(header); System.out.println("Commands: Battle, Attributes, Check Inventory, Stats"); break; case "BATTLE": System.out.println(header); battle(user); break; case "ATTRIBUTES": System.out.println(header); attribute(user); break; case "CHECK INVENTORY": System.out.println(header); inventory(user); break; case "STATS": System.out.println(header); stats(user); break; default: System.out.println("Choose a correct command (Type Commands to see a list)"); break; } if(user.isDead()){ user.deathPenalty(); System.out.println("\nYou are revived, at the cost of half your gold and your current weapon" ); user.incrementCurHp(user.getMaxHp()); } if(user.isDead()){ break; }else{ userInput = prompt("\nWhat would you like to do: "); } } } public String equip(Player player, Weapon item){ if(player.equipWeapon(item)){ return ind + player.getName() + " successfully equipped " + item.getName(); }else{ return ind + player.getName() + " failed to equip " + item.getName(); } } public String consume(Player player, Consumable item){ if(player.useConsumable(item)){ return ind + player.getName() + " consumed a " + item.getName() + " and gained " + item.getHeal() + " Hp"; }else{ return ind + player.getName() + " failed to consume a " + item.getName(); } } public String prompt(String question){ System.out.print(question); String input = s.nextLine(); return input; } public int promptInt(String question){ while(true){ String input = prompt(question); try{ int n = Integer.parseInt(input); return n; }catch(NumberFormatException e){ } } } public boolean rollAgility(Creature attacker, int high){ int roll = GameRandom.range(1, high); if(roll <= attacker.agilityBonus()){ return true; }else{ return false; } } public String attack(Creature one, Creature two){ boolean crit = rollAgility(one, 100); int a = one.attack(two, crit); if(crit){ return one.getName() + " CRITICAL hit " + two.getName() + " for " + a + " damage."; }else{ return one.getName() + " hit " + two.getName() + " for " + a + " damage."; } } public void battle(Player player){ //Generation of a random Monster with a random Weapon of the monster's level Monster encounter = new Monster(player.getLevel() + 1); Weapon randomWeapon = new Weapon(encounter.getLevel()); encounter.equipWeapon(randomWeapon); encounter.incrementCurHp(encounter.getMaxHp()); System.out.println(ind + "You encounter " + encounter.getName()); System.out.println(ind + encounter.getWeapon()); String input = prompt("\n" + ind + "What do you do (Type Choices to see a list): "); while(!player.isDead() && !encounter.isDead()){ boolean run = false; switch(input.toUpperCase()){ case "CHOICES": System.out.println(ind + "Attack, Run, Inventory(code this)"); input = prompt(ind + "What do you do: "); continue; case "ATTACK": System.out.println(ind + attack(player, encounter)); System.out.println(ind + encounter.status()); break; case "RUN": run = rollAgility(player, 25); break; default: System.out.println(ind + "Choose a correct choice (Type Choices to see a list)"); input = prompt(ind + "What do you do: "); continue; } if(input.equalsIgnoreCase("run")){ if(run){ System.out.println(ind + "You succeed at running"); break; }else{ System.out.println(ind + "You fail at running"); } } if(encounter.isDead()){ System.out.println("\n" + ind + "You killed " + encounter.getName()); System.out.println(ind + "You gain " + player.gainBattleXp(encounter) + " exp"); if(player.checkXp()){ System.out.println(ind + "You leveled up, your health is restored and you gain 1 attribute point!"); } System.out.println(ind + "You gained " + player.gainBattleGold(encounter) + " gold"); System.out.println(ind + "You have " + player.getXp() + " exp"); int roll = GameRandom.range(1, 20); if(roll <= 5){ System.out.println(ind + "You find a " + randomWeapon.getName() + "!"); player.addToInventory(randomWeapon, 1); } break; } System.out.println(ind + attack(encounter, player)); System.out.println(ind + player.status()); if(player.isDead()){ System.out.println("\n" + ind + "You fainted!"); break; } input = prompt("\n" + ind + "What do you do: "); } } public void attribute(Player player){ System.out.println(ind + "Your attribute points: " + player.getAtPoints() + "\n" + ind + "Strength: " + player.getStrength() + "\n" + ind + "Intellect: " + player.getIntellect() + "\n" + ind + "Agility: " + player.getAgility() + "\n" + ind + "Vitality: " + player.getVitality() + "\n"); if(player.getAtPoints() > 0){ String use = prompt(ind + "Do you want to use your " + player.getAtPoints() + " attribute points (Yes or No): "); if(use.equalsIgnoreCase("Yes")){ String choice = prompt(ind + "What attribute do you want to be increased: "); int amount = promptInt(ind + "How many attribute points do you want to spend on it: "); while(player.getAtPoints() > 0){ if(amount <= player.getAtPoints()){ switch(choice.toUpperCase()){ case "STRENGTH": player.getAt().increaseStrength(amount); player.incrementAtPoints(-amount); System.out.println(ind + "Your base Strength is now: " + player.getAt().getStrength()); break; case "INTELLECT": player.getAt().increaseIntellect(amount); player.incrementAtPoints(-amount); System.out.println(ind + "Your base Intellect is now: " + player.getAt().getIntellect()); break; case "AGILITY": player.getAt().increaseAgility(amount); player.incrementAtPoints(-amount); System.out.println(ind + "Your base Agility is now: " + player.getAt().getAgility()); break; case "VITALITY": player.getAt().increaseVitality(amount); player.incrementAtPoints(-amount); System.out.println(ind + "Your base Vitality is now: " + player.getAt().getVitality()); player.applyVitality(); break; case "DONE": amount = -1; break; default: System.out.println(ind + "Choose a correct attribute."); break; } if(amount == -1){ break; } System.out.println(ind + "You have " + player.getAtPoints() + " attribute points left"); if(player.getAtPoints() == 0){ break; } choice = prompt(ind + "What attribute do you want to be increased: "); amount = promptInt(ind + "How many attribute points do you want to spend on it: "); }else{ System.out.println(ind + "You do not have that many attribute points"); choice = prompt(ind + "What attribute do you want to be increased: "); amount = promptInt(ind + "How many attribute points do you want to spend on it: "); } } } } } public void inventory(Player player){ System.out.println(player.checkInventory(player)); String ask = prompt(ind + "Do you want to equip a weapon or use a consumable: "); if(ask.equalsIgnoreCase("Equip a Weapon")){ String equip = prompt(ind + "What do you want to equip: "); try{ Item test = player.stringToItem(equip); if(player.stringToItem(equip).getType().equalsIgnoreCase("Weapon")){ test = (Weapon)test; System.out.println(equip(player, (Weapon)player.stringToItem(equip))); }else{ System.out.println(ind + "You cannot equip that"); } }catch(NullPointerException e){ System.out.println(ind + "Invalid weapon"); } }else if(ask.equalsIgnoreCase("Use a Consumable")){ String consumable = prompt(ind + "What do you want to use: "); try{ Item test = player.stringToItem(consumable); if(test.getType().equalsIgnoreCase("Consumable")){ test = (Consumable)test; System.out.println(consume(player, (Consumable)player.stringToItem(consumable))); }else{ System.out.println(ind + "You cannot consume that"); } }catch(NullPointerException e){ System.out.println(ind + "Invalid consumable"); } } } public void stats(Player player){ System.out.println(ind + player.getWeapon() + ind); System.out.println(ind + player.status()); System.out.println(ind + "Level: " + player.getLevel()); System.out.println(ind + "Exp: " + player.getXp()); } } 

The GameRandom class handles generation of random numbers and random names/stats for Monsters and Weapons:

public class GameRandom{ //Handles all random events such as pulling a number from a range or generating a name/stats public static int roll(int sides){ int aRoll = (int)(Math.random() * sides + 1); return aRoll; } public static int range(double min, double max){ return (int)(Math.random() * (max - min + 1) + min); } public static String monsterName(){ String[] names = {"Spooky", "Scary", "Yup", "Yessir", "Grumblegore", "Minotaur"}; int index = (int)(Math.random() * names.length); return names[index]; } public static Attributes monsterAttributes(int level){ int str = (int)(level * Math.random() + 2); int intel = (int)(level * Math.random() + 2); int agil = (int)(level * Math.random() + 2); int vit = (int)(level * Math.random() + 2.5); Attributes x = new Attributes(str, intel, agil, vit); return x; } public static String weaponName(int level){ String output = ""; output += "Lv" + level + " "; String[] prefix; String[] base; String[] suffix; String suf = ""; if(level <= 5){ prefix = new String[]{"Broken" , "Bronze", "Copper", "Weak", "Training", "Fragile"}; }else if(level <= 10){ prefix = new String[]{"Iron", "Silver", "Cold Iron", "Darkwood", "Bone", "Reinforced"}; }else{ prefix = new String[]{"Gilded", "Warrior's", "Absolute", "Golden", "Mithril", "Holy", "Demonic"}; suffix = new String[]{"Of Destruction", "Of Fear", "Of Spooks", "Of Serenity", "Of Hope", "Of Despair", "Of Depression"}; int sIndex = (int)(Math.random() * suffix.length); suf += " " + suffix[sIndex]; } base = new String[]{"Lance", "Sword", "Dagger", "Staff", "Mace", "Pole", "Spear", "Halberd", "Scythe", "Knife", "Shiv", "Nunchucks", "Gauntlets", "Flail", "Morning Star" }; int bIndex = (int)(Math.random() * base.length); int pIndex = (int)(Math.random() * prefix.length); output += prefix[pIndex] + " "; output += base[bIndex]; if(level > 10){ output += suf; } return output; } public static Attributes weaponAttributes(int level){ int str = (int)(level * Math.random()); int intel = (int)(level * Math.random()); int agil = (int)(level * Math.random()); int vit = (int)(level * Math.random()); Attributes x = new Attributes(str, intel, agil, vit); return x; } } 

This is the Creature class, from which the Player and Monster classes extend:

public abstract class Creature{ public String name; public int baseHp; public int maxHp; public int curHp; public int maxAtt; public int level; public Attributes attr; public Weapon equippedWeapon; public Weapon fists = new Weapon("Fists" , 0, 0, 0 ,0, 0); public Creature(String name, int baseHp, int maxAtt, int level){ this.name = name; this.level = level; this.maxAtt = maxAtt; this.attr = new Attributes(0, 0, 0 ,0); this.equippedWeapon = fists; this.baseHp = baseHp; this.applyVitality(); this.curHp = maxHp; } //Names and Statuses public String getName(){ return name; } public String getWeapon(){ return name + " is wielding " + equippedWeapon.getName(); } public String status(){ this.applyVitality(); return name + " has " + curHp + "/" + maxHp + " Hp."; } public int getLevel(){ return level; } public int getMaxHp(){ return maxHp; } //Weapons //Battle Methods public int attack(Creature other, boolean crit){ int att = maxAtt + (int)(this.strengthBonus() * level)/3; int roll = GameRandom.range(1, 100); //Pulls a boolean from the input that way crits can be announced in MyProgram if(crit){ att *= 1.75; } int minAtt = (int)(att * .7); att = GameRandom.range(minAtt, att); other.incrementCurHp(-att); return att; } public void incrementCurHp(int amount){ applyVitality(); curHp += amount; if(curHp >= maxHp){ curHp = maxHp; }else if(curHp <= 0){ curHp = 0; } } public int getCurHp(){ return curHp; } public int getMaxAtt(){ return maxAtt; } public boolean isDead(){ return curHp <= 0; } //Attributes public Attributes getAt(){ return attr; } public String getStrength(){ return this.getAt().getStrength() + " + " + this.equippedWeapon.getAt().getStrength(); } public int strengthBonus(){ return this.getAt().getStrength() + this.equippedWeapon.getAt().getStrength(); } public String getIntellect(){ return this.getAt().getIntellect() +" + " + this.equippedWeapon.getAt().getIntellect(); } public int intellectBonus(){ return this.getAt().getIntellect() + this.equippedWeapon.getAt().getIntellect(); } public String getAgility(){ return this.getAt().getAgility() + " + " + this.equippedWeapon.getAt().getAgility(); } public int agilityBonus(){ return this.getAt().getAgility() + this.equippedWeapon.getAt().getAgility(); } public String getVitality(){ return this.getAt().getVitality() + " + " +this.equippedWeapon.getAt().getVitality(); } public int vitalityBonus(){ return this.getAt().getVitality() + this.equippedWeapon.getAt().getVitality(); } public void applyVitality(){ this.maxHp = (this.vitalityBonus() * level) + baseHp; } } 

Attribute class, the stats bonuses for Creatures and Weapons

 public class Attributes{ public int strength; public int intellect; public int agility; public int vitality; public Attributes(int strength, int intellect, int agility, int vitality){ this.strength = strength; this.intellect = intellect; this.agility = agility; this.vitality = vitality; } public int getStrength(){ return strength; } public int getIntellect(){ return intellect; } public int getAgility(){ return agility; } public int getVitality(){ return vitality; } public void increaseStrength(int amount){ strength += amount; } public void increaseIntellect(int amount){ intellect += amount; } public void increaseAgility(int amount){ agility += amount; } public void increaseVitality(int amount){ vitality += amount; } } 

Player class:

import java.util.HashMap; public class Player extends Creature{ public int gold; Attributes playerAttr; public int xp = 0; public int xpThreshold = 80; public int attributePoints; Inventory playerInv; public Player(String name){ super(name, 25, 15, 1); this.maxHp = baseHp; this.attr = new Attributes(0, 0 , 1, 0); this.playerInv = new Inventory(this); this.attributePoints = 5; this.gold = 0; } //equipping/unequipping weapons as it pertains to the player's inventory public boolean equipWeapon(Weapon weapon){ if(weapon.getLevel() <= this.level){ if(this.getHashMap().containsKey(weapon)){ if(playerInv.incrementAmount(weapon, -1)){ this.unequipWeapon(); this.equippedWeapon = weapon; this.applyVitality(); this.incrementCurHp(0); return true; }else{ return false; } } }else{ this.incrementCurHp(0); return false; } return false; } public void unequipWeapon(){ this.addToInventory(this.equippedWeapon, 1); this.equippedWeapon = fists; this.incrementCurHp(0); } //XP and Level ups public void levelUp(){ level++; this.baseHp += level * 3; this.maxAtt += (int)(level * 1.5); this.applyVitality(); this.curHp = this.maxHp; attributePoints++; } public void setXpThreshold(){ if(this.level <=2){ xpThreshold = this.level * 80; }else{ xpThreshold = this.level * (this.level - 2) * 120; } } public int gainBattleXp(Creature other){ int base = other.level * 40; int gain = GameRandom.range(base * 1.3, (base * .7)); xp += gain; return gain; } public boolean checkXp(){ this.setXpThreshold(); if(xp >= xpThreshold){ xp -= xpThreshold; this.levelUp(); this.setXpThreshold(); return true; }else{ return false; } } public int gainBattleGold(Creature other){ int money = other.level * 40; int gain = GameRandom.range(money * 1.3, money * .7); this.gold += gain; return gain; } public String getXp(){ this.setXpThreshold(); return xp + "/" + xpThreshold; } //Attribute Points public int getAtPoints(){ return attributePoints; } public void incrementAtPoints(int amount){ attributePoints += amount; } //Inventory, HashMap with item keys and amount values public String checkInventory(Player player){ return "-----Your Gold: " + this.gold + "\n" + playerInv.checkInventory(player); } public void addToInventory(Item item, int amount){ if(this.getHashMap().containsKey(item)){ playerInv.incrementAmount(item, amount); }else{ this.getHashMap().put(item, amount); } } public boolean removeFromInventory(Item item, int amount){ if(this.getHashMap().containsKey(item)){ if(playerInv.incrementAmount(item, amount)){ return true; }else{ return false; } }else{ return false; } } public Inventory getInventory(){ return playerInv; } public HashMap<Item, Integer> getHashMap(){ return playerInv.inventory; } public boolean checkInventoryFor(String input){ for(Item key: getHashMap().keySet()){ if(input.equalsIgnoreCase(key.getName())){ return true; }else{ return false; } } return true; } public Item stringToItem(String input){ for(Item key: this.getHashMap().keySet()){ if(key.getName().equalsIgnoreCase(input)){ return key; } } return null; } public boolean useConsumable(Consumable item){ if(this.getHashMap().containsKey(item)){ if(playerInv.incrementAmount(item, -1)){ this.applyVitality(); this.incrementCurHp(item.getHeal()); return true; }else{ return false; } } return false; } public void deathPenalty(){ this.gold = (int)(this.gold * .5); this.equippedWeapon = fists; } } 

Monster class:

public class Monster extends Creature{ //Hand created Monster public Monster(String name, int maxHp, int maxAtt, int level){ super(name, maxHp, maxAtt, level); this.attr = GameRandom.monsterAttributes(level); this.applyVitality(); this.curHp = this.maxHp; } //Randomly genereated Monster of a certain level public Monster(int level){ super(GameRandom.monsterName(), 20, 10, level); this.attr = GameRandom.monsterAttributes(level); this.applyVitality(); this.curHp = this.maxHp; } public void equipWeapon(Weapon weapon){ this.equippedWeapon = weapon; this.applyVitality(); } } 

Item class, from which Weapon and Consumable extend:

public class Item{ public String name; public Attributes attr; public String type; public Item(String name){ this.name = name; this.attr = new Attributes(0, 0 ,0 ,0); this.type = "item"; } public String getName(){ return name; } public String toString(){ return name; } public Attributes getAt(){ return attr; } public String getType(){ return type; } } 

Weapon class:

public class Weapon extends Item{ Attributes weaponAttr; public int level; public Weapon(String name, int strength, int magic, int agility, int vitality, int level){ super(name); this.weaponAttr = new Attributes(strength, magic, agility, vitality); this.level = level; this.type = "Weapon"; } public Weapon(int level){ super(GameRandom.weaponName(level)); this.weaponAttr = GameRandom.weaponAttributes(level); this.level = level; this.type = "Weapon"; } public Attributes getAt(){ return weaponAttr; } public int getLevel(){ return level; } } 

Consumable class:

public class Consumable extends Item{ public int heal; public int level; public Consumable(String name, int heal, int level){ super(name); this.heal = heal; this.level = level; this.type = "Consumable"; } //Create a second constructor to generate a random consumable from a level //Create name databases within GameRandom for fishing/hunting public int getHeal(){ return heal; } } 

And finally the Inventory class:

import java.util.HashMap; public class Inventory{ public Creature owner; public HashMap<Item , Integer> inventory = new HashMap<Item , Integer>(); public Inventory(Creature owner){ this.owner = owner; } public boolean incrementAmount(Item item, int change){ if(change > 0){ int oldValue = inventory.get(item); int newValue = oldValue + change; inventory.put(item, newValue); return true; }else{ if(inventory.get(item) >= change){ int oldValue = inventory.get(item); int newValue = oldValue + change; inventory.put(item, newValue); return true; }else{ return false; } } } public HashMap<Item, Integer> inventory(){ return inventory; } //Consider splitting inventory into two/three hashmaps (Weapon, Consumable, collectible etc) public String checkInventory(Player player){ String output = ""; for(Item key: inventory.keySet()){ if(!(key.getName().equalsIgnoreCase("Fists"))){ Integer amount = inventory.get(key); if(amount > 0){ String name = key.getName(); output += "-----" + name; if(key.getType().equalsIgnoreCase("Weapon")){ key = (Weapon)key; int str = key.getAt().getStrength(); int inte = key.getAt().getIntellect(); int agi = key.getAt().getAgility(); int vit = key.getAt().getVitality(); output += "(" + str + ", " + inte + ", " + agi + ", " + vit + ")"; } output += ": " + amount + "\n"; } } } return output; } } 
\$\endgroup\$

    1 Answer 1

    1
    \$\begingroup\$

    It looks like Consumable is extending Item, but is not using attribute, so i would get rid of attribute in item.

    You could extend Item with the fields amount and stackable and remove attr

    Then extend Item with the following methods (Also add removeItem)

    public int getAmount(){ return amount; } public boolean addToStack(){ if(stackable){ amount++; return true; } return false; } 

    Now i would instead use a HashMap<String, Item> because it would be easier to add and remove stuff from your inventory.

    public class Inventory{ private Map<String, Item> inventory; public Inventory(Create owner){ this.inventory = new HashMap<String, Item>(); } public boolean addItem(Item item){ if(!inventory.containsKey(item.getName()){ inventory.put(item.getName(), item); return true; } else if(inventory.containsKey(item.getName()) && item.isStackable()){ inventory.get(item.getName()).addToStack(); return true; } return false; } public String checkInventory(){ StringBuilder builder = new StringBuilder(); List<Item> items = inventory.values(); for(Item item : items){ builder.append(item.getName()); if(item instanceof Weapon){ Weapon weapon = (Weapon)item; Attribute attribute = weapon.getAttribute(); builder.append(atrribute.getStrength()); //etc. } builder.append(item.getAmount()); } return builder.toString(); } } 

    Considering that you usually can have many different items in your inventory, it wouldn't make sense to separate them into different HashMaps, but instead make Item smaller, then use instanceof to figure out what class it is.

    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.