Jack Nguyen
Jack Nguyen

Reputation: 103

What is the misuse of static final objects in my rpg game?

So I've been working on a pretty straight forward text based game. It's driven by user input and a few options. I've gotten the game to run and have been debugging but I came across a bug I can't seem to find the source of the issue.

Enemies spawn in my game whenever you move as long as your "alert" stat is high enough. Because I have different enemies with different stats I just used static final objects with their own specific stats as shown here.

public static final Monsters CRAWLER = new Monsters("Crawler", 15, 5, 100, 3);
public static final Monsters HOWLER = new Monsters ( "Howler", 20, 10, 50, 4);
public static final Monsters PROWLER = new Monsters ( "Prowler", 30, 15, 150, 5);
public static final Monsters MANTIS = new Monsters ( "Mantis", 40, 20, 200, 8);
public static final Monsters GOLEM = new Monsters ("Golem", 60, 25, 300, 10);
public static final Monsters DOGS = new Monsters ( "Group of Dogs", 10 ,5, 40, 2 );

This code is in a class called Monsters which extends an abstract class Character because they share the instance variables Name, health, alert, atk, and xp.

I then put these enemies into a Monsters object Arraylist and another arraylist for the monster in combat.

ArrayList<Monsters> allenemy = new ArrayList<Monsters>();
ArrayList<Monsters> currentenemy = new ArrayList<Monsters>();

The allenemy array has the above Monsters with their stats and the currentenemy is the one that the player fights. Combat happens at once and both the player and the monster get damaged using getAtk, setHealth, and getHealth. If the player's health becomes 0, then a game over message is displayed. On the other hand, if the enemy's health becomes 0, then a different message is displayed and the enemy is removed from the currentenemy. However, upon encountering the same enemy ( same name ), the enemy just dies instantly. The only way this is possible is if the enemy's health is 0, so I figured it must have something to do with the public static final Monster objects. Can anyone help me? Below is some more specific code.

Enemy spawn method: public static String espawn ( Player one ){

    int enemychance = (int)(Math.random()*101);
    Monsters current= null;
    if ( enemychance >= 0 && enemychance <= 25 ) current = one.allenemy.get(5); //end if enemychance is from 0 to 25
    if ( enemychance > 25 && enemychance <= 50 ) current = one.allenemy.get(0); //end if enemychance is from 26 to 50
    if ( enemychance > 50 && enemychance <= 70 ) current = one.allenemy.get(1); //end if enemychance is from 51 to 70
    if ( enemychance > 70 && enemychance <= 85 ) current = one.allenemy.get(2); //end if enemychance is from 71 to 85
    if ( enemychance > 85 && enemychance <= 95 ) current = one.allenemy.get(3); //end if enemychance is from 86 to 95
    if ( enemychance > 95 && enemychance <= 100) current = one.allenemy.get(4); //end if enemychance is from 96 to 100
    one.currentenemy.add(current);
    String espawn = "\nA " + current.getName() + " has appeared.";
    return espawn;
} //end static espawn method

The combat loop:

if ( one.getAlert() >= 15){ //threshold for spawning monsters
            if ( enemy <= 30 ){ //chance for encountering enemies
                System.out.print ( Interaction.espawn(one) + "\n");
                while (one.currentenemy.get(0).getHealth() > 0 && one.getHealth() > 0){
                    flee =  (int)((Math.random()*4)+1); //1-5 for fleeing
                    attack1 = (int)((Math.random()*9)+1); //1-10 for your attack chance
                    attack2 = (int)((Math.random()*9)+1); //1-10 for enemy attack chance
                    attack3 = (int)((Math.random()*9)+1); //1-10 for enemy attackchance while fleeing
                    System.out.print ("\n" + Interaction.combatmenu ( one) + "\n");
                    int check2 = reader.nextInt();
                    if ( check2 == 1 ){
                        if ( attack1 <= 6 ) {
                            hp();
                            System.out.print (Interaction.atk1(one));
                            one.setHealth(one.getHealth()-one.currentenemy.get(0).getAtk());
                        } //end if enemy atk hits
                        else { 
                            System.out.print (Interaction.atk3());
                        } //end if enemy atk misses
                        if ( attack2 <= 8 ){
                            System.out.print (Interaction.atk2(one));
                            one.currentenemy.get(0).setHealth(one.currentenemy.get(0).getHealth()-one.getAtk()); //calls arraylist index 0's setHealth method as arraylist index 0's getHealth method-player's getAtk method
                        } //end if your attack hits
                        else{
                            System.out.print (Interaction.atk4());
                        } //end if your attack misses
                    } //end if user wants to attack
                    if ( check2 == 2 ) {
                        if ( flee == 1 ){
                            System.out.print (Interaction.flee1());
                            one.currentenemy.remove(0);
                            break here;
                        } //end if flee works
                        else{
                            System.out.print (Interaction.flee2());
                            one.setHealth(one.getHealth()-one.currentenemy.get(0).getAtk());
                            if ( attack3 <= 6 ) {
                                hp();
                                System.out.print (Interaction.atk1(one));
                                one.setHealth(one.getHealth()-one.currentenemy.get(0).getAtk());
                            } //end if enemy attack hits
                            else {
                                System.out.print (Interaction.atk3());
                            } //end if enemy atk misses
                        } //end if flee fails
                    } //end if user wants to flee
                } //end while player and enemy are not dead
                if (one.currentenemy.get(0).getHealth() <= 0 ){
                    System.out.print ( Interaction.deade(one));
                    if ( loot <= 4 ){
                        System.out.print ( Interaction.loot1(one) + "\n" + Interaction.atk(one));
                    } //end if no weapon has been found for 80%
                    else {
                        System.out.print ( Interaction.loot2());
                        one.setAtk(one.weapon.get(0).getAtk());
                    } //end if weapon has been found for 20%
                    one.setXP(one.getXP() + one.currentenemy.get(0).getXP());
                    one.maxXP();
                    Interaction.lvlup(one);
                    one.currentenemy.remove(0);
                 if ( one.getHealth()<= 0){
                    hp();   
                 } //end if player is dead  
                } //end if enemy is dead
            } // if enemy spawn chance for 20%
        } //end if alert is greater than 15

