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 Weapon
s or Consumable
s. 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 Monster
s and Weapon
s:
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; } }