My assignment was to make a game called 'World of Warcrap', and I finished, but there are errors.

the errors are in respawnNPC(), attack(), and fightToTheDeath() methods.

here are the instructions on what each methods are suppose to do:

1) Define the respawnNPC method inside the WorldOfWarcrap class such that it makes and returns a new character object that has the level argument as its level and where the characters is randomly named either Orc, Tauren, or Undead, and the class is randomly selected as either a Mage, Ranger, or Warrior. Also, if a request for a level 0 character is given, clamp it at level 1 (no level 0 characters).

2) attack: This method is used for a single attack of the referenced character object on the method argument character. How should we implement our battle system? Well, think of an attack as two separate things:
1. Did the attack score a hit?
2. If a hit was achieved, how many hit points damage did it inflict?
For the first calculation, we'll say the hit rate (likelihood of hit) is the attacker's xp /(the enemy's xp + the attacker's xp). So if Joe has 10 xp and Jane has 30 xp, the hit rate for Joe to successfully hit Jane would be 10/(30 + 10) = .25, which means a hit will only be achieved about 25% of the time. Each time this method is called you can simply generate a random number to see if a hit was successful or not.

For the second calculation, randomly scale the damage inflicted by the attacker and then reduce it according to the armor of the target enemy (armor of 90 would reduce the damage 90%). This will be the amount of hp lost by the target. Note that if an attack kills the enemy, you should increase the attacker's xp by 2 if the target is a lower level, 4 if the target is the same level, 6 if the target is a higher level. Note that for every 10 xp earned, a character should increase a level. So a level 1 character starts with 10 xp, when 20 xp is reached, the player levels up, which should increase stats accordingly.


3) fightToTheDeath: When called, the two characters (the referenced one and the argument) should attack each other until one of the characters is dead.


here is my code

import java.util.Random;
enum RPGClass { MAGE, RANGER, WARRIOR };

public class RPGCharacter 
{
	private static RPGCharacter RPGCharacter;
	public static int level;
	public static int armor;
	public static int damage;
	public static int XP;
	public static int xpLimit;
	public int HP;
	private static String name;
	private static RPGClass characterClass;
	
	// Makes new character
	public RPGCharacter(String characterName, RPGClass characterClassNew, int characterLevel)
	{	
		name = characterName;
		characterClass = characterClassNew;
		level = characterLevel;
		XP =0;
		
		levelStat();
	}
	
	// Sets the status for the newly made character initially according to the level
	public void levelStat()
	{
		if (characterClass == RPGClass.MAGE)
		{	
			if (armor <= 40)
			{
				armor = 25 + (level - 1);
			}
			else
			{
				armor = 40;
			}
			
			damage = 100 + ((level - 1) * 4);
			HP = level * 100;
		}
		else if (characterClass == RPGClass.RANGER)
		{	
			if (armor <= 65)
			{
				armor = 50 + (level - 1);
			}
			else
			{
				armor = 65;
			}
			
			damage = 66 + ((level - 1) * 2);
			HP = level * 100;
		}
		else if (characterClass == RPGClass.WARRIOR)
		{	
			if (armor <= 90)
			{
				armor = 75 + (level - 1);
			}
			else
			{
				armor = 90;
			}
			
			damage = 33 + (level - 1);
			HP = level * 100;
		}
		
		xpLimit = level * 10;
	}
	
	// Attack behavior
	public int attack(RPGCharacter opponent)
	{
		int attackRatio = (int)((getXPLimit() / (opponent.getXPLimit() + getXPLimit())) * 100);
		
		Random generator = new Random();
		
		int attackChance = generator.nextInt(100) + 1;
		
		int appliedAttack = 0;
		
		// Attacks if the attackChance is among the attackRatio based on opponent's armor stat
		if (attackChance <= (int)attackRatio)
		{
			double opponentDefense = opponent.getArmor() / 100;
			appliedAttack = (int)(damage * opponentDefense);
			
			// Applies the attack
			opponent.HP -= appliedAttack;
			
			// Adds XP if opponent is dead
			if (opponent.isAlive() == false)
			{
				if (level > opponent.getLevel())
				{
					XP += 2;
				}
				else if (level == opponent.getLevel())
				{
					XP += 4;
				}
				else if (level < opponent.getLevel())
				{
					XP += 6;
				}
			}
		}
		
		return appliedAttack;
	}
	
	public void fightToTheDeath(RPGCharacter opponent)
	{
		while ((isAlive() == true) && (opponent.isAlive() == true))
		{
			attack(opponent);
			opponent.attack(RPGCharacter);
		}
	}

