Τhanks for sharing your code!
From looking at your current code, it looks like it will quickly become difficult to maintain. This game looks like a prime candidate to refactor into different classes.
You said that you were new to programming, so you may not have much (if any) experience with creating your own classes.
Classes can be thought of as a blueprint for an object. You should create classes to represent the things in your game. Some potential classes that jump out to me are maybe a Game class to represent the game itself, a Player class to represent the player of the game, a Monster or 'Enemy' class to represent an enemy in your game. You currently have a Skeleton and a Zombie so both of those could be an instance of Monster instead of a collection of values that you need to manage yourself!
You could settle with just those, or you could go even further and have a Room class, an Item class (a potion could be an item) and so on.
Your TextBasedAdventure class is doing everything at the moment; it will quickly become difficult to manage. Separating it out into different classes will go a long way towards increasing readability and also maintainability.
Let's start with creating the Player class and see why it would be preferable over your current way of handling the player (that being)
public static String Name;
public static int HP;
public static int maxHP;
public static int xp;
public static int atk;
public static int def;
public static int lvl;
public static int potion;
You currently have just a bunch of different values that are all loose in your program. Let's move these into a Player class to encapsulate them.
public class Player {
private int HP;
private int maxHP;
private int xp;
private int atk;
private int def;
private int lvl;
private int numPotions;
public Player(){
HP = maxHP;
lvl = 1;
// ... any other initial values you want
}
/* Getters & Setters */
public int getHp(){
return HP;
}
public void setHP(int hp){
HP = hp;
}
// .. any other ones you need
}
So we have a Player class now that holds all the values that are related to the Player of your game.
You can also provide some helper methods in your Player that could look like this.
public boolean isAlive(){
return HP > 0;
}
It's a lot more readable to see something like this
if(player.isAlive()){
// do stuff
}
over this
if(HP > 0){
// do stuff
}
And how about another one
public void heal(){
if(numPotions > 0){
numPotions--;
HP = maxHP;
}
}
now we can use
player.heal();
instead of implementing it directly in the main method. This also has the advantage of allowing us to call this method any time it needs to be used, and also changing the implementation in one place if it needs to change, instead of finding and changing the logic all throughout your code.
A big problem I can see is your methods combatskel() and combatzombie(). These methods are almost identical, the only thing that changes is the text that is printed out and some numbers. This could be refactored to take an instance of a Monster as an argument.
First we need to make a Monster class. Looking at those 2 methods, let's take out all the variables that a Monster should have.
It looks like hp, damage, atk, def. I may be missing some but let's go with this for now.
We might end up with something like this
public class Monster {
private int hp;
private int damage;
private int atk;
private int def;
public Monster(int hp, int damage, int atk, int def) {
this.hp = hp;
this.damage = damage;
this.atk = atk;
this.def = def;
}
public int getHp() {
return hp;
}
public int getDamage() {
return damage;
}
public int getAtk() {
return atk;
}
public int getDef() {
return def;
}
}
Now we have a lot of cross over between the Player class and the Monster class, so we could derive them both from a Creature class or something similar. But let's keep things simple for now and just have some code duplication.
Now that we have a Monster class, we could maybe make a method that looks like
public void combat(Monster monster){
// do the fight logic
}
Now this monster could be a zombie, a skeleton, a ghost, orc, elf or however many new monsters you want to add to your game (you could put their information in a file and load in any number of them!)
Let's try and start writing the method out, copying the logic from your current implementation.
public void combat(Monster monster){
System.out.println("You Have Encountered a " + monster.getName());
int damage = monster.getDamage();
if (!player.isAlive()) {
gameover();
}
if(!monster.isAlive()){
System.out.println("You Defeated A " + monster.getName());
player.levelUp(); // doesn't exist - you could implement it yourself by placing the level up logic there.
}
// and the rest...
}
This is obviously only the start, I won't completely refactor all of your code, but I hope this gives a good idea of how you can use objects and methods to reduce code duplication. Now you could have 1 method to handle combat with a monster instead of 1 x the number of monsters in your game!
Misc stuff
A lot of your methods throw an InterruptedException. This is implementation detail leaking out of your methods. There's no reason the calling code should have to handle these exceptions just because you want to use Thread.sleep() in your game. I would recommend something like this
public void wait(int numSeconds){
try {
Thread.sleep(numSeconds * 1000);
} catch (InterruptedException e) {
// don't worry so much about this
}
}
Normally you should never do a try catch with an empty block, but in this scenario, it doesn't really matter if something messes up when sleeping, which will probably never happen. Now the rest of your code doesn't have to deal with the exceptions. You could use it like this
wait(3);
will wait for 3 seconds.
Don't use public variables. You use a lot of public variables in your main code, all variables should be private in 99% of cases. Some good examples of good use cases for public are Integer.MAX_VALUE and Math.PI. These are good examples because these are values that will never ever change. If you want to provide access to variables in other classes, create private fields and then create public getter methods.
Your variable names and method names should use camelCase. You adhere to this correctly in a lot of places, but variables like Name and StartGame violate this convention.
See the docs on naming conventions here
Hopefully this review was helpful for you!