Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: Sparky on Fri 13/04/2007 05:28:00

Title: Goal List: Null pointer referenced (solved)
Post by: Sparky on Fri 13/04/2007 05:28:00
Thank you all for your continued support. I hate to post again so soon after my last query, but I ran into a problem I'm stumped by. The basic idea behind this script is to give the player a concise list of current objectives, such as "Find the four map pieces" or "Catch a golden butterfly". There will be other script elsewhere that sets updates the GUI; the following is just sets up an array for storing the goals.

These variables are declared in the global script.struct goal {
  String goal_name;
  String goal_note;
  int goal_status; // 0 = not on list, 1 = incomplete, 2 = complete
};
goal goal_list[20]; // array of current goals


These functions are also in the global script. The script compiles without errors. But when the function is called in-engine I get a "null pointer referenced" error that points to the indicated line.

// Goal List
function add_goal(String new_goal) { // add a goal to the list
player.Say("Right, we're calling the add_goal function now.");
// add the goal name to the goal list
int n = 0;
bool already_added = false;
while (n < 20) { // make sure it isn't already added
if (new_goal == goal_list[n].goal_name) { //   <------------------------ NULL POINTER ERROR here
already_added = true;
player.Say("The goal '%s' is in slot %d, so we don't need to add it a second time.", new_goal, n);
}
n ++;
}
// The script after this line seems to work as desired, you can probably ignore it.
if (already_added == false) {
n = 0;
bool exit = false;
while (exit == false) { // find a free slot
if (goal_list[n].goal_name == null) { // if the slot is empty
player.Say("We're setting putting the goal '%s' in slot %d", new_goal, n);
exit = true; // we found a free slot, so we can stop hunting
goal_list[n].goal_name = new_goal;
// add appropriate dialog, etc.
if (new_goal == "valve") {
goal_list[n].goal_note = "Find a handle for the broken valve.";
}
else { // if the name isn't recognized
player.Say("add_goal isn't finding a match for that goal.");
} // goal specific tasks
} // empty slot check
n ++;
}
}
}

If the while loop containing the offending line is commented out, everything runs without an error. The purpose of the line is to check if a string in the array is equal to the variable new_goal. It's part of a loop that exists to prevent listing the same goal twice. Perhaps there's another way to write the line?

In searching for a solution, the function String.CompareTo came up. It seemed useful at first, but it seems it needs a constant string instead of a variable. Is there a way to use this?
Title: Re: Goal List: Null pointer referenced
Post by: monkey0506 on Fri 13/04/2007 07:14:44
You can pass Strings through a const string parameter. That's why Chris implemented const strings vs strings. A const string parameter can be a new-style String, a string-literal ("this is some text"), or an old-style string. A String parameter can only be a new-style String or a string-literal. A string parameter can only be a string-literal or an old-style string.

I'm not sure how String.CompareTo handles null Strings, however I believe it's a known bug that you can't have a null String on the right-hand side of an operand such as == (double equal signs). The solution would be to try something like:

if ((goal_list[n].goal_name != null) && (new_goal == goal_list[n].goal_name)) {

Or perhaps String.CompareTo would work, but again, I'm not sure how it handles null Strings.
Title: Re: Goal List: Null pointer referenced
Post by: Ashen on Fri 13/04/2007 10:30:01
Nope, String.CompareTo has the same problem with null values. The only solution is to add the goal_list[n].goal_name != null check, or possibly to set all of them to an empty String (goal_list[n].goal_name = "";) at game start. (Obviously, you'd then need to change the goal_list[n].goal_name == null check later in the code to goal_list[n].goal_name == "".)
Title: Re: Goal List: Null pointer referenced
Post by: SupSuper on Sat 14/04/2007 00:30:26
Quote from: monkey_05_06 on Fri 13/04/2007 07:14:44
You can pass Strings through a const string parameter. That's why Chris implemented const strings vs strings. A const string parameter can be a new-style String, a string-literal ("this is some text"), or an old-style string. A String parameter can only be a new-style String or a string-literal. A string parameter can only be a string-literal or an old-style string.

I'm not sure how String.CompareTo handles null Strings, however I believe it's a known bug that you can't have a null String on the right-hand side of an operand such as == (double equal signs). The solution would be to try something like:

if ((goal_list[n].goal_name != null) && (new_goal == goal_list[n].goal_name)) {

Or perhaps String.CompareTo would work, but again, I'm not sure how it handles null Strings.
I think you'll have to queue up the conditions though, since that way the script will still check both conditions if it's null and throw the error. This should be error-free:
if (goal_list[n].goal_name != null)
if (new_goal == goal_list[n].goal_name) {
Title: Re: Goal List: Null pointer referenced
Post by: strazer on Sat 14/04/2007 00:42:57
No, the so-called "lazy evaluation" was implemented in AGS v2.71.
Title: Re: Goal List: Null pointer referenced
Post by: Sparky on Sat 14/04/2007 22:39:03
I never would have guessed to try that workaround. Here's the updated script.
if ((goal_list[n].goal_name != null) && (goal_list[n].goal_name == new_goal)) {
already_added = true;
}
n ++;

The script is up and running now, with no errors in sight. Thanks to all.
Quote from: strazer on Sat 14/04/2007 00:42:57
No, the so-called "lazy evaluation" was implemented in AGS v2.71.
Is combining if statements using && or || considered bad practice? I don't know much about scripting, but it seems to be tidier and more readable in cases like this where there's only a single check. If, on the other hand we were doing something like if ((boolean 1 == true) {
if (boolean 2 == true) {
do stuff
}
else if (boolean 2 == false) {
do other stuff
}
}
I could see using the longer form being more sensible.
Title: Re: Goal List: Null pointer referenced (solved)
Post by: strazer on Sat 14/04/2007 23:53:40
Quote
if ((goal_list[n].goal_name != null) && (goal_list[n].goal_name == new_goal)) {

This wouldn't have worked prior to AGS v2.71 since it used to process both expressions, regardless of whether the first one was already false or not. So if goal_list[n].goal_name were null, the second expression would still throw up an error.
That's why you had to break such if-statements up into two parts as SupSuper said.

But as of AGS v2.71, if the first expression is false, the second one won't even be processed. So if you use a current version of AGS your use of the && operator is perfectly fine.