	// Level up
	public void incLevel()
	{
		if (XP >= xpLimit)
		{
			level += 1;
		}
	}
	
	public void rejuvinate()
	{
		// Resets HP
		HP = level * 100;
	}
	
	public boolean isAlive()
	{
		boolean status = true;
		
		if (HP > 0)
		{
			status = true;
		}
		else if (HP == 0)
		{
			status = false;
		}
		return status;
	}
	
	public void currentCharacter()
	{
		
	}
	
	public String toString()
	{
		return getName() + " (Level " + getLevel() + " " + getCharacterClass() + ")" +
			   "\nXP: " + getXP() + "\n" +
			   "\nHP: " + getHP() + "\n" +
			   "\nArmor: " + getArmor() + "\n" +
			   "\nDamage: " + getDamage() + "\n";
	}
	
	public RPGClass getCharacterClass()
	{
		return characterClass;
	}
	
	public String getName()
	{
		return name;
	}
	
	public int getLevel()
	{
		return level;
	}
	
	public int getXP()
	{
		return XP;
	}
	
	public int getXPLimit()
	{
		return xpLimit;
	}
	
	public int getArmor()
	{
		return armor;
	}
	
	public int getDamage()
	{
		return (int)damage;
	}
	
	public int getHP()
	{
		return HP;
	}
}

and here is the respawnNPC() method:

public static RPGCharacter respawnNPC(int level)
	{	
		Random generator1 = new Random();
		Random generator2 = new Random();
		
		int nameSelector = generator1.nextInt(3) + 1;
		int classSelector = generator2.nextInt(3) + 1;		
			
		String name = "";
			
		if (level == 0)
		{
			level += 1;
		}
		
		if (nameSelector == 1)
		{
			name = "Orc";
			
			if (classSelector == 1)
			{
				characterClass = RPGClass.MAGE;
			}
			else if (classSelector == 2)
			{
				characterClass = RPGClass.RANGER;
			}
			else if (classSelector == 3)
			{
				characterClass = RPGClass.WARRIOR;
			}
		}
		else if (nameSelector == 2)
		{
			name = "Tauren";
			
			if (classSelector == 1)
			{
				characterClass = RPGClass.MAGE;
			}
			else if (classSelector == 2)
			{
				characterClass = RPGClass.RANGER;
			}
			else if (classSelector == 3)
			{
				characterClass = RPGClass.WARRIOR;
			}
		}
		else if (nameSelector == 3)
		{
			name = "Undead";
			
			if (classSelector == 1)
			{
				characterClass = RPGClass.MAGE;
			}
			else if (classSelector == 2)
			{
				characterClass = RPGClass.RANGER;
			}
			else if (classSelector == 3)
			{
				characterClass = RPGClass.WARRIOR;
			}
		}
		
		RPGCharacter NPC = new RPGCharacter(name, characterClass, level);
	
		return NPC;
	}

and the error is the following:

What does Undead want to do?
1) Fight a Level 1 Undead RANGER
2) Fight a Level 1 Undead RANGER
3) Fight a Level 1 Undead RANGER
X) Return to the main menu
Selection: 1
Exception in thread "main" java.lang.NullPointerException
at RPGCharacter.attack(RPGCharacter.java:79)
at RPGCharacter.fightToTheDeath(RPGCharacter.java:122)
at WorldOfWarcrap.processGameInput(WorldOfWarcrap.java:275)
at WorldOfWarcrap.runGame(WorldOfWarcrap.java:353)
at WorldOfWarcrap.processMainMenuInput(WorldOfWarcrap.java:258)
at WorldOfWarcrap.main(WorldOfWarcrap.java:65)

it is suppose to generate 3 random NPCs but it doesn't, and attack() and fightToTheDeath() shows error but I am stuck.

It's hard to know exactly what's going on since you didn't include your WorldOfWarcrap class, but in method fightToTheDeath on line 122, you call opponent.attack(RPGCharacter); . Do you ever initialize the static variable RPGCharacter? Perhaps you should pass the keyword this instead?

By the way, it's generally not a great idea to have a variable with the same name as the name of the class - it's confusing.

Edited 6 Years Ago by kramerd: n/a

that was the respawnNPC() of WorldOfWarcrap class, and here's the whole thing:

import java.io.PrintStream;
import java.util.Scanner;
import java.util.Random;
/**
 * This is a rather insipid RPG with a player
 * and three monsters battling it out in a
 * text-based interface. Fun!
 * 
 * @author McKilla Gorilla
 * @author ?
 */
