Simple text-based RPG leveling system











up vote
6
down vote

favorite
1












I have two issues/questions with figuring out a proper way to write this code for a player leveling system.



My First question/issue is that when this code is run it works as intended with a slight problem. public void levelUp() is being used to, "set", the level if you will (I am sure this is a sloppy way of doing this but we will get to that in the next question) while levelUpXp() is being used as the only way I currently know how to store the elements of int requiredXP to let the game know when the player has reached X amount of xp to level him up to X level. ding() is simply a notification for the player to be aware that they have increased in level.



As stated before this all works the way I am wanting it to work however upon implementing ding() after a countless amount of time trying to write multiple if statements to do what ding is doing now, I have run into an issue where curXP is NOT equal to either reqXP or requiredXP due to the amount of XP the player is gaining not always being an exact number within requiredXP. While I already assumed this would happen, it is now resulting in either a level up notification not being sent because curXP == requiredXP[x] is not always the case if the amount of xp needed to level is 17 but after killing an enemy the player goes from 15xp to 18xp or curXP <= requiredXP[x] causing the level up notification to be constantly sent until that player reaches their next level in which the notification is still sent however with a new level attached.



Second question will be below code.



Here is my player class and methods:



public class Player extends Creature {

int health = 100;
int maxHealth = 100;
int attackDamage = 25;
int numHealthPotions = 3;
int healthPotHeal = 30;
int curXP = 0;
int level = 0;
int reqXP = 0;

int currentLevel = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int requiredXP = { 0, 6, 17, 36, 65, 105, 158, 224, 305, 402 };

public void levelUp() {

if (curXP == requiredXP[0]) {
level = currentLevel[0];
} else if (curXP == requiredXP[1]) {
level = currentLevel[1];
} else if (curXP == requiredXP[2]) {
level = currentLevel[2];
} else if (curXP == requiredXP[3]) {
level = currentLevel[3];
} else if (curXP == requiredXP[4]) {
level = currentLevel[4];
} else if (curXP == requiredXP[5]) {
level = currentLevel[5];
} else if (curXP == requiredXP[6]) {
level = currentLevel[6];
} else if (curXP == requiredXP[7]) {
level = currentLevel[7];
} else if (curXP == requiredXP[8]) {
level = currentLevel[8];
} else if (curXP == requiredXP[9]) {
level = currentLevel[9];
}
}

public void levelUpXp() {

if (level == currentLevel[0]) {
reqXP = requiredXP[0];
} else if (level == currentLevel[1]) {
reqXP = requiredXP[1];
} else if (level == currentLevel[2]) {
reqXP = requiredXP[2];
}else if (level == currentLevel[3]) {
reqXP = requiredXP[3];
} else if (level == currentLevel[4]) {
reqXP = requiredXP[4];
}else if (level == currentLevel[5]) {
reqXP = requiredXP[5];
} else if (level == currentLevel[6]) {
reqXP = requiredXP[6];
}else if (level == currentLevel[7]) {
reqXP = requiredXP[7];
} else if (level == currentLevel[8]) {
reqXP = requiredXP[8];
}else if(level == currentLevel[9]) {
reqXP = requiredXP[9];
}
}

public void ding() {
if(level == 2 && curXP == requiredXP[1]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 3 && curXP == requiredXP[2]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 4 && curXP == requiredXP[3]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 5 && curXP == requiredXP[4]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 6 && curXP == requiredXP[5]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 7 && curXP == requiredXP[6]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 8 && curXP == requiredXP[7]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 9 && curXP == requiredXP[8]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 10 && curXP == requiredXP[9]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
}
}

public int getLevel() {
return level;
}

public void setLevel(int level) {
this.level = level;
}

public void Level() {
int maxLevel = 200;
int maxLevelXP = 1000000;
for (int currentLevel = 1; currentLevel < maxLevel; currentLevel += 1) {
float x = currentLevel / (float) maxLevel;
double y = Math.pow(x, 2.61);
int requiredXP = (int) (y * maxLevelXP);
System.out.println("Level " + currentLevel + " XP: " + requiredXP);
}

}


Here is game class where ding and other previously mentioned Methods are used:



GAME: while (running) {

p.levelUp();
p.levelUpXp();
System.out.println("-----------------------------------------------");

String enemy = e.enemies[rand.nextInt(e.enemies.length)];
int enemyHealth = e.enemyHealth;
int preLevel = 1;
int curLevel = preLevel + 1;

System.out.println("t# " + enemy + " has appeared! #n");

while (enemyHealth > 0) {

System.out.println(
"t" + userName + "nt HP: " + p.health + " Level: " + p.level + " Exp: " + p.curXP + "nt" + "nt" + enemy + "nt HP: " + enemyHealth);
System.out.println("nt What would you like to do?");
System.out.println("t 1. Attack");
System.out.println("t 2. Drink health potion");
System.out.println("t 3. Run!");

String input = in.nextLine();
if (input.equals("1")) {
int damageDealt = rand.nextInt(p.attackDamage);
int damageTaken = rand.nextInt(e.maxEnemyAD);

enemyHealth -= damageDealt;
p.health -= damageTaken;
if (damageDealt > 0) {
System.out.println("t> You strike the " + enemy + " for " + damageDealt + " damage!");
} else if (damageDealt < 1) {
System.out.println("t> You attempt to hit the " + enemy + " but miss!");
}
if (damageTaken > 0) {
System.out.println("t> The " + enemy + " retaliates! You take " + damageTaken + " damage!");
} else if (damageTaken < 1) {
System.out.println("t> The " + enemy + " retaliates but misses!");
}
if (p.health < 1) {
System.out.println(
"t ##################" + "nt # You Have Died! #" + "nt ##################");
break;
}

} else if (input.equals("2")) {
if (p.numHealthPotions > 0 && p.health != p.maxHealth) {
p.health += p.healthPotHeal;

p.numHealthPotions--;
if (p.health > p.maxHealth) {
p.health = p.maxHealth;
}

System.out.println("t> You drink the Health Potion!" + "nt> You now have " + p.health
+ " Health!" + "nt> You now have " + p.numHealthPotions + " Health Potion(s) left!");

} else if (p.health == p.maxHealth) {
System.out.println("t> Your health is already full!");

} else if (p.numHealthPotions < 1) {
System.out.println("t You have no Health Potions left!");
}
} else if (input.equals("3")) {
System.out.println("t> You run away from the " + enemy + "!");
continue GAME;
} else {
System.out.println("t Invalid Command!");
}
}
if (p.health < 1) {
System.out.println("You fought bravely but in the end, you're just another corpse...");
break;
}

System.out.println("-----------------------------------------------");

if (enemyHealth < 1) {
p.curXP += 3;
p.levelUp();
p.levelUpXp();

}

System.out.println(" # The " + enemy + " was defeated! # ");
System.out.println(" # You have " + p.health + " HP left! # ");
System.out.println(" # You have gained " + e.xpGive + " xp! # ");
p.ding();
if (rand.nextInt(100) < e.dropChance) {
p.numHealthPotions++;
System.out.println(" # The " + enemy + " droppped a Health Potion! # ");
System.out.println(" # You now have " + p.numHealthPotions + " Health Potion(s)! # ");
}

System.out.println("-----------------------------------------------");
System.out.println("tWhat would you like to do?");
System.out.println("t1. Press on!");
System.out.println("t2. Get me out of here!");

String input = in.nextLine();

while (!input.equals("1") && !input.equals("2")) {
System.out.println("tInvalid Command!");
input = in.nextLine();

}

if (input.equals("1")) {
System.out.println("t> You march on, deeper into the Dungeon!");

} else if (input.equals("2")) {
System.out.println(
"tYou exit the dungeon. Battered and bruised as you may be, you live to fight another day!");
break;
}
}

}


Now my second and last question is with my Player class and more specifically the way my levelUp(), levelUpXp() and ding() Methods are written. There has to be a better way right? I have read Javadocs, watched tutorials, read physical books and I have even done courses on sites like Codecademy and just can't seem to wrap my head around this.



My initial goal was to have the Array automatically switch from currentLevel[0](Level 1) to currentLevel[1](Level 2) once the amount of reqXP of requiredXP[1] was met. I know a .next() exists for ArrayLists similar to rand.nextInt() but I don't know if that is exclusive to ArrayLists or if they can be used with Arrays as well. The current code you see now is a result of this lack of knowledge so any help or advice is much appreciated.



I am still new to programming and this is also only my second post in 2 years.










share|improve this question