Finally, sample output:

First encounter : The enemy Group of Dogs attacked you and did 5 damage. You attacked the enemy Group of Dogs for 20 damage.

Health: 85 Atk: 20 Enemy Health: 20 Enemy Atk: 5

Press 1 to attack. Press 2 to flee. 1 The enemy attack missed! You attacked the enemy Group of Dogs for 20 damage. The Group of Dogs is dead.

You found a Baseball bat on the dead Group of Dogs and equipped it. Your atk has been increased by 50

Second Encounter: A Group of Dogs has appeared. The Group of Dogs is dead. You didnt find anything on the body.You have leveled up. Your stats have been reset and increased.

So, the group of Dogs just dies instantly and gives the xp from killing it. This confirms that the enemy health must reach 0, because only in that loop does the player get XP. Any ideas? I know it's quite a messy code and probably disgusting in your eyes, but I'm a learning student, so forgive me. Please and thank you!

Upvotes: 2

Views: 178

Answers (3)

K.J.
K.J.

Reputation: 941

Try resetting the monster's health when it is removed from the currentmonster array list. I believe because those are static finals your are not properly instantiating new monsters. Which is why the health remains 0.

Upvotes: 0

Michael Macha
Michael Macha

Reputation: 1781

I don't entirely understand your code, but I dealt with a similar problem recently while attempting to rapidly create classes of polyhedra.

What you should really look at is two things: the factory pattern, and complex enumerations. That is, enumerations with initializers, and create() methods, which return a fresh instance of an object describing your monsters. You're right, something is definitely being carried over from before, and we would like material like that to simply be garbage collected.

Case in point would be something like:

interface Monsters {
    …
}

interface MonstersFactory {
    public Monsters create();
}

enum StockMonstersFactory implements MonstersFactory {
    CRAWLER("Crawler", 15, 5, 100, 3),
    HOWLER( "Howler", 20, 10, 50, 4),
    PROWLER( "Prowler", 30, 15, 150, 5),
    MANTIS( "Mantis", 40, 20, 200, 8),
    GOLEM("Golem", 60, 25, 300, 10),
    DOGS( "Group of Dogs", 10 ,5, 40, 2 );

    private StockMonstersFactory(String name, /*…other initialization options…*/) {
        //…
        //standard initializer, set enum constants to what you need them 

to be //… }

    public Monsters create() {
        Monsters monsters = new Monsters(/*Initialization constants stored in enumeration fields*/);
        return monsters;
    }
}

You get the idea, I hope? And when you need a new monster, instead of simply using current = one.allenemy.get(#);— which is almost certainly where the problem is — you would use current = StockMonstersFactory.[chosen monster].create();, which will give you a completely fresh object with no memory of anything that happened before.

Of course there are other ways to get what you're looking for, but this is what comes to mind. It might also help if you provided us more specific code for how these monsters are being instantiated; one.allenemy.get(…) could specifically be expanded on.

Anyway, this kind of thing is a large part of what enumerations were created for.

Upvotes: 4

Misha
Misha

Reputation: 28163

You are right to suspect the static variables. You have only one instance of DOGS. When you fought it for the first time, you dropped its health to zero. Next time you fight a group of dogs, you are putting the same object into the enemies list.

Upvotes: 0

Related Questions