public class WorldOfWarcrap 
{
	static RPGClass characterClass;

	// FOR GETTING INPUT FROM THE USER
	static Scanner keyboard;
	
	// AND A SHORTCUT FOR PRINTING
	static PrintStream out;
	
	// FLAG FOR LETTING THE PROGRAM CONTINUE
	static boolean keepAppRunning;
	
	// FLAG FOR LETTING THE GAME CONTINUE
	static boolean keepPlaying;
	
	// HERE IS THE PLAYER
	static RPGCharacter player;
	
	// AND HERE ARE THE ENEMY NPCs WHICH 
	// WE'LL RESPAWN AS SOON AS THEY DIE
	// NPC MEANS NON-PLAYER CHARACTER
	static RPGCharacter npc1;
	static RPGCharacter npc2;
	static RPGCharacter npc3;
	
	/**
	 * Here's where our program starts.
	 */
	public static void main(String[] args)
	{
		// INIT KEYBOARD INPUT STUFF
		keyboard = new Scanner(System.in);
		
		// AND THIS IS FOR DISPLAYING STUFF
		out = System.out;
		
		// WE'LL ASSUME THE USER WANTS TO PLAY NOW
		// BUT WILL CHANGE THIS LATER UPON REQUEST
		keepPlaying = true;
		keepAppRunning = true;

		// WELCOME THE USER
		printWelcomeMessage();
		
		// KEEP THE APP OPEN AS LONG AS THE USER
		// WANTS TO KEEP PLAYING
		while (keepAppRunning)
		{
			// DISPLAY THE MAIN MENU
			displayMainMenu();
			
			// GET INPUT FROM THE USER AND USE IT
			processMainMenuInput();
		}
		
		// SAY GOODBYE TO USER
		printGoodbyeMessage();
	}

	/**
	 * This method should make and returns a new,
	 * randomly selected NPC.
	 */
	public static RPGCharacter respawnNPC(int level)
	{	
		Random generator1 = new Random();
		Random generator2 = new Random();
		
		int nameSelector = generator1.nextInt(3) + 1;
		int classSelector = generator2.nextInt(3) + 1;		
			
		String name = "";
			
		if (level == 0)
		{
			level += 1;
		}
		
		if (nameSelector == 1)
		{
			name = "Orc";
			
			if (classSelector == 1)
			{
				characterClass = RPGClass.MAGE;
			}
			else if (classSelector == 2)
			{
				characterClass = RPGClass.RANGER;
			}
			else if (classSelector == 3)
			{
				characterClass = RPGClass.WARRIOR;
			}
		}
		else if (nameSelector == 2)
		{
			name = "Tauren";
			
			if (classSelector == 1)
			{
				characterClass = RPGClass.MAGE;
			}
			else if (classSelector == 2)
			{
				characterClass = RPGClass.RANGER;
			}
			else if (classSelector == 3)
			{
				characterClass = RPGClass.WARRIOR;
			}
		}
		else if (nameSelector == 3)
		{
			name = "Undead";
			
			if (classSelector == 1)
			{
				characterClass = RPGClass.MAGE;
			}
			else if (classSelector == 2)
			{
				characterClass = RPGClass.RANGER;
			}
			else if (classSelector == 3)
			{
				characterClass = RPGClass.WARRIOR;
			}
		}
		
		RPGCharacter NPC = new RPGCharacter(name, characterClass, level);
	
		return NPC;
	}

	/**
	 * This helper method simply prints
	 * a welcome message to the user.
	 */
	public static void printWelcomeMessage()
	{
		out.println("*** WELCOME TO THE WORLD OF WARCRAP ***");
	}

	/**
	 * This helper method simply prints 
	 * a goodbye message to the user.
	 */
	public static void printGoodbyeMessage()
	{
		out.println("Thanks for playing the World of Warcrap,"
				+ "remember to pay your subscription bill on time");
	}

	/**
	 * This method uses some randomization to
	 * construct all three NPCs.
	 */
	public static void initializeNPCs()
	{
		npc1 = respawnNPC((int)(Math.random() * (player.getLevel() + 2)));
		npc2 = respawnNPC((int)(Math.random() * (player.getLevel() + 2)));
		npc3 = respawnNPC((int)(Math.random() * (player.getLevel() + 2)));
	}

	/**
	 * This method prints the main menu for the user.
	 */
	public static void displayMainMenu()
	{
		out.println("MAIN MENU");
		out.println("M) Play new game as Mage");
		out.println("R) Play new game as Ranger");
		out.println("W) Play new game as Warrior");
		out.println("X) Exit the World of Warcrap");
		out.print("Selection: ");
	}