New contributor




Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 2




    It may be worth adding the definition of Creature if that will enable reviewers to compile and run the code ourselves. Don't worry about including "too much" code - that's unlikely to be a problem here (we're very different to Stack Overflow in that respect!).
    – Toby Speight
    19 hours ago















up vote
6
down vote

favorite
1












I have two issues/questions with figuring out a proper way to write this code for a player leveling system.



My First question/issue is that when this code is run it works as intended with a slight problem. public void levelUp() is being used to, "set", the level if you will (I am sure this is a sloppy way of doing this but we will get to that in the next question) while levelUpXp() is being used as the only way I currently know how to store the elements of int requiredXP to let the game know when the player has reached X amount of xp to level him up to X level. ding() is simply a notification for the player to be aware that they have increased in level.



As stated before this all works the way I am wanting it to work however upon implementing ding() after a countless amount of time trying to write multiple if statements to do what ding is doing now, I have run into an issue where curXP is NOT equal to either reqXP or requiredXP due to the amount of XP the player is gaining not always being an exact number within requiredXP. While I already assumed this would happen, it is now resulting in either a level up notification not being sent because curXP == requiredXP[x] is not always the case if the amount of xp needed to level is 17 but after killing an enemy the player goes from 15xp to 18xp or curXP <= requiredXP[x] causing the level up notification to be constantly sent until that player reaches their next level in which the notification is still sent however with a new level attached.



Second question will be below code.



Here is my player class and methods:



public class Player extends Creature {

int health = 100;
int maxHealth = 100;
int attackDamage = 25;
int numHealthPotions = 3;
int healthPotHeal = 30;
int curXP = 0;
int level = 0;
int reqXP = 0;

int currentLevel = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int requiredXP = { 0, 6, 17, 36, 65, 105, 158, 224, 305, 402 };

public void levelUp() {

if (curXP == requiredXP[0]) {
level = currentLevel[0];
} else if (curXP == requiredXP[1]) {
level = currentLevel[1];
} else if (curXP == requiredXP[2]) {
level = currentLevel[2];
} else if (curXP == requiredXP[3]) {
level = currentLevel[3];
} else if (curXP == requiredXP[4]) {
level = currentLevel[4];
} else if (curXP == requiredXP[5]) {
level = currentLevel[5];
} else if (curXP == requiredXP[6]) {
level = currentLevel[6];
} else if (curXP == requiredXP[7]) {
level = currentLevel[7];
} else if (curXP == requiredXP[8]) {
level = currentLevel[8];
} else if (curXP == requiredXP[9]) {
level = currentLevel[9];
}
}

public void levelUpXp() {

if (level == currentLevel[0]) {
reqXP = requiredXP[0];
} else if (level == currentLevel[1]) {
reqXP = requiredXP[1];
} else if (level == currentLevel[2]) {
reqXP = requiredXP[2];
}else if (level == currentLevel[3]) {
reqXP = requiredXP[3];
} else if (level == currentLevel[4]) {
reqXP = requiredXP[4];
}else if (level == currentLevel[5]) {
reqXP = requiredXP[5];
} else if (level == currentLevel[6]) {
reqXP = requiredXP[6];
}else if (level == currentLevel[7]) {
reqXP = requiredXP[7];
} else if (level == currentLevel[8]) {
reqXP = requiredXP[8];
}else if(level == currentLevel[9]) {
reqXP = requiredXP[9];
}
}

public void ding() {
if(level == 2 && curXP == requiredXP[1]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 3 && curXP == requiredXP[2]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 4 && curXP == requiredXP[3]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 5 && curXP == requiredXP[4]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 6 && curXP == requiredXP[5]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 7 && curXP == requiredXP[6]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 8 && curXP == requiredXP[7]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 9 && curXP == requiredXP[8]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 10 && curXP == requiredXP[9]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
}
}

public int getLevel() {
return level;
}

public void setLevel(int level) {
this.level = level;
}

public void Level() {
int maxLevel = 200;
int maxLevelXP = 1000000;
for (int currentLevel = 1; currentLevel < maxLevel; currentLevel += 1) {
float x = currentLevel / (float) maxLevel;
double y = Math.pow(x, 2.61);
int requiredXP = (int) (y * maxLevelXP);
System.out.println("Level " + currentLevel + " XP: " + requiredXP);
}

}


Here is game class where ding and other previously mentioned Methods are used:



GAME: while (running) {

p.levelUp();
p.levelUpXp();
System.out.println("-----------------------------------------------");

String enemy = e.enemies[rand.nextInt(e.enemies.length)];
int enemyHealth = e.enemyHealth;
int preLevel = 1;
int curLevel = preLevel + 1;

System.out.println("t# " + enemy + " has appeared! #n");

while (enemyHealth > 0) {

System.out.println(
"t" + userName + "nt HP: " + p.health + " Level: " + p.level + " Exp: " + p.curXP + "nt" + "nt" + enemy + "nt HP: " + enemyHealth);
System.out.println("nt What would you like to do?");
System.out.println("t 1. Attack");
System.out.println("t 2. Drink health potion");
System.out.println("t 3. Run!");

String input = in.nextLine();
if (input.equals("1")) {
int damageDealt = rand.nextInt(p.attackDamage);
int damageTaken = rand.nextInt(e.maxEnemyAD);

enemyHealth -= damageDealt;
p.health -= damageTaken;
if (damageDealt > 0) {
System.out.println("t> You strike the " + enemy + " for " + damageDealt + " damage!");
} else if (damageDealt < 1) {
System.out.println("t> You attempt to hit the " + enemy + " but miss!");
}
if (damageTaken > 0) {
System.out.println("t> The " + enemy + " retaliates! You take " + damageTaken + " damage!");
} else if (damageTaken < 1) {
System.out.println("t> The " + enemy + " retaliates but misses!");
}
if (p.health < 1) {
System.out.println(
"t ##################" + "nt # You Have Died! #" + "nt ##################");
break;
}

} else if (input.equals("2")) {
if (p.numHealthPotions > 0 && p.health != p.maxHealth) {
p.health += p.healthPotHeal;

p.numHealthPotions--;
if (p.health > p.maxHealth) {
p.health = p.maxHealth;
}

System.out.println("t> You drink the Health Potion!" + "nt> You now have " + p.health
+ " Health!" + "nt> You now have " + p.numHealthPotions + " Health Potion(s) left!");

} else if (p.health == p.maxHealth) {
System.out.println("t> Your health is already full!");

} else if (p.numHealthPotions < 1) {
System.out.println("t You have no Health Potions left!");
}
} else if (input.equals("3")) {
System.out.println("t> You run away from the " + enemy + "!");
continue GAME;
} else {
System.out.println("t Invalid Command!");
}
}
if (p.health < 1) {
System.out.println("You fought bravely but in the end, you're just another corpse...");
break;
}

System.out.println("-----------------------------------------------");

if (enemyHealth < 1) {
p.curXP += 3;
p.levelUp();
p.levelUpXp();

}

System.out.println(" # The " + enemy + " was defeated! # ");
System.out.println(" # You have " + p.health + " HP left! # ");
System.out.println(" # You have gained " + e.xpGive + " xp! # ");
p.ding();
if (rand.nextInt(100) < e.dropChance) {
p.numHealthPotions++;
System.out.println(" # The " + enemy + " droppped a Health Potion! # ");
System.out.println(" # You now have " + p.numHealthPotions + " Health Potion(s)! # ");
}

System.out.println("-----------------------------------------------");
System.out.println("tWhat would you like to do?");
System.out.println("t1. Press on!");
System.out.println("t2. Get me out of here!");

String input = in.nextLine();

while (!input.equals("1") && !input.equals("2")) {
System.out.println("tInvalid Command!");
input = in.nextLine();

}

if (input.equals("1")) {
System.out.println("t> You march on, deeper into the Dungeon!");

} else if (input.equals("2")) {
System.out.println(
"tYou exit the dungeon. Battered and bruised as you may be, you live to fight another day!");
break;
}
}

}


Now my second and last question is with my Player class and more specifically the way my levelUp(), levelUpXp() and ding() Methods are written. There has to be a better way right? I have read Javadocs, watched tutorials, read physical books and I have even done courses on sites like Codecademy and just can't seem to wrap my head around this.



My initial goal was to have the Array automatically switch from currentLevel[0](Level 1) to currentLevel[1](Level 2) once the amount of reqXP of requiredXP[1] was met. I know a .next() exists for ArrayLists similar to rand.nextInt() but I don't know if that is exclusive to ArrayLists or if they can be used with Arrays as well. The current code you see now is a result of this lack of knowledge so any help or advice is much appreciated.



I am still new to programming and this is also only my second post in 2 years.










share|improve this question









New contributor




Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 2




    It may be worth adding the definition of Creature if that will enable reviewers to compile and run the code ourselves. Don't worry about including "too much" code - that's unlikely to be a problem here (we're very different to Stack Overflow in that respect!).
    – Toby Speight
    19 hours ago













up vote
6
down vote

favorite
1









up vote
6
down vote

favorite
1






1





I have two issues/questions with figuring out a proper way to write this code for a player leveling system.



My First question/issue is that when this code is run it works as intended with a slight problem. public void levelUp() is being used to, "set", the level if you will (I am sure this is a sloppy way of doing this but we will get to that in the next question) while levelUpXp() is being used as the only way I currently know how to store the elements of int requiredXP to let the game know when the player has reached X amount of xp to level him up to X level. ding() is simply a notification for the player to be aware that they have increased in level.



As stated before this all works the way I am wanting it to work however upon implementing ding() after a countless amount of time trying to write multiple if statements to do what ding is doing now, I have run into an issue where curXP is NOT equal to either reqXP or requiredXP due to the amount of XP the player is gaining not always being an exact number within requiredXP. While I already assumed this would happen, it is now resulting in either a level up notification not being sent because curXP == requiredXP[x] is not always the case if the amount of xp needed to level is 17 but after killing an enemy the player goes from 15xp to 18xp or curXP <= requiredXP[x] causing the level up notification to be constantly sent until that player reaches their next level in which the notification is still sent however with a new level attached.



Second question will be below code.



Here is my player class and methods:



public class Player extends Creature {

int health = 100;
int maxHealth = 100;
int attackDamage = 25;
int numHealthPotions = 3;
int healthPotHeal = 30;
int curXP = 0;
int level = 0;
int reqXP = 0;

int currentLevel = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int requiredXP = { 0, 6, 17, 36, 65, 105, 158, 224, 305, 402 };

public void levelUp() {

if (curXP == requiredXP[0]) {
level = currentLevel[0];
} else if (curXP == requiredXP[1]) {
level = currentLevel[1];
} else if (curXP == requiredXP[2]) {
level = currentLevel[2];
} else if (curXP == requiredXP[3]) {
level = currentLevel[3];
} else if (curXP == requiredXP[4]) {
level = currentLevel[4];
} else if (curXP == requiredXP[5]) {
level = currentLevel[5];
} else if (curXP == requiredXP[6]) {
level = currentLevel[6];
} else if (curXP == requiredXP[7]) {
level = currentLevel[7];
} else if (curXP == requiredXP[8]) {
level = currentLevel[8];
} else if (curXP == requiredXP[9]) {
level = currentLevel[9];
}
}

public void levelUpXp() {

if (level == currentLevel[0]) {
reqXP = requiredXP[0];
} else if (level == currentLevel[1]) {
reqXP = requiredXP[1];
} else if (level == currentLevel[2]) {
reqXP = requiredXP[2];
}else if (level == currentLevel[3]) {
reqXP = requiredXP[3];
} else if (level == currentLevel[4]) {
reqXP = requiredXP[4];
}else if (level == currentLevel[5]) {
reqXP = requiredXP[5];
} else if (level == currentLevel[6]) {
reqXP = requiredXP[6];
}else if (level == currentLevel[7]) {
reqXP = requiredXP[7];
} else if (level == currentLevel[8]) {
reqXP = requiredXP[8];
}else if(level == currentLevel[9]) {
reqXP = requiredXP[9];
}
}

public void ding() {
if(level == 2 && curXP == requiredXP[1]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 3 && curXP == requiredXP[2]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 4 && curXP == requiredXP[3]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 5 && curXP == requiredXP[4]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 6 && curXP == requiredXP[5]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 7 && curXP == requiredXP[6]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 8 && curXP == requiredXP[7]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 9 && curXP == requiredXP[8]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 10 && curXP == requiredXP[9]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
}
}

public int getLevel() {
return level;
}

public void setLevel(int level) {
this.level = level;
}

public void Level() {
int maxLevel = 200;
int maxLevelXP = 1000000;
for (int currentLevel = 1; currentLevel < maxLevel; currentLevel += 1) {
float x = currentLevel / (float) maxLevel;
double y = Math.pow(x, 2.61);
int requiredXP = (int) (y * maxLevelXP);
System.out.println("Level " + currentLevel + " XP: " + requiredXP);
}

}


Here is game class where ding and other previously mentioned Methods are used:



GAME: while (running) {

p.levelUp();
p.levelUpXp();
System.out.println("-----------------------------------------------");

String enemy = e.enemies[rand.nextInt(e.enemies.length)];
int enemyHealth = e.enemyHealth;
int preLevel = 1;
int curLevel = preLevel + 1;

System.out.println("t# " + enemy + " has appeared! #n");

while (enemyHealth > 0) {

System.out.println(
"t" + userName + "nt HP: " + p.health + " Level: " + p.level + " Exp: " + p.curXP + "nt" + "nt" + enemy + "nt HP: " + enemyHealth);
System.out.println("nt What would you like to do?");
System.out.println("t 1. Attack");
System.out.println("t 2. Drink health potion");
System.out.println("t 3. Run!");

String input = in.nextLine();
if (input.equals("1")) {
int damageDealt = rand.nextInt(p.attackDamage);
int damageTaken = rand.nextInt(e.maxEnemyAD);

enemyHealth -= damageDealt;
p.health -= damageTaken;
if (damageDealt > 0) {
System.out.println("t> You strike the " + enemy + " for " + damageDealt + " damage!");
} else if (damageDealt < 1) {
System.out.println("t> You attempt to hit the " + enemy + " but miss!");
}
if (damageTaken > 0) {
System.out.println("t> The " + enemy + " retaliates! You take " + damageTaken + " damage!");
} else if (damageTaken < 1) {
System.out.println("t> The " + enemy + " retaliates but misses!");
}
if (p.health < 1) {
System.out.println(
"t ##################" + "nt # You Have Died! #" + "nt ##################");
break;
}

} else if (input.equals("2")) {
if (p.numHealthPotions > 0 && p.health != p.maxHealth) {
p.health += p.healthPotHeal;

p.numHealthPotions--;
if (p.health > p.maxHealth) {
p.health = p.maxHealth;
}

System.out.println("t> You drink the Health Potion!" + "nt> You now have " + p.health
+ " Health!" + "nt> You now have " + p.numHealthPotions + " Health Potion(s) left!");

} else if (p.health == p.maxHealth) {
System.out.println("t> Your health is already full!");

} else if (p.numHealthPotions < 1) {
System.out.println("t You have no Health Potions left!");
}
} else if (input.equals("3")) {
System.out.println("t> You run away from the " + enemy + "!");
continue GAME;
} else {
System.out.println("t Invalid Command!");
}
}
if (p.health < 1) {
System.out.println("You fought bravely but in the end, you're just another corpse...");
break;
}

System.out.println("-----------------------------------------------");

if (enemyHealth < 1) {
p.curXP += 3;
p.levelUp();
p.levelUpXp();

}

System.out.println(" # The " + enemy + " was defeated! # ");
System.out.println(" # You have " + p.health + " HP left! # ");
System.out.println(" # You have gained " + e.xpGive + " xp! # ");
p.ding();
if (rand.nextInt(100) < e.dropChance) {
p.numHealthPotions++;
System.out.println(" # The " + enemy + " droppped a Health Potion! # ");
System.out.println(" # You now have " + p.numHealthPotions + " Health Potion(s)! # ");
}

System.out.println("-----------------------------------------------");
System.out.println("tWhat would you like to do?");
System.out.println("t1. Press on!");
System.out.println("t2. Get me out of here!");

String input = in.nextLine();

while (!input.equals("1") && !input.equals("2")) {
System.out.println("tInvalid Command!");
input = in.nextLine();

}

if (input.equals("1")) {
System.out.println("t> You march on, deeper into the Dungeon!");

} else if (input.equals("2")) {
System.out.println(
"tYou exit the dungeon. Battered and bruised as you may be, you live to fight another day!");
break;
}
}

}


Now my second and last question is with my Player class and more specifically the way my levelUp(), levelUpXp() and ding() Methods are written. There has to be a better way right? I have read Javadocs, watched tutorials, read physical books and I have even done courses on sites like Codecademy and just can't seem to wrap my head around this.



My initial goal was to have the Array automatically switch from currentLevel[0](Level 1) to currentLevel[1](Level 2) once the amount of reqXP of requiredXP[1] was met. I know a .next() exists for ArrayLists similar to rand.nextInt() but I don't know if that is exclusive to ArrayLists or if they can be used with Arrays as well. The current code you see now is a result of this lack of knowledge so any help or advice is much appreciated.



I am still new to programming and this is also only my second post in 2 years.










share|improve this question









New contributor




Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I have two issues/questions with figuring out a proper way to write this code for a player leveling system.



My First question/issue is that when this code is run it works as intended with a slight problem. public void levelUp() is being used to, "set", the level if you will (I am sure this is a sloppy way of doing this but we will get to that in the next question) while levelUpXp() is being used as the only way I currently know how to store the elements of int requiredXP to let the game know when the player has reached X amount of xp to level him up to X level. ding() is simply a notification for the player to be aware that they have increased in level.



As stated before this all works the way I am wanting it to work however upon implementing ding() after a countless amount of time trying to write multiple if statements to do what ding is doing now, I have run into an issue where curXP is NOT equal to either reqXP or requiredXP due to the amount of XP the player is gaining not always being an exact number within requiredXP. While I already assumed this would happen, it is now resulting in either a level up notification not being sent because curXP == requiredXP[x] is not always the case if the amount of xp needed to level is 17 but after killing an enemy the player goes from 15xp to 18xp or curXP <= requiredXP[x] causing the level up notification to be constantly sent until that player reaches their next level in which the notification is still sent however with a new level attached.



Second question will be below code.



Here is my player class and methods:



public class Player extends Creature {

int health = 100;
int maxHealth = 100;
int attackDamage = 25;
int numHealthPotions = 3;
int healthPotHeal = 30;
int curXP = 0;
int level = 0;
int reqXP = 0;

int currentLevel = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int requiredXP = { 0, 6, 17, 36, 65, 105, 158, 224, 305, 402 };

public void levelUp() {

if (curXP == requiredXP[0]) {
level = currentLevel[0];
} else if (curXP == requiredXP[1]) {
level = currentLevel[1];
} else if (curXP == requiredXP[2]) {
level = currentLevel[2];
} else if (curXP == requiredXP[3]) {
level = currentLevel[3];
} else if (curXP == requiredXP[4]) {
level = currentLevel[4];
} else if (curXP == requiredXP[5]) {
level = currentLevel[5];
} else if (curXP == requiredXP[6]) {
level = currentLevel[6];
} else if (curXP == requiredXP[7]) {
level = currentLevel[7];
} else if (curXP == requiredXP[8]) {
level = currentLevel[8];
} else if (curXP == requiredXP[9]) {
level = currentLevel[9];
}
}

public void levelUpXp() {

if (level == currentLevel[0]) {
reqXP = requiredXP[0];
} else if (level == currentLevel[1]) {
reqXP = requiredXP[1];
} else if (level == currentLevel[2]) {
reqXP = requiredXP[2];
}else if (level == currentLevel[3]) {
reqXP = requiredXP[3];
} else if (level == currentLevel[4]) {
reqXP = requiredXP[4];
}else if (level == currentLevel[5]) {
reqXP = requiredXP[5];
} else if (level == currentLevel[6]) {
reqXP = requiredXP[6];
}else if (level == currentLevel[7]) {
reqXP = requiredXP[7];
} else if (level == currentLevel[8]) {
reqXP = requiredXP[8];
}else if(level == currentLevel[9]) {
reqXP = requiredXP[9];
}
}

public void ding() {
if(level == 2 && curXP == requiredXP[1]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 3 && curXP == requiredXP[2]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 4 && curXP == requiredXP[3]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 5 && curXP == requiredXP[4]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 6 && curXP == requiredXP[5]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 7 && curXP == requiredXP[6]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 8 && curXP == requiredXP[7]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 9 && curXP == requiredXP[8]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
} else if(level == 10 && curXP == requiredXP[9]) {
System.out.println(" #############################");
System.out.println(" # You have reached level " + level + "! # ");
System.out.println(" #############################");
}
}

public int getLevel() {
return level;
}

public void setLevel(int level) {
this.level = level;
}

public void Level() {
int maxLevel = 200;
int maxLevelXP = 1000000;
for (int currentLevel = 1; currentLevel < maxLevel; currentLevel += 1) {
float x = currentLevel / (float) maxLevel;
double y = Math.pow(x, 2.61);
int requiredXP = (int) (y * maxLevelXP);
System.out.println("Level " + currentLevel + " XP: " + requiredXP);
}

}


Here is game class where ding and other previously mentioned Methods are used:



GAME: while (running) {

p.levelUp();
p.levelUpXp();
System.out.println("-----------------------------------------------");

String enemy = e.enemies[rand.nextInt(e.enemies.length)];
int enemyHealth = e.enemyHealth;
int preLevel = 1;
int curLevel = preLevel + 1;

System.out.println("t# " + enemy + " has appeared! #n");

while (enemyHealth > 0) {

System.out.println(
"t" + userName + "nt HP: " + p.health + " Level: " + p.level + " Exp: " + p.curXP + "nt" + "nt" + enemy + "nt HP: " + enemyHealth);
System.out.println("nt What would you like to do?");
System.out.println("t 1. Attack");
System.out.println("t 2. Drink health potion");
System.out.println("t 3. Run!");

String input = in.nextLine();
if (input.equals("1")) {
int damageDealt = rand.nextInt(p.attackDamage);
int damageTaken = rand.nextInt(e.maxEnemyAD);

enemyHealth -= damageDealt;
p.health -= damageTaken;
if (damageDealt > 0) {
System.out.println("t> You strike the " + enemy + " for " + damageDealt + " damage!");
} else if (damageDealt < 1) {
System.out.println("t> You attempt to hit the " + enemy + " but miss!");
}
if (damageTaken > 0) {
System.out.println("t> The " + enemy + " retaliates! You take " + damageTaken + " damage!");
} else if (damageTaken < 1) {
System.out.println("t> The " + enemy + " retaliates but misses!");
}
if (p.health < 1) {
System.out.println(
"t ##################" + "nt # You Have Died! #" + "nt ##################");
break;
}

} else if (input.equals("2")) {
if (p.numHealthPotions > 0 && p.health != p.maxHealth) {
p.health += p.healthPotHeal;

p.numHealthPotions--;
if (p.health > p.maxHealth) {
p.health = p.maxHealth;
}

System.out.println("t> You drink the Health Potion!" + "nt> You now have " + p.health
+ " Health!" + "nt> You now have " + p.numHealthPotions + " Health Potion(s) left!");

} else if (p.health == p.maxHealth) {
System.out.println("t> Your health is already full!");

} else if (p.numHealthPotions < 1) {
System.out.println("t You have no Health Potions left!");
}
} else if (input.equals("3")) {
System.out.println("t> You run away from the " + enemy + "!");
continue GAME;
} else {
System.out.println("t Invalid Command!");
}
}
if (p.health < 1) {
System.out.println("You fought bravely but in the end, you're just another corpse...");
break;
}

System.out.println("-----------------------------------------------");

if (enemyHealth < 1) {
p.curXP += 3;
p.levelUp();
p.levelUpXp();

}

System.out.println(" # The " + enemy + " was defeated! # ");
System.out.println(" # You have " + p.health + " HP left! # ");
System.out.println(" # You have gained " + e.xpGive + " xp! # ");
p.ding();
if (rand.nextInt(100) < e.dropChance) {
p.numHealthPotions++;
System.out.println(" # The " + enemy + " droppped a Health Potion! # ");
System.out.println(" # You now have " + p.numHealthPotions + " Health Potion(s)! # ");
}

System.out.println("-----------------------------------------------");
System.out.println("tWhat would you like to do?");
System.out.println("t1. Press on!");
System.out.println("t2. Get me out of here!");

String input = in.nextLine();

while (!input.equals("1") && !input.equals("2")) {
System.out.println("tInvalid Command!");
input = in.nextLine();

}

if (input.equals("1")) {
System.out.println("t> You march on, deeper into the Dungeon!");

} else if (input.equals("2")) {
System.out.println(
"tYou exit the dungeon. Battered and bruised as you may be, you live to fight another day!");
break;
}
}

}


Now my second and last question is with my Player class and more specifically the way my levelUp(), levelUpXp() and ding() Methods are written. There has to be a better way right? I have read Javadocs, watched tutorials, read physical books and I have even done courses on sites like Codecademy and just can't seem to wrap my head around this.



My initial goal was to have the Array automatically switch from currentLevel[0](Level 1) to currentLevel[1](Level 2) once the amount of reqXP of requiredXP[1] was met. I know a .next() exists for ArrayLists similar to rand.nextInt() but I don't know if that is exclusive to ArrayLists or if they can be used with Arrays as well. The current code you see now is a result of this lack of knowledge so any help or advice is much appreciated.



I am still new to programming and this is also only my second post in 2 years.







java beginner role-playing-game






share|improve this question









New contributor




Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 13 hours ago









200_success

127k15148411




127k15148411






New contributor




Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 19 hours ago









Brian Craycraft

333




333




New contributor




Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Brian Craycraft is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.








  • 2




    It may be worth adding the definition of Creature if that will enable reviewers to compile and run the code ourselves. Don't worry about including "too much" code - that's unlikely to be a problem here (we're very different to Stack Overflow in that respect!).
    – Toby Speight
    19 hours ago














  • 2




    It may be worth adding the definition of Creature if that will enable reviewers to compile and run the code ourselves. Don't worry about including "too much" code - that's unlikely to be a problem here (we're very different to Stack Overflow in that respect!).
    – Toby Speight
    19 hours ago








2




2




It may be worth adding the definition of Creature if that will enable reviewers to compile and run the code ourselves. Don't worry about including "too much" code - that's unlikely to be a problem here (we're very different to Stack Overflow in that respect!).
– Toby Speight
19 hours ago




It may be worth adding the definition of Creature if that will enable reviewers to compile and run the code ourselves. Don't worry about including "too much" code - that's unlikely to be a problem here (we're very different to Stack Overflow in that respect!).
– Toby Speight
19 hours ago










3 Answers
3






active

oldest

votes

















up vote
4
down vote



accepted










I'm not sure if it is ideal to keep the logic on how much XP is needed for a level up inside the player class itself. Usually these are properties kept outside and feed into the logic. While there are certain designs that consider the player to be a data class with some external manager performing modifications like the levleUp and other routines on the player object, this somehow violates OOP principles where state should be encapsulated in the class and method invocations used to alter the state internally.



By that, the first thing which comes to my mind on analyzing your code is, that you are actually performing a mapping between XP and the level. Your current solution creates such a mapping per player. If you have a system with hundreds of players this mapping would probably create a lot of duplicate data unless you want a per-player solution, which also would not work with the current approach. This therefore is a strong candidate for refactoring.



You might use an implementation of Map that you can leverage to see how many XP are required for a player to gain a level-up. As mention before, ideally you sepcify such a mapping outside of the code and then only read in the configuration on application start. This allows you to tweak your application later on without having to recompile the whole code. As the levels are furthermore progressing you only need to store the XP required per level.



You could i.e. have an xpPerLevel.txt file with the following content:



6
17
36
...


which you could read in with a method like below:



public Map<Integer, Integer> loadXpPerLevel() {
Map<Integer, Integer> xpPerLevel = new LinkedHashMap<>();
int level = 1;
try (Stream<String> lines = Files.lines(Paths.get("xpPerLevel.txt"))) {
lines.forEach(line -> xpPerLevel.put(level++, Integer.valueOf(line));
}
return xpPerLevel;
}


Ideally this code sits outside of the Player class and only the xpPerLevel map is injected to the player through its constructor. This allows certain different XP-level settings for different kinds of players, i.e. premium players get a different map injected than normal players. Via a strategy pattern this can be customized quite easily.



Next in your levelUp method you perform a check for the currentXP against the requiredXP. In order for a level up to kick in the player has tho have exactly the same amount of XP as required. If s/he has more though, no level up would occur. With the changes proposed from above the levelUp() method can be refactored as follows, which was renamed to checkCurrentXP to give it a bit more clarity:



private void checkCurrentXP() {
Integer xpRequired = null;
do {
xpRequired = xpPerLevel.get(curLevel);
if (null != xpRequired) {
if (curXP >= xpRequired) {
performLevelUp();
}
}
} while (xpRequired == null || curXP < xpRequired);
}


This method simply retrieves the XP required for a level up based on the user's current level and checks whether the user already acquired more XP than needed for a level up and in that case invokes the performLevelUp() method, which is just a renamed version of ding(). As the level up is XP driven it may occur that a user acquired more XP than actually needed for a single level up in which cace the proposed soultion automatically would perform a second, third, ... level up as well.



Note that I started the xpPerLevel.txt by the XP requirements needed to reach level 2 as the current logic would initially bump a player to level 2 automatically as 0 XP are required to reach that level. On applying these changes you basically only need to store the xpPerLevel map, the current level of the user as well as the gained XP on the user.



As before with the xpPerLevel map, ding() or performLevelUp(), as I renamed it to, are also good candidates for a strategy pattern that allows you to define different level up behaviors for different players which can be changed during runtime i.e. to see during test time which level-up logic is more suitable if multiple candidates may exist (i.e. one strategy might reset the XP of a user after a level up was performed while an other just builds up on the old amount. Yet another strategy might refill the player's HP back to maximum and so forth). The method itself has also a lot of duplicate code, which you don't really need as you don't do anything differently from level to leve. It is therefore a strong candidate for refactoring as well:



private void performLevelUp() {
System.out.println(" #############################");
System.out.println(" # You have reached level " + (++curLevel) + "! # ");
System.out.println(" #############################");
}


As the level of a player is dependent on the XP earned I'd remove the setLevel(int level) method completly. Instead I'd provide a method that allows a player to gain XP:



public void awardXP(int xpAmount) {
curXP += xpAmount;
checkCurrentXP();
}


Right after you awarded the player with a number of XP it will automatically check the whether it lead to a level up or not and thus update the user's level accordingly.



As hopefully can be seen, certain state, such as the player's level and curXP, is encapsulated in the player's object and through invoking methods on that player's object that state gets manipulated. This is in essence what object-oriented programming should be.



Whithin your game loop you only need to award the player a certain amount of XP which will trigger internal state changes automatically. So there is no need to invoke p.levelUp() and p.levelUpXp() within your game loop.



As your main-loop is basically a typical console application reading in some user input and performing some task on the input applying a command pattern here does make the code a bit more readable. You basically refactor out your if/if-else segments into own little classes that are only focusing on that particular task. Your loop is basically responsible for too many things, which violates a bit the single responsibility principle which is further a part of SOLID.



Basically what you can do to improve your code here is, i.e. introduce a new EventLoop class where you can register a couple of commands with it. The commands are just implementations of either an interface or abstract base class. The EventLoop class will store the commands within a map for a certain input command. Upon user-input the input will be parsed and the respective command invoked, if available. In Java this may look something like this:



public class EventLoop {

private final Map<String, ConsoleCommand> registeredCommands;

EventLoop() {
reisteredCommands = new ConcurrentSkipListMap<>();
}

public void registerCommand(String command, ConsoleCommand action) {
if (action == null) {
throws new IllegalArgumentException("No defined action for command " + command);
}
registeredCommands.put(command, action);
}

public vod unregisterCommand(String command) {
registeredCommands.remove(command);
}

public void update(GameState state) {
Scanner scanner = new Scanner(System.in);

while(GameState.FINISHED != state.currentState()) {
printHelp(state);

String input = scanner.nextLine();
// String input = validate(input);
ConsoleCommand action = registeredCommands.get(input);
if (null != action) {
action.perform(state);
} else {
System.err.println("t Invalid Command!");
}
}
scanner.close();
}

private void printHelp(GameState state) {
System.out.println(
getPlayerInfo(state.getPlayer())
+ "ntnt"
+ getEnemyInfo(state.getEnemy()));
System.out.println("nt What would you like to do?");
for(String cmd : registeredCommands.keySet()) {
ConsoleCommand action = registeredCommands.get(cmd);
System.out.println("t " + cmd + ". " + action.printHelp());
}
}

private String getPlayerInfo(Player player) {
return "t" + player.getName() + "nt HP: " player.getHealth() + " Level: " + player.getCurrentLevel() + " Exp: " + player.getCurrentXP();
}

private String getEnemyInfo(Creature enemy) {
return "t" + enemy.getName() + "nt HP: " +enemy.getHealth() + " Level: " + enemy.getCurrentLevel();
}
}


A ConsoleCommand defines now the concrete action to perform once invoked. As can be seen from the EventLoop two methods are at least necessary:



public interface ConsoleCommand {
void perform(GameState state);
String printHelp();
}


If you later on decide that you want to pass certain properties to a command, you should change the interface to an abstract class and implement a method like parseCommand(String input) in it so that inheriting classes automatically have access to the already parsed values.



A concrete implementation of the AttackCommand may now look like this



public class AttackCommand implements ConsoleCommand {

private Random rand = new SecureRandom();

@Override
public String printHelp() {
return "Attack"
}

@Override
public void perform(GameState state) {

Player player = state.getPlayer();
Creature enemy = state.getEnemy();
int damageDealt = rand.nextInt(player.getAttackDamage());
int damageTaken = rand.nextInt(enemy.getAttackDamage());

player.takeDamage(damageTaken);
enemy.takeDamage(damageDealt);

if (damageDealt > 0) {
System.out.println("t> You strike the "
+ enemy.getName() + " for " + damageDealt + " damage!");
} else if (damageDealt < 1) {
System.out.println("t> You attempt to hit the "
+ enemy.getName() + " but miss!");
}
if (damageTaken > 0) {
System.out.println("t> The " + enemy.getName()
+ " retaliates! You take " + damageTaken + " damage!");
} else if (damageTaken < 1) {
System.out.println("t> The " + enemy.getName()
+ " retaliates but misses!");
}

if (player.getHealth() < 1) {
System.out.println("t ##################"
+ "nt # You Have Died! #"
+ "nt ##################");
state.updateGameState(GameState.FINISHED);
}

if (enemy.getHealth() < 1) {
System.out.println("t Enemy " + enemy.getName()
+ " was crushed by your mighty strikes. You have been awarded with "
+ enemy.getAmountOfXPWorthForKilling() + " XP";
player.awardXP(enemy.getAmountOfXPWorthForKilling());
}
}
}


I hope that by the presented examples it does make sense to you, that by separating the actual actions into commands the general code becomes less cluttered and therefore easily readable and understandable. You further can test an action more easily as well.



As you might have noticed that I also changed a couple of things throughout this example. I like to have names of methods that actually do the same things on the object to be the same. So player.takeDamage(int) does basically the same as enemy.takeDamage(int). Refactoring this behavior to a parent class would make sense here as everything has to die once it has no HP left. The difference here is though that if a player dies the game is over compared to when a creature dies the player is awarded with XP.



Further, it is IMO a good design to refactor the overall GameState out of the actual main-loop so that it can get passed around more easily. This allows the action commands to update the game state if needed without having to perform a callback into the main-loop.






share|improve this answer






























    up vote
    3
    down vote













    Welcome on Code Review.



    Your game's loop is way too long. You have to separate your code into functions that constitute logical units.



    E.g., taking and dealing of damages have to be moved into two separate function dealDamage and takeDamage. The whole loop of the battle into a function doBattle. Spawn of enemy have to be wrap into a spawnEnemy, inside which you place a call to doBattle. Etc.



    I'm afraid to tell you that all your logic seem to be weird.



    Instead of increasing the level and Xp in a clumsy way, just remove ding, levelUp and levelUpXp, replace:



    p.curXP += 3;
    p.levelUp();
    p.levelUpXp();


    by



    p.addXp(3);


    and add this function :



    public void addXp(int reward) {
    curXP += reward;
    while (level < requiredXP.length && requiredXP[level] < curXP) {
    ++level;
    System.out.println(" #############################");
    System.out.println(" # You have reached level " + level + "! # ");
    System.out.println(" #############################");
    }
    }


    By doing that, you can get rig or reqXP and currentLevel too.



    Your max level become the size of requiredXP (which you could rename experienceGap).



    You can populate this array using the logic from Level() (adding the values to the array instead of printing them)



    If you have some trouble to understand how game mechanics works, dive into the GameDev site.






    share|improve this answer




























      up vote
      2
      down vote













      This could be shortened by looping through requiredXP until you reach a value that curXP is equal to or using a switch statement. The same could be done in levelUpXp().

      You are right that using and ArrayList<Integer> could simplify things as well. Possibly look into a list or queue data type.

      You could also call ding() from inside levelUp() or levelUpXp() instead of the while loop or set a flag variable (eg. newLevel) that it tests for to skip the test conditions inside.






      share|improve this answer








      New contributor




      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.














      • 1




        Other than that, nice job on the code!
        – LukeDev
        15 hours ago











      Your Answer





      StackExchange.ifUsing("editor", function () {
      return StackExchange.using("mathjaxEditing", function () {
      StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
      StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
      });
      });
      }, "mathjax-editing");

      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "196"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });






      Brian Craycraft is a new contributor. Be nice, and check out our Code of Conduct.










       

      draft saved


      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208278%2fsimple-text-based-rpg-leveling-system%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      4
      down vote



      accepted










      I'm not sure if it is ideal to keep the logic on how much XP is needed for a level up inside the player class itself. Usually these are properties kept outside and feed into the logic. While there are certain designs that consider the player to be a data class with some external manager performing modifications like the levleUp and other routines on the player object, this somehow violates OOP principles where state should be encapsulated in the class and method invocations used to alter the state internally.



      By that, the first thing which comes to my mind on analyzing your code is, that you are actually performing a mapping between XP and the level. Your current solution creates such a mapping per player. If you have a system with hundreds of players this mapping would probably create a lot of duplicate data unless you want a per-player solution, which also would not work with the current approach. This therefore is a strong candidate for refactoring.



      You might use an implementation of Map that you can leverage to see how many XP are required for a player to gain a level-up. As mention before, ideally you sepcify such a mapping outside of the code and then only read in the configuration on application start. This allows you to tweak your application later on without having to recompile the whole code. As the levels are furthermore progressing you only need to store the XP required per level.



      You could i.e. have an xpPerLevel.txt file with the following content:



      6
      17
      36
      ...


      which you could read in with a method like below:



      public Map<Integer, Integer> loadXpPerLevel() {
      Map<Integer, Integer> xpPerLevel = new LinkedHashMap<>();
      int level = 1;
      try (Stream<String> lines = Files.lines(Paths.get("xpPerLevel.txt"))) {
      lines.forEach(line -> xpPerLevel.put(level++, Integer.valueOf(line));
      }
      return xpPerLevel;
      }


      Ideally this code sits outside of the Player class and only the xpPerLevel map is injected to the player through its constructor. This allows certain different XP-level settings for different kinds of players, i.e. premium players get a different map injected than normal players. Via a strategy pattern this can be customized quite easily.



      Next in your levelUp method you perform a check for the currentXP against the requiredXP. In order for a level up to kick in the player has tho have exactly the same amount of XP as required. If s/he has more though, no level up would occur. With the changes proposed from above the levelUp() method can be refactored as follows, which was renamed to checkCurrentXP to give it a bit more clarity:



      private void checkCurrentXP() {
      Integer xpRequired = null;
      do {
      xpRequired = xpPerLevel.get(curLevel);
      if (null != xpRequired) {
      if (curXP >= xpRequired) {
      performLevelUp();
      }
      }
      } while (xpRequired == null || curXP < xpRequired);
      }


      This method simply retrieves the XP required for a level up based on the user's current level and checks whether the user already acquired more XP than needed for a level up and in that case invokes the performLevelUp() method, which is just a renamed version of ding(). As the level up is XP driven it may occur that a user acquired more XP than actually needed for a single level up in which cace the proposed soultion automatically would perform a second, third, ... level up as well.



      Note that I started the xpPerLevel.txt by the XP requirements needed to reach level 2 as the current logic would initially bump a player to level 2 automatically as 0 XP are required to reach that level. On applying these changes you basically only need to store the xpPerLevel map, the current level of the user as well as the gained XP on the user.



      As before with the xpPerLevel map, ding() or performLevelUp(), as I renamed it to, are also good candidates for a strategy pattern that allows you to define different level up behaviors for different players which can be changed during runtime i.e. to see during test time which level-up logic is more suitable if multiple candidates may exist (i.e. one strategy might reset the XP of a user after a level up was performed while an other just builds up on the old amount. Yet another strategy might refill the player's HP back to maximum and so forth). The method itself has also a lot of duplicate code, which you don't really need as you don't do anything differently from level to leve. It is therefore a strong candidate for refactoring as well:



      private void performLevelUp() {
      System.out.println(" #############################");
      System.out.println(" # You have reached level " + (++curLevel) + "! # ");
      System.out.println(" #############################");
      }


      As the level of a player is dependent on the XP earned I'd remove the setLevel(int level) method completly. Instead I'd provide a method that allows a player to gain XP:



      public void awardXP(int xpAmount) {
      curXP += xpAmount;
      checkCurrentXP();
      }


      Right after you awarded the player with a number of XP it will automatically check the whether it lead to a level up or not and thus update the user's level accordingly.



      As hopefully can be seen, certain state, such as the player's level and curXP, is encapsulated in the player's object and through invoking methods on that player's object that state gets manipulated. This is in essence what object-oriented programming should be.



      Whithin your game loop you only need to award the player a certain amount of XP which will trigger internal state changes automatically. So there is no need to invoke p.levelUp() and p.levelUpXp() within your game loop.



      As your main-loop is basically a typical console application reading in some user input and performing some task on the input applying a command pattern here does make the code a bit more readable. You basically refactor out your if/if-else segments into own little classes that are only focusing on that particular task. Your loop is basically responsible for too many things, which violates a bit the single responsibility principle which is further a part of SOLID.



      Basically what you can do to improve your code here is, i.e. introduce a new EventLoop class where you can register a couple of commands with it. The commands are just implementations of either an interface or abstract base class. The EventLoop class will store the commands within a map for a certain input command. Upon user-input the input will be parsed and the respective command invoked, if available. In Java this may look something like this:



      public class EventLoop {

      private final Map<String, ConsoleCommand> registeredCommands;

      EventLoop() {
      reisteredCommands = new ConcurrentSkipListMap<>();
      }

      public void registerCommand(String command, ConsoleCommand action) {
      if (action == null) {
      throws new IllegalArgumentException("No defined action for command " + command);
      }
      registeredCommands.put(command, action);
      }

      public vod unregisterCommand(String command) {
      registeredCommands.remove(command);
      }

      public void update(GameState state) {
      Scanner scanner = new Scanner(System.in);

      while(GameState.FINISHED != state.currentState()) {
      printHelp(state);

      String input = scanner.nextLine();
      // String input = validate(input);
      ConsoleCommand action = registeredCommands.get(input);
      if (null != action) {
      action.perform(state);
      } else {
      System.err.println("t Invalid Command!");
      }
      }
      scanner.close();
      }

      private void printHelp(GameState state) {
      System.out.println(
      getPlayerInfo(state.getPlayer())
      + "ntnt"
      + getEnemyInfo(state.getEnemy()));
      System.out.println("nt What would you like to do?");
      for(String cmd : registeredCommands.keySet()) {
      ConsoleCommand action = registeredCommands.get(cmd);
      System.out.println("t " + cmd + ". " + action.printHelp());
      }
      }

      private String getPlayerInfo(Player player) {
      return "t" + player.getName() + "nt HP: " player.getHealth() + " Level: " + player.getCurrentLevel() + " Exp: " + player.getCurrentXP();
      }

      private String getEnemyInfo(Creature enemy) {
      return "t" + enemy.getName() + "nt HP: " +enemy.getHealth() + " Level: " + enemy.getCurrentLevel();
      }
      }


      A ConsoleCommand defines now the concrete action to perform once invoked. As can be seen from the EventLoop two methods are at least necessary:



      public interface ConsoleCommand {
      void perform(GameState state);
      String printHelp();
      }


      If you later on decide that you want to pass certain properties to a command, you should change the interface to an abstract class and implement a method like parseCommand(String input) in it so that inheriting classes automatically have access to the already parsed values.



      A concrete implementation of the AttackCommand may now look like this



      public class AttackCommand implements ConsoleCommand {

      private Random rand = new SecureRandom();

      @Override
      public String printHelp() {
      return "Attack"
      }

      @Override
      public void perform(GameState state) {

      Player player = state.getPlayer();
      Creature enemy = state.getEnemy();
      int damageDealt = rand.nextInt(player.getAttackDamage());
      int damageTaken = rand.nextInt(enemy.getAttackDamage());

      player.takeDamage(damageTaken);
      enemy.takeDamage(damageDealt);

      if (damageDealt > 0) {
      System.out.println("t> You strike the "
      + enemy.getName() + " for " + damageDealt + " damage!");
      } else if (damageDealt < 1) {
      System.out.println("t> You attempt to hit the "
      + enemy.getName() + " but miss!");
      }
      if (damageTaken > 0) {
      System.out.println("t> The " + enemy.getName()
      + " retaliates! You take " + damageTaken + " damage!");
      } else if (damageTaken < 1) {
      System.out.println("t> The " + enemy.getName()
      + " retaliates but misses!");
      }

      if (player.getHealth() < 1) {
      System.out.println("t ##################"
      + "nt # You Have Died! #"
      + "nt ##################");
      state.updateGameState(GameState.FINISHED);
      }

      if (enemy.getHealth() < 1) {
      System.out.println("t Enemy " + enemy.getName()
      + " was crushed by your mighty strikes. You have been awarded with "
      + enemy.getAmountOfXPWorthForKilling() + " XP";
      player.awardXP(enemy.getAmountOfXPWorthForKilling());
      }
      }
      }


      I hope that by the presented examples it does make sense to you, that by separating the actual actions into commands the general code becomes less cluttered and therefore easily readable and understandable. You further can test an action more easily as well.



      As you might have noticed that I also changed a couple of things throughout this example. I like to have names of methods that actually do the same things on the object to be the same. So player.takeDamage(int) does basically the same as enemy.takeDamage(int). Refactoring this behavior to a parent class would make sense here as everything has to die once it has no HP left. The difference here is though that if a player dies the game is over compared to when a creature dies the player is awarded with XP.



      Further, it is IMO a good design to refactor the overall GameState out of the actual main-loop so that it can get passed around more easily. This allows the action commands to update the game state if needed without having to perform a callback into the main-loop.






      share|improve this answer



























        up vote
        4
        down vote



        accepted










        I'm not sure if it is ideal to keep the logic on how much XP is needed for a level up inside the player class itself. Usually these are properties kept outside and feed into the logic. While there are certain designs that consider the player to be a data class with some external manager performing modifications like the levleUp and other routines on the player object, this somehow violates OOP principles where state should be encapsulated in the class and method invocations used to alter the state internally.



        By that, the first thing which comes to my mind on analyzing your code is, that you are actually performing a mapping between XP and the level. Your current solution creates such a mapping per player. If you have a system with hundreds of players this mapping would probably create a lot of duplicate data unless you want a per-player solution, which also would not work with the current approach. This therefore is a strong candidate for refactoring.



        You might use an implementation of Map that you can leverage to see how many XP are required for a player to gain a level-up. As mention before, ideally you sepcify such a mapping outside of the code and then only read in the configuration on application start. This allows you to tweak your application later on without having to recompile the whole code. As the levels are furthermore progressing you only need to store the XP required per level.



        You could i.e. have an xpPerLevel.txt file with the following content:



        6
        17
        36
        ...


        which you could read in with a method like below:



        public Map<Integer, Integer> loadXpPerLevel() {
        Map<Integer, Integer> xpPerLevel = new LinkedHashMap<>();
        int level = 1;
        try (Stream<String> lines = Files.lines(Paths.get("xpPerLevel.txt"))) {
        lines.forEach(line -> xpPerLevel.put(level++, Integer.valueOf(line));
        }
        return xpPerLevel;
        }


        Ideally this code sits outside of the Player class and only the xpPerLevel map is injected to the player through its constructor. This allows certain different XP-level settings for different kinds of players, i.e. premium players get a different map injected than normal players. Via a strategy pattern this can be customized quite easily.



        Next in your levelUp method you perform a check for the currentXP against the requiredXP. In order for a level up to kick in the player has tho have exactly the same amount of XP as required. If s/he has more though, no level up would occur. With the changes proposed from above the levelUp() method can be refactored as follows, which was renamed to checkCurrentXP to give it a bit more clarity:



        private void checkCurrentXP() {
        Integer xpRequired = null;
        do {
        xpRequired = xpPerLevel.get(curLevel);
        if (null != xpRequired) {
        if (curXP >= xpRequired) {
        performLevelUp();
        }
        }
        } while (xpRequired == null || curXP < xpRequired);
        }


        This method simply retrieves the XP required for a level up based on the user's current level and checks whether the user already acquired more XP than needed for a level up and in that case invokes the performLevelUp() method, which is just a renamed version of ding(). As the level up is XP driven it may occur that a user acquired more XP than actually needed for a single level up in which cace the proposed soultion automatically would perform a second, third, ... level up as well.



        Note that I started the xpPerLevel.txt by the XP requirements needed to reach level 2 as the current logic would initially bump a player to level 2 automatically as 0 XP are required to reach that level. On applying these changes you basically only need to store the xpPerLevel map, the current level of the user as well as the gained XP on the user.



        As before with the xpPerLevel map, ding() or performLevelUp(), as I renamed it to, are also good candidates for a strategy pattern that allows you to define different level up behaviors for different players which can be changed during runtime i.e. to see during test time which level-up logic is more suitable if multiple candidates may exist (i.e. one strategy might reset the XP of a user after a level up was performed while an other just builds up on the old amount. Yet another strategy might refill the player's HP back to maximum and so forth). The method itself has also a lot of duplicate code, which you don't really need as you don't do anything differently from level to leve. It is therefore a strong candidate for refactoring as well:



        private void performLevelUp() {
        System.out.println(" #############################");
        System.out.println(" # You have reached level " + (++curLevel) + "! # ");
        System.out.println(" #############################");
        }


        As the level of a player is dependent on the XP earned I'd remove the setLevel(int level) method completly. Instead I'd provide a method that allows a player to gain XP:



        public void awardXP(int xpAmount) {
        curXP += xpAmount;
        checkCurrentXP();
        }


        Right after you awarded the player with a number of XP it will automatically check the whether it lead to a level up or not and thus update the user's level accordingly.



        As hopefully can be seen, certain state, such as the player's level and curXP, is encapsulated in the player's object and through invoking methods on that player's object that state gets manipulated. This is in essence what object-oriented programming should be.



        Whithin your game loop you only need to award the player a certain amount of XP which will trigger internal state changes automatically. So there is no need to invoke p.levelUp() and p.levelUpXp() within your game loop.



        As your main-loop is basically a typical console application reading in some user input and performing some task on the input applying a command pattern here does make the code a bit more readable. You basically refactor out your if/if-else segments into own little classes that are only focusing on that particular task. Your loop is basically responsible for too many things, which violates a bit the single responsibility principle which is further a part of SOLID.



        Basically what you can do to improve your code here is, i.e. introduce a new EventLoop class where you can register a couple of commands with it. The commands are just implementations of either an interface or abstract base class. The EventLoop class will store the commands within a map for a certain input command. Upon user-input the input will be parsed and the respective command invoked, if available. In Java this may look something like this:



        public class EventLoop {

        private final Map<String, ConsoleCommand> registeredCommands;

        EventLoop() {
        reisteredCommands = new ConcurrentSkipListMap<>();
        }

        public void registerCommand(String command, ConsoleCommand action) {
        if (action == null) {
        throws new IllegalArgumentException("No defined action for command " + command);
        }
        registeredCommands.put(command, action);
        }

        public vod unregisterCommand(String command) {
        registeredCommands.remove(command);
        }

        public void update(GameState state) {
        Scanner scanner = new Scanner(System.in);

        while(GameState.FINISHED != state.currentState()) {
        printHelp(state);

        String input = scanner.nextLine();
        // String input = validate(input);
        ConsoleCommand action = registeredCommands.get(input);
        if (null != action) {
        action.perform(state);
        } else {
        System.err.println("t Invalid Command!");
        }
        }
        scanner.close();
        }

        private void printHelp(GameState state) {
        System.out.println(
        getPlayerInfo(state.getPlayer())
        + "ntnt"
        + getEnemyInfo(state.getEnemy()));
        System.out.println("nt What would you like to do?");
        for(String cmd : registeredCommands.keySet()) {
        ConsoleCommand action = registeredCommands.get(cmd);
        System.out.println("t " + cmd + ". " + action.printHelp());
        }
        }

        private String getPlayerInfo(Player player) {
        return "t" + player.getName() + "nt HP: " player.getHealth() + " Level: " + player.getCurrentLevel() + " Exp: " + player.getCurrentXP();
        }

        private String getEnemyInfo(Creature enemy) {
        return "t" + enemy.getName() + "nt HP: " +enemy.getHealth() + " Level: " + enemy.getCurrentLevel();
        }
        }


        A ConsoleCommand defines now the concrete action to perform once invoked. As can be seen from the EventLoop two methods are at least necessary:



        public interface ConsoleCommand {
        void perform(GameState state);
        String printHelp();
        }


        If you later on decide that you want to pass certain properties to a command, you should change the interface to an abstract class and implement a method like parseCommand(String input) in it so that inheriting classes automatically have access to the already parsed values.



        A concrete implementation of the AttackCommand may now look like this



        public class AttackCommand implements ConsoleCommand {

        private Random rand = new SecureRandom();

        @Override
        public String printHelp() {
        return "Attack"
        }

        @Override
        public void perform(GameState state) {

        Player player = state.getPlayer();
        Creature enemy = state.getEnemy();
        int damageDealt = rand.nextInt(player.getAttackDamage());
        int damageTaken = rand.nextInt(enemy.getAttackDamage());

        player.takeDamage(damageTaken);
        enemy.takeDamage(damageDealt);

        if (damageDealt > 0) {
        System.out.println("t> You strike the "
        + enemy.getName() + " for " + damageDealt + " damage!");
        } else if (damageDealt < 1) {
        System.out.println("t> You attempt to hit the "
        + enemy.getName() + " but miss!");
        }
        if (damageTaken > 0) {
        System.out.println("t> The " + enemy.getName()
        + " retaliates! You take " + damageTaken + " damage!");
        } else if (damageTaken < 1) {
        System.out.println("t> The " + enemy.getName()
        + " retaliates but misses!");
        }

        if (player.getHealth() < 1) {
        System.out.println("t ##################"
        + "nt # You Have Died! #"
        + "nt ##################");
        state.updateGameState(GameState.FINISHED);
        }

        if (enemy.getHealth() < 1) {
        System.out.println("t Enemy " + enemy.getName()
        + " was crushed by your mighty strikes. You have been awarded with "
        + enemy.getAmountOfXPWorthForKilling() + " XP";
        player.awardXP(enemy.getAmountOfXPWorthForKilling());
        }
        }
        }


        I hope that by the presented examples it does make sense to you, that by separating the actual actions into commands the general code becomes less cluttered and therefore easily readable and understandable. You further can test an action more easily as well.



        As you might have noticed that I also changed a couple of things throughout this example. I like to have names of methods that actually do the same things on the object to be the same. So player.takeDamage(int) does basically the same as enemy.takeDamage(int). Refactoring this behavior to a parent class would make sense here as everything has to die once it has no HP left. The difference here is though that if a player dies the game is over compared to when a creature dies the player is awarded with XP.



        Further, it is IMO a good design to refactor the overall GameState out of the actual main-loop so that it can get passed around more easily. This allows the action commands to update the game state if needed without having to perform a callback into the main-loop.






        share|improve this answer

























          up vote
          4
          down vote



          accepted







          up vote
          4
          down vote



          accepted






          I'm not sure if it is ideal to keep the logic on how much XP is needed for a level up inside the player class itself. Usually these are properties kept outside and feed into the logic. While there are certain designs that consider the player to be a data class with some external manager performing modifications like the levleUp and other routines on the player object, this somehow violates OOP principles where state should be encapsulated in the class and method invocations used to alter the state internally.



          By that, the first thing which comes to my mind on analyzing your code is, that you are actually performing a mapping between XP and the level. Your current solution creates such a mapping per player. If you have a system with hundreds of players this mapping would probably create a lot of duplicate data unless you want a per-player solution, which also would not work with the current approach. This therefore is a strong candidate for refactoring.



          You might use an implementation of Map that you can leverage to see how many XP are required for a player to gain a level-up. As mention before, ideally you sepcify such a mapping outside of the code and then only read in the configuration on application start. This allows you to tweak your application later on without having to recompile the whole code. As the levels are furthermore progressing you only need to store the XP required per level.



          You could i.e. have an xpPerLevel.txt file with the following content:



          6
          17
          36
          ...


          which you could read in with a method like below:



          public Map<Integer, Integer> loadXpPerLevel() {
          Map<Integer, Integer> xpPerLevel = new LinkedHashMap<>();
          int level = 1;
          try (Stream<String> lines = Files.lines(Paths.get("xpPerLevel.txt"))) {
          lines.forEach(line -> xpPerLevel.put(level++, Integer.valueOf(line));
          }
          return xpPerLevel;
          }


          Ideally this code sits outside of the Player class and only the xpPerLevel map is injected to the player through its constructor. This allows certain different XP-level settings for different kinds of players, i.e. premium players get a different map injected than normal players. Via a strategy pattern this can be customized quite easily.



          Next in your levelUp method you perform a check for the currentXP against the requiredXP. In order for a level up to kick in the player has tho have exactly the same amount of XP as required. If s/he has more though, no level up would occur. With the changes proposed from above the levelUp() method can be refactored as follows, which was renamed to checkCurrentXP to give it a bit more clarity:



          private void checkCurrentXP() {
          Integer xpRequired = null;
          do {
          xpRequired = xpPerLevel.get(curLevel);
          if (null != xpRequired) {
          if (curXP >= xpRequired) {
          performLevelUp();
          }
          }
          } while (xpRequired == null || curXP < xpRequired);
          }


          This method simply retrieves the XP required for a level up based on the user's current level and checks whether the user already acquired more XP than needed for a level up and in that case invokes the performLevelUp() method, which is just a renamed version of ding(). As the level up is XP driven it may occur that a user acquired more XP than actually needed for a single level up in which cace the proposed soultion automatically would perform a second, third, ... level up as well.



          Note that I started the xpPerLevel.txt by the XP requirements needed to reach level 2 as the current logic would initially bump a player to level 2 automatically as 0 XP are required to reach that level. On applying these changes you basically only need to store the xpPerLevel map, the current level of the user as well as the gained XP on the user.



          As before with the xpPerLevel map, ding() or performLevelUp(), as I renamed it to, are also good candidates for a strategy pattern that allows you to define different level up behaviors for different players which can be changed during runtime i.e. to see during test time which level-up logic is more suitable if multiple candidates may exist (i.e. one strategy might reset the XP of a user after a level up was performed while an other just builds up on the old amount. Yet another strategy might refill the player's HP back to maximum and so forth). The method itself has also a lot of duplicate code, which you don't really need as you don't do anything differently from level to leve. It is therefore a strong candidate for refactoring as well:



          private void performLevelUp() {
          System.out.println(" #############################");
          System.out.println(" # You have reached level " + (++curLevel) + "! # ");
          System.out.println(" #############################");
          }


          As the level of a player is dependent on the XP earned I'd remove the setLevel(int level) method completly. Instead I'd provide a method that allows a player to gain XP:



          public void awardXP(int xpAmount) {
          curXP += xpAmount;
          checkCurrentXP();
          }


          Right after you awarded the player with a number of XP it will automatically check the whether it lead to a level up or not and thus update the user's level accordingly.



          As hopefully can be seen, certain state, such as the player's level and curXP, is encapsulated in the player's object and through invoking methods on that player's object that state gets manipulated. This is in essence what object-oriented programming should be.



          Whithin your game loop you only need to award the player a certain amount of XP which will trigger internal state changes automatically. So there is no need to invoke p.levelUp() and p.levelUpXp() within your game loop.



          As your main-loop is basically a typical console application reading in some user input and performing some task on the input applying a command pattern here does make the code a bit more readable. You basically refactor out your if/if-else segments into own little classes that are only focusing on that particular task. Your loop is basically responsible for too many things, which violates a bit the single responsibility principle which is further a part of SOLID.



          Basically what you can do to improve your code here is, i.e. introduce a new EventLoop class where you can register a couple of commands with it. The commands are just implementations of either an interface or abstract base class. The EventLoop class will store the commands within a map for a certain input command. Upon user-input the input will be parsed and the respective command invoked, if available. In Java this may look something like this:



          public class EventLoop {

          private final Map<String, ConsoleCommand> registeredCommands;

          EventLoop() {
          reisteredCommands = new ConcurrentSkipListMap<>();
          }

          public void registerCommand(String command, ConsoleCommand action) {
          if (action == null) {
          throws new IllegalArgumentException("No defined action for command " + command);
          }
          registeredCommands.put(command, action);
          }

          public vod unregisterCommand(String command) {
          registeredCommands.remove(command);
          }

          public void update(GameState state) {
          Scanner scanner = new Scanner(System.in);

          while(GameState.FINISHED != state.currentState()) {
          printHelp(state);

          String input = scanner.nextLine();
          // String input = validate(input);
          ConsoleCommand action = registeredCommands.get(input);
          if (null != action) {
          action.perform(state);
          } else {
          System.err.println("t Invalid Command!");
          }
          }
          scanner.close();
          }

          private void printHelp(GameState state) {
          System.out.println(
          getPlayerInfo(state.getPlayer())
          + "ntnt"
          + getEnemyInfo(state.getEnemy()));
          System.out.println("nt What would you like to do?");
          for(String cmd : registeredCommands.keySet()) {
          ConsoleCommand action = registeredCommands.get(cmd);
          System.out.println("t " + cmd + ". " + action.printHelp());
          }
          }

          private String getPlayerInfo(Player player) {
          return "t" + player.getName() + "nt HP: " player.getHealth() + " Level: " + player.getCurrentLevel() + " Exp: " + player.getCurrentXP();
          }

          private String getEnemyInfo(Creature enemy) {
          return "t" + enemy.getName() + "nt HP: " +enemy.getHealth() + " Level: " + enemy.getCurrentLevel();
          }
          }


          A ConsoleCommand defines now the concrete action to perform once invoked. As can be seen from the EventLoop two methods are at least necessary:



          public interface ConsoleCommand {
          void perform(GameState state);
          String printHelp();
          }


          If you later on decide that you want to pass certain properties to a command, you should change the interface to an abstract class and implement a method like parseCommand(String input) in it so that inheriting classes automatically have access to the already parsed values.



          A concrete implementation of the AttackCommand may now look like this



          public class AttackCommand implements ConsoleCommand {

          private Random rand = new SecureRandom();

          @Override
          public String printHelp() {
          return "Attack"
          }

          @Override
          public void perform(GameState state) {

          Player player = state.getPlayer();
          Creature enemy = state.getEnemy();
          int damageDealt = rand.nextInt(player.getAttackDamage());
          int damageTaken = rand.nextInt(enemy.getAttackDamage());

          player.takeDamage(damageTaken);
          enemy.takeDamage(damageDealt);

          if (damageDealt > 0) {
          System.out.println("t> You strike the "
          + enemy.getName() + " for " + damageDealt + " damage!");
          } else if (damageDealt < 1) {
          System.out.println("t> You attempt to hit the "
          + enemy.getName() + " but miss!");
          }
          if (damageTaken > 0) {
          System.out.println("t> The " + enemy.getName()
          + " retaliates! You take " + damageTaken + " damage!");
          } else if (damageTaken < 1) {
          System.out.println("t> The " + enemy.getName()
          + " retaliates but misses!");
          }

          if (player.getHealth() < 1) {
          System.out.println("t ##################"
          + "nt # You Have Died! #"
          + "nt ##################");
          state.updateGameState(GameState.FINISHED);
          }

          if (enemy.getHealth() < 1) {
          System.out.println("t Enemy " + enemy.getName()
          + " was crushed by your mighty strikes. You have been awarded with "
          + enemy.getAmountOfXPWorthForKilling() + " XP";
          player.awardXP(enemy.getAmountOfXPWorthForKilling());
          }
          }
          }


          I hope that by the presented examples it does make sense to you, that by separating the actual actions into commands the general code becomes less cluttered and therefore easily readable and understandable. You further can test an action more easily as well.



          As you might have noticed that I also changed a couple of things throughout this example. I like to have names of methods that actually do the same things on the object to be the same. So player.takeDamage(int) does basically the same as enemy.takeDamage(int). Refactoring this behavior to a parent class would make sense here as everything has to die once it has no HP left. The difference here is though that if a player dies the game is over compared to when a creature dies the player is awarded with XP.



          Further, it is IMO a good design to refactor the overall GameState out of the actual main-loop so that it can get passed around more easily. This allows the action commands to update the game state if needed without having to perform a callback into the main-loop.






          share|improve this answer














          I'm not sure if it is ideal to keep the logic on how much XP is needed for a level up inside the player class itself. Usually these are properties kept outside and feed into the logic. While there are certain designs that consider the player to be a data class with some external manager performing modifications like the levleUp and other routines on the player object, this somehow violates OOP principles where state should be encapsulated in the class and method invocations used to alter the state internally.



          By that, the first thing which comes to my mind on analyzing your code is, that you are actually performing a mapping between XP and the level. Your current solution creates such a mapping per player. If you have a system with hundreds of players this mapping would probably create a lot of duplicate data unless you want a per-player solution, which also would not work with the current approach. This therefore is a strong candidate for refactoring.



          You might use an implementation of Map that you can leverage to see how many XP are required for a player to gain a level-up. As mention before, ideally you sepcify such a mapping outside of the code and then only read in the configuration on application start. This allows you to tweak your application later on without having to recompile the whole code. As the levels are furthermore progressing you only need to store the XP required per level.



          You could i.e. have an xpPerLevel.txt file with the following content:



          6
          17
          36
          ...


          which you could read in with a method like below:



          public Map<Integer, Integer> loadXpPerLevel() {
          Map<Integer, Integer> xpPerLevel = new LinkedHashMap<>();
          int level = 1;
          try (Stream<String> lines = Files.lines(Paths.get("xpPerLevel.txt"))) {
          lines.forEach(line -> xpPerLevel.put(level++, Integer.valueOf(line));
          }
          return xpPerLevel;
          }


          Ideally this code sits outside of the Player class and only the xpPerLevel map is injected to the player through its constructor. This allows certain different XP-level settings for different kinds of players, i.e. premium players get a different map injected than normal players. Via a strategy pattern this can be customized quite easily.



          Next in your levelUp method you perform a check for the currentXP against the requiredXP. In order for a level up to kick in the player has tho have exactly the same amount of XP as required. If s/he has more though, no level up would occur. With the changes proposed from above the levelUp() method can be refactored as follows, which was renamed to checkCurrentXP to give it a bit more clarity:



          private void checkCurrentXP() {
          Integer xpRequired = null;
          do {
          xpRequired = xpPerLevel.get(curLevel);
          if (null != xpRequired) {
          if (curXP >= xpRequired) {
          performLevelUp();
          }
          }
          } while (xpRequired == null || curXP < xpRequired);
          }


          This method simply retrieves the XP required for a level up based on the user's current level and checks whether the user already acquired more XP than needed for a level up and in that case invokes the performLevelUp() method, which is just a renamed version of ding(). As the level up is XP driven it may occur that a user acquired more XP than actually needed for a single level up in which cace the proposed soultion automatically would perform a second, third, ... level up as well.



          Note that I started the xpPerLevel.txt by the XP requirements needed to reach level 2 as the current logic would initially bump a player to level 2 automatically as 0 XP are required to reach that level. On applying these changes you basically only need to store the xpPerLevel map, the current level of the user as well as the gained XP on the user.



          As before with the xpPerLevel map, ding() or performLevelUp(), as I renamed it to, are also good candidates for a strategy pattern that allows you to define different level up behaviors for different players which can be changed during runtime i.e. to see during test time which level-up logic is more suitable if multiple candidates may exist (i.e. one strategy might reset the XP of a user after a level up was performed while an other just builds up on the old amount. Yet another strategy might refill the player's HP back to maximum and so forth). The method itself has also a lot of duplicate code, which you don't really need as you don't do anything differently from level to leve. It is therefore a strong candidate for refactoring as well:



          private void performLevelUp() {
          System.out.println(" #############################");
          System.out.println(" # You have reached level " + (++curLevel) + "! # ");
          System.out.println(" #############################");
          }


          As the level of a player is dependent on the XP earned I'd remove the setLevel(int level) method completly. Instead I'd provide a method that allows a player to gain XP:



          public void awardXP(int xpAmount) {
          curXP += xpAmount;
          checkCurrentXP();
          }


          Right after you awarded the player with a number of XP it will automatically check the whether it lead to a level up or not and thus update the user's level accordingly.



          As hopefully can be seen, certain state, such as the player's level and curXP, is encapsulated in the player's object and through invoking methods on that player's object that state gets manipulated. This is in essence what object-oriented programming should be.



          Whithin your game loop you only need to award the player a certain amount of XP which will trigger internal state changes automatically. So there is no need to invoke p.levelUp() and p.levelUpXp() within your game loop.



          As your main-loop is basically a typical console application reading in some user input and performing some task on the input applying a command pattern here does make the code a bit more readable. You basically refactor out your if/if-else segments into own little classes that are only focusing on that particular task. Your loop is basically responsible for too many things, which violates a bit the single responsibility principle which is further a part of SOLID.



          Basically what you can do to improve your code here is, i.e. introduce a new EventLoop class where you can register a couple of commands with it. The commands are just implementations of either an interface or abstract base class. The EventLoop class will store the commands within a map for a certain input command. Upon user-input the input will be parsed and the respective command invoked, if available. In Java this may look something like this:



          public class EventLoop {

          private final Map<String, ConsoleCommand> registeredCommands;

          EventLoop() {
          reisteredCommands = new ConcurrentSkipListMap<>();
          }

          public void registerCommand(String command, ConsoleCommand action) {
          if (action == null) {
          throws new IllegalArgumentException("No defined action for command " + command);
          }
          registeredCommands.put(command, action);
          }

          public vod unregisterCommand(String command) {
          registeredCommands.remove(command);
          }

          public void update(GameState state) {
          Scanner scanner = new Scanner(System.in);

          while(GameState.FINISHED != state.currentState()) {
          printHelp(state);

          String input = scanner.nextLine();
          // String input = validate(input);
          ConsoleCommand action = registeredCommands.get(input);
          if (null != action) {
          action.perform(state);
          } else {
          System.err.println("t Invalid Command!");
          }
          }
          scanner.close();
          }

          private void printHelp(GameState state) {
          System.out.println(
          getPlayerInfo(state.getPlayer())
          + "ntnt"
          + getEnemyInfo(state.getEnemy()));
          System.out.println("nt What would you like to do?");
          for(String cmd : registeredCommands.keySet()) {
          ConsoleCommand action = registeredCommands.get(cmd);
          System.out.println("t " + cmd + ". " + action.printHelp());
          }
          }

          private String getPlayerInfo(Player player) {
          return "t" + player.getName() + "nt HP: " player.getHealth() + " Level: " + player.getCurrentLevel() + " Exp: " + player.getCurrentXP();
          }

          private String getEnemyInfo(Creature enemy) {
          return "t" + enemy.getName() + "nt HP: " +enemy.getHealth() + " Level: " + enemy.getCurrentLevel();
          }
          }


          A ConsoleCommand defines now the concrete action to perform once invoked. As can be seen from the EventLoop two methods are at least necessary:



          public interface ConsoleCommand {
          void perform(GameState state);
          String printHelp();
          }


          If you later on decide that you want to pass certain properties to a command, you should change the interface to an abstract class and implement a method like parseCommand(String input) in it so that inheriting classes automatically have access to the already parsed values.



          A concrete implementation of the AttackCommand may now look like this



          public class AttackCommand implements ConsoleCommand {

          private Random rand = new SecureRandom();

          @Override
          public String printHelp() {
          return "Attack"
          }

          @Override
          public void perform(GameState state) {

          Player player = state.getPlayer();
          Creature enemy = state.getEnemy();
          int damageDealt = rand.nextInt(player.getAttackDamage());
          int damageTaken = rand.nextInt(enemy.getAttackDamage());

          player.takeDamage(damageTaken);
          enemy.takeDamage(damageDealt);

          if (damageDealt > 0) {
          System.out.println("t> You strike the "
          + enemy.getName() + " for " + damageDealt + " damage!");
          } else if (damageDealt < 1) {
          System.out.println("t> You attempt to hit the "
          + enemy.getName() + " but miss!");
          }
          if (damageTaken > 0) {
          System.out.println("t> The " + enemy.getName()
          + " retaliates! You take " + damageTaken + " damage!");
          } else if (damageTaken < 1) {
          System.out.println("t> The " + enemy.getName()
          + " retaliates but misses!");
          }

          if (player.getHealth() < 1) {
          System.out.println("t ##################"
          + "nt # You Have Died! #"
          + "nt ##################");
          state.updateGameState(GameState.FINISHED);
          }

          if (enemy.getHealth() < 1) {
          System.out.println("t Enemy " + enemy.getName()
          + " was crushed by your mighty strikes. You have been awarded with "
          + enemy.getAmountOfXPWorthForKilling() + " XP";
          player.awardXP(enemy.getAmountOfXPWorthForKilling());
          }
          }
          }


          I hope that by the presented examples it does make sense to you, that by separating the actual actions into commands the general code becomes less cluttered and therefore easily readable and understandable. You further can test an action more easily as well.



          As you might have noticed that I also changed a couple of things throughout this example. I like to have names of methods that actually do the same things on the object to be the same. So player.takeDamage(int) does basically the same as enemy.takeDamage(int). Refactoring this behavior to a parent class would make sense here as everything has to die once it has no HP left. The difference here is though that if a player dies the game is over compared to when a creature dies the player is awarded with XP.



          Further, it is IMO a good design to refactor the overall GameState out of the actual main-loop so that it can get passed around more easily. This allows the action commands to update the game state if needed without having to perform a callback into the main-loop.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 9 hours ago

























          answered 10 hours ago









          Roman Vottner

          39128




          39128
























              up vote
              3
              down vote













              Welcome on Code Review.



              Your game's loop is way too long. You have to separate your code into functions that constitute logical units.



              E.g., taking and dealing of damages have to be moved into two separate function dealDamage and takeDamage. The whole loop of the battle into a function doBattle. Spawn of enemy have to be wrap into a spawnEnemy, inside which you place a call to doBattle. Etc.



              I'm afraid to tell you that all your logic seem to be weird.



              Instead of increasing the level and Xp in a clumsy way, just remove ding, levelUp and levelUpXp, replace:



              p.curXP += 3;
              p.levelUp();
              p.levelUpXp();


              by



              p.addXp(3);


              and add this function :



              public void addXp(int reward) {
              curXP += reward;
              while (level < requiredXP.length && requiredXP[level] < curXP) {
              ++level;
              System.out.println(" #############################");
              System.out.println(" # You have reached level " + level + "! # ");
              System.out.println(" #############################");
              }
              }


              By doing that, you can get rig or reqXP and currentLevel too.



              Your max level become the size of requiredXP (which you could rename experienceGap).



              You can populate this array using the logic from Level() (adding the values to the array instead of printing them)



              If you have some trouble to understand how game mechanics works, dive into the GameDev site.






              share|improve this answer

























                up vote
                3
                down vote













                Welcome on Code Review.



                Your game's loop is way too long. You have to separate your code into functions that constitute logical units.



                E.g., taking and dealing of damages have to be moved into two separate function dealDamage and takeDamage. The whole loop of the battle into a function doBattle. Spawn of enemy have to be wrap into a spawnEnemy, inside which you place a call to doBattle. Etc.



                I'm afraid to tell you that all your logic seem to be weird.



                Instead of increasing the level and Xp in a clumsy way, just remove ding, levelUp and levelUpXp, replace:



                p.curXP += 3;
                p.levelUp();
                p.levelUpXp();


                by



                p.addXp(3);


                and add this function :



                public void addXp(int reward) {
                curXP += reward;
                while (level < requiredXP.length && requiredXP[level] < curXP) {
                ++level;
                System.out.println(" #############################");
                System.out.println(" # You have reached level " + level + "! # ");
                System.out.println(" #############################");
                }
                }


                By doing that, you can get rig or reqXP and currentLevel too.



                Your max level become the size of requiredXP (which you could rename experienceGap).



                You can populate this array using the logic from Level() (adding the values to the array instead of printing them)



                If you have some trouble to understand how game mechanics works, dive into the GameDev site.






                share|improve this answer























                  up vote
                  3
                  down vote










                  up vote
                  3
                  down vote









                  Welcome on Code Review.



                  Your game's loop is way too long. You have to separate your code into functions that constitute logical units.



                  E.g., taking and dealing of damages have to be moved into two separate function dealDamage and takeDamage. The whole loop of the battle into a function doBattle. Spawn of enemy have to be wrap into a spawnEnemy, inside which you place a call to doBattle. Etc.



                  I'm afraid to tell you that all your logic seem to be weird.



                  Instead of increasing the level and Xp in a clumsy way, just remove ding, levelUp and levelUpXp, replace:



                  p.curXP += 3;
                  p.levelUp();
                  p.levelUpXp();


                  by



                  p.addXp(3);


                  and add this function :



                  public void addXp(int reward) {
                  curXP += reward;
                  while (level < requiredXP.length && requiredXP[level] < curXP) {
                  ++level;
                  System.out.println(" #############################");
                  System.out.println(" # You have reached level " + level + "! # ");
                  System.out.println(" #############################");
                  }
                  }


                  By doing that, you can get rig or reqXP and currentLevel too.



                  Your max level become the size of requiredXP (which you could rename experienceGap).



                  You can populate this array using the logic from Level() (adding the values to the array instead of printing them)



                  If you have some trouble to understand how game mechanics works, dive into the GameDev site.






                  share|improve this answer












                  Welcome on Code Review.



                  Your game's loop is way too long. You have to separate your code into functions that constitute logical units.



                  E.g., taking and dealing of damages have to be moved into two separate function dealDamage and takeDamage. The whole loop of the battle into a function doBattle. Spawn of enemy have to be wrap into a spawnEnemy, inside which you place a call to doBattle. Etc.



                  I'm afraid to tell you that all your logic seem to be weird.



                  Instead of increasing the level and Xp in a clumsy way, just remove ding, levelUp and levelUpXp, replace:



                  p.curXP += 3;
                  p.levelUp();
                  p.levelUpXp();


                  by



                  p.addXp(3);


                  and add this function :



                  public void addXp(int reward) {
                  curXP += reward;
                  while (level < requiredXP.length && requiredXP[level] < curXP) {
                  ++level;
                  System.out.println(" #############################");
                  System.out.println(" # You have reached level " + level + "! # ");
                  System.out.println(" #############################");
                  }
                  }


                  By doing that, you can get rig or reqXP and currentLevel too.



                  Your max level become the size of requiredXP (which you could rename experienceGap).



                  You can populate this array using the logic from Level() (adding the values to the array instead of printing them)



                  If you have some trouble to understand how game mechanics works, dive into the GameDev site.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 12 hours ago









                  Calak

                  1,799214




                  1,799214






















                      up vote
                      2
                      down vote













                      This could be shortened by looping through requiredXP until you reach a value that curXP is equal to or using a switch statement. The same could be done in levelUpXp().

                      You are right that using and ArrayList<Integer> could simplify things as well. Possibly look into a list or queue data type.

                      You could also call ding() from inside levelUp() or levelUpXp() instead of the while loop or set a flag variable (eg. newLevel) that it tests for to skip the test conditions inside.






                      share|improve this answer








                      New contributor




                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.














                      • 1




                        Other than that, nice job on the code!
                        – LukeDev
                        15 hours ago















                      up vote
                      2
                      down vote













                      This could be shortened by looping through requiredXP until you reach a value that curXP is equal to or using a switch statement. The same could be done in levelUpXp().

                      You are right that using and ArrayList<Integer> could simplify things as well. Possibly look into a list or queue data type.

                      You could also call ding() from inside levelUp() or levelUpXp() instead of the while loop or set a flag variable (eg. newLevel) that it tests for to skip the test conditions inside.






                      share|improve this answer








                      New contributor




                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.














                      • 1




                        Other than that, nice job on the code!
                        – LukeDev
                        15 hours ago













                      up vote
                      2
                      down vote










                      up vote
                      2
                      down vote









                      This could be shortened by looping through requiredXP until you reach a value that curXP is equal to or using a switch statement. The same could be done in levelUpXp().

                      You are right that using and ArrayList<Integer> could simplify things as well. Possibly look into a list or queue data type.

                      You could also call ding() from inside levelUp() or levelUpXp() instead of the while loop or set a flag variable (eg. newLevel) that it tests for to skip the test conditions inside.






                      share|improve this answer








                      New contributor




                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.









                      This could be shortened by looping through requiredXP until you reach a value that curXP is equal to or using a switch statement. The same could be done in levelUpXp().

                      You are right that using and ArrayList<Integer> could simplify things as well. Possibly look into a list or queue data type.

                      You could also call ding() from inside levelUp() or levelUpXp() instead of the while loop or set a flag variable (eg. newLevel) that it tests for to skip the test conditions inside.







                      share|improve this answer








                      New contributor




                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.









                      share|improve this answer



                      share|improve this answer






                      New contributor




                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.









                      answered 15 hours ago









                      LukeDev

                      214




                      214




                      New contributor




                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.





                      New contributor





                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.






                      LukeDev is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                      Check out our Code of Conduct.








                      • 1




                        Other than that, nice job on the code!
                        – LukeDev
                        15 hours ago














                      • 1




                        Other than that, nice job on the code!
                        – LukeDev
                        15 hours ago








                      1




                      1




                      Other than that, nice job on the code!
                      – LukeDev
                      15 hours ago




                      Other than that, nice job on the code!
                      – LukeDev
                      15 hours ago










                      Brian Craycraft is a new contributor. Be nice, and check out our Code of Conduct.










                       

                      draft saved


                      draft discarded


















                      Brian Craycraft is a new contributor. Be nice, and check out our Code of Conduct.













                      Brian Craycraft is a new contributor. Be nice, and check out our Code of Conduct.












                      Brian Craycraft is a new contributor. Be nice, and check out our Code of Conduct.















                       


                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208278%2fsimple-text-based-rpg-leveling-system%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Bundesstraße 106

                      Verónica Boquete

                      Ida-Boy-Ed-Garten