	/**
	 * This prints the in game menu, which presents
	 * options to the user while the game is running.
	 */
	public static void displayGameMenu()
	{
		out.println("\nWhat does "
				+ player.getName()
				+ " want to do?");
		out.println("1) Fight a Level " + npc1.getLevel() + " "
				+ npc1.getName() + " " + npc1.getCharacterClass());
		out.println("2) Fight a Level " + npc2.getLevel() + " "
				+ npc2.getName() + " " + npc2.getCharacterClass());
		out.println("3) Fight a Level " + npc3.getLevel() + " "
				+ npc3.getName() + " " + npc3.getCharacterClass());
		out.println("X) Return to the main menu");
		out.print("Selection: ");		
	}

	/**
	 * This gets main menu input from the user,
	 * tests to see what it is, and does work
	 * based on that input.
	 */
	public static void processMainMenuInput()
	{
		// GET USER INPUT
		String input = keyboard.nextLine();
		input = input.toUpperCase().trim();
		RPGClass characterClass;

		// DOES THE USER WANT TO BE A MAGE?
		if (input.equals("M"))
		{
			characterClass = RPGClass.MAGE;
		}
		// OR A RANGER?
		else if (input.equals("R"))
		{
			characterClass = RPGClass.RANGER;
		}
		// OR A WARRIOR?
		else if (input.equals("W"))
		{
			characterClass = RPGClass.WARRIOR;
		}
		// OR QUIT?
		else if (input.equals("X"))
		{
			keepAppRunning = false;
			return;
		}
		else
		{
			// THE USER SCREWED UP
			giveFeedbackForIllegalInput();
			return;
		}
		
		// ASK THE USER FOR THE CHARACTER NAME
		out.print("\nWhat is your character's name? ");
		String name = keyboard.nextLine();

		// AND INITIALIZE THE PLAYER
		player = new RPGCharacter(name, characterClass, 1);
		
		// AND NOW RUN THE GAME
		runGame();
	}

	/**
	 * This gets user input for the game menu
	 * and tests to see what the user wants
	 * to do and then does it.
	 */
	public static void processGameInput()
	{
		// GET USER INPUT
		String input = keyboard.nextLine();
		input = input.toUpperCase().trim();
		
		// FIGHT MONSTER # 1?
		if (input.equals("1"))
		{
			player.fightToTheDeath(npc1);
			displayFightResults(npc1);			
			if (!npc1.isAlive())
				npc1 = respawnNPC((int)(Math.random() * (player.getLevel() + 2)));
		}
		// FIGHT MONSTER # 2?
		else if (input.equals("2"))
		{
			player.fightToTheDeath(npc2);
			displayFightResults(npc2);
			if (!npc2.isAlive())
				npc2 = respawnNPC((int)(Math.random() * (player.getLevel() + 2)));
		}
		// OR FIGHT MONSTER # 3?
		else if (input.equals("3"))
		{
			player.fightToTheDeath(npc3);
			displayFightResults(npc3);			
			if (!npc3.isAlive())
				npc3 = respawnNPC((int)(Math.random() * (player.getLevel() + 2)));
		}
		// OR GO BACK TO THE MAIN MENU?
		else if (input.equals("X"))
		{
			keepPlaying = false;
			return;
		}
		else
		{
			// INVALID INPUT
			giveFeedbackForIllegalInput();
			return;
		}
	}

	/**
	 * This displays a summary of the results
	 * of the fight.
	 */
	public static void displayFightResults(RPGCharacter npc)
	{
		if (player.isAlive())
		{
			out.println("\nYOU WON THE BATTLE!\n");
			out.println("\n" + player + "\n");
			player.rejuvinate();
			out.println("Time to Rest and Rejuvinate\n");
			out.println("Stats after Rejuvination:");
			out.println("\n" + player + "\n");
		}
		else
		{
			out.println("\nYOU ARE VERY DEAD!\n");
			out.println("\nWinner:\n");
			out.println(npc);
			keepPlaying = false;
		}
	}

	/**
	 * This method contains the game loop for
	 * running an individual game.
	 */
	public static void runGame()
	{
		// MAKE THE ENEMIES
		initializeNPCs();

		// START THE GAME
		keepPlaying = true;
		
		// HERE'S OUR GAME LOOP
		while (keepPlaying)
		{
			// PRESENT THE OPTIONS TO THE PLAYER
			displayGameMenu();

			// PROCESS THE USER INPUT
			processGameInput();
		}
	}

	/**
	 * Tell the user they screwed up.
	 */
	public static void giveFeedbackForIllegalInput()
	{
		out.println("\nYou have provided illegal input\n");
	}
}

I had some insight while you were posting your code so I edited my previous post. Please read again.

thank you for the reply kramerd. I fixed the 3 NPC problem by making all variables non-static, and I didn't initialize RPGCharacter in opponent.attack(RPGCharacter), and I forgot about using 'this' for running the constructor.

I tried doing opponent.attack(this); and it showed a blank output but no errors.

I'm guessing that the line 79

int attackRatio = (int)((getXPLimit() / (opponent.getXPLimit() + getXPLimit())) * 100);

is causing fightToTheDeath() to result in error, but I don't know where I went wrong...

I ran the debugger and realized that it fell into infinite loop while running fightToTheDeath().

Are you still getting an error on line 79 in the attack method even after changing to opponent.attack(this)?

Are you still getting an error on line 79 in the attack method even after changing to opponent.attack(this)?

I ran the debugger and the line 79 processes, but it falls into infinite loop while running fightToTheDeath(). I'm guessing that none of the HP are getting subtracted

I think the problem is that line 79 is doing integer division, and you need it to do floating point division.

Integer division: 2/4 = 0 (result is truncated, not rounded)
Floating point division: 2.0/4.0 = 0.5

So attackRatio is always going to be zero. To fix this, you need to make at least one of the quantities involved in the division have a floating point type. For example:

int attackRatio = (int)(((float)getXPLimit() / (opponent.getXPLimit() + getXPLimit())) * 100);

Also your calculation of opponentDefense has the same problem. Change it to

double opponentDefense = opponent.getArmor() / 100.0;

I think the problem is that line 79 is doing integer division, and you need it to do floating point division.

Integer division: 2/4 = 0 (result is truncated, not rounded)
Floating point division: 2.0/4.0 = 0.5

So attackRatio is always going to be zero. To fix this, you need to make at least one of the quantities involved in the division have a floating point type. For example:

int attackRatio = (int)(((float)getXPLimit() / (opponent.getXPLimit() + getXPLimit())) * 100);

Also your calculation of opponentDefense has the same problem. Change it to

double opponentDefense = opponent.getArmor() / 100.0;

thank you for the reply. I tried the same method with (double) before but I did not try float. I changed it, but it still goes into an infinite loop after selecting an opponent NPC to fight. I can see that the HP is not changing, causing the fightToTheDeath() to loop infinitely... I kept running the debugger but I don't know what to fix yet..

Either double or float should work - they both cause the calculation to be done in floating point form. Did you change opponentDefense too?

thanks for the reply. yeah I fixed it to way I did it before, and modifed hell of a lot and got it to work perfectly. I found out that the attack() was taking HP out and replaced HP back to original, causing the fightToTheDeath() to go in an infinite loop. From this I learned the true value of Eclipse's debugger...extremely helpful

but I still don't get what I have to set all the variables non-static...does static type mean that the variable will be used all through out the class with ability to be modified by any method, or does it mean that one unchangeable value will be used through out the class?

The static keyword does not have to do with whether the variable is modifiable or not. The final keyword does that. static means that the variable "belongs to the class" rather than belonging to any particular instance. For example, you have two player objects. You want each player to have its own HP value. If the HP variable is static, then there is only one HP value for ALL players, which doesn't work. So HP needs to be an instance variable rather than a class variable.

A good example for when you would want to use a static variable is, let's say you want your RPGCharacter class to know how many players have been created in the game. If you had a number of players for each player, that wouldn't work. You need one number of players for the whole class that you can increment each time a player gets created. The way to do that is to make the number of players variable static.

oh... I understand it now. So static is basically allocating the same memory space for the whole class, instead of allocating each for individual instances... I was using static without even knowing that I understood its usage wrong.

I'm also confused with the usage of public and private; In my understanding, public instance means that any of the class' methods can change or extract the public instance, and private is changeable but not allowed to be called or changed by other classes, right?

Thank you for all the help by the way.

I think you have it right. public means any class/object can access the variable or method that's declared to be public. This is generally frowned upon for variables, since you don't necessarily want other classes to change the data inside your class. That's why instance variables are usually declared private, and then a public getter method is supplied so any other class/object can read the value.

Many people also write setter methods, but this can be dangerous as well, so it is becoming more common to avoiding providing setters.

private means no other class outside of the class where the private variable or method is defined can access the variable or method. This is a way of protecting your data, or writing helper methods that you don't want anyone outside your class to call.

This article has been dead for over six months. Start a new discussion instead.