0

so I'm working on a text based game and I'm running into an issue of an aray of object not being populated properly during run-time. I'm not entirely sure why this is happening, but here is the code:

public class Area
{
    public List<Area> options;
    public string name;
    public string text;
    public uint time;

    private void loadOptions()
    {
        MainWindow.app.options.Children.Clear();
        for(int i = 0; i < options.Count(); i++)
        {
            Button b = new Button();

            b.Content = options[i].name;
            b.Name = "b"+i;
            b.Click += new RoutedEventHandler(optionClick);

            MainWindow.app.options.Children.Add(b);
        }
    }

    private void optionClick(object sender, EventArgs e)
    {
        Button clicked = (Button)sender;
        int index = Int32.Parse(clicked.Name.Split('b')[1]);
        options[index].load();
    }

    public void load()
    {
        loadOptions();
        MainWindow.app.mainText.SelectAll();
        MainWindow.app.mainText.Selection.Text = text;
    }
}

that is the logic portion of the Area class, and I'm holding all the other areas in a seperate file, in an Areas class, shown here:

public static class Areas
{
    public static Area opening = new Area()
    {
        name = "Opening",
        text = "This is the first area, just a test for now, but will be fully filled out at a later time",
        time = 0,
        options = new List<Area>()
        {
            second
        }
    };

    public static Area second = new Area()
    {
        name = "second",
        text = "this is the second area for stuffs and stuffs",
        time = 0,
        options = new List<Area>()
        {
            opening
        }
    };
}

both are a part of the same namespace, but are in separate files for readability. The idea is that the options array holds all the possible menus that the player can go to from that menue. the first test area's code is this:

public Area test = new Area()
    {
        text = "This is yet another test to test stuffs and things and places",
        time = 0,
        options = new List<Area>
        {
            Areas.opening
        }
    };

and that loads properly, but as soon as I click on the button, the entire thing crashes and throws a null reference exception.

Here are the details of the error:

    System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=LostWorlds
  StackTrace:
   at LostWorlds.Area.loadOptions() in C:\Users\Max\source\repos\LostWorlds\LostWorlds\LostWorlds\MainWindow.xaml.cs:line 356
   at LostWorlds.Area.load() in C:\Users\Max\source\repos\LostWorlds\LostWorlds\LostWorlds\MainWindow.xaml.cs:line 373
   at LostWorlds.Area.optionClick(Object sender, EventArgs e) in C:\Users\Max\source\repos\LostWorlds\LostWorlds\LostWorlds\MainWindow.xaml.cs:line 368
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
   at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
   at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at LostWorlds.App.Main()

from what I've gathered, there's some issue with populating the area's options list with the objects in the static class Areas, whenever I look at the values of the list, it has one entry that is null.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Raxmo
  • 57
  • 5
  • Post the text and stack trace of the Null Reference Exception. Will help identify where it is occurring. – Austin Winstanley May 31 '18 at 21:22
  • just edited the original post to include full details of the exception – Raxmo May 31 '18 at 21:26
  • In Areas class, you are trying to add `second` to `list` which not declared. – lkdhruw May 31 '18 at 21:30
  • Is ` MainWindow.app.options` initialized? Put a break point on that first line in `loadOptions()` and see. You can also step through the debugger from there and see where it's breaking and then you'll have a better idea of what wasn't initialized. – Austin Winstanley May 31 '18 at 21:30
  • is this an issue of C# not hoisting the declarations? – Raxmo May 31 '18 at 21:31

1 Answers1

0

I came to realize that this is an issue with Hoisting, because "opening" referenced "second" before "second" was declared, "opening" didn't have anything to populate itself with, and thus returned a null reference. so the solution is to do as follows:

public static Area second = new Area()
    {
        name = "second",
        text = "this is the second area for stuffs and stuffs",
        time = 0,
        options = new List<Area>()
        {
            opening
        }
    };

    public static Area opening = new Area()
    {
        name = "Opening",
        text = "This is the first area, just a test for now, but will be fully filled out at a later time",
        time = 0,
        options = new List<Area>()
        {
            second
        }
    };

not entirely sure yet how to make the references cyclical, but I'm sure I can figure it out.

Raxmo
  • 57
  • 5
  • That did look a little hinky. Why do these have to be cyclical? – Robert Harvey May 31 '18 at 21:38
  • I suppose it doesn't absolutely need to be cyclical, and definitely not cyclical between two elements, however, later on in development, I might want to have loops of areas (likely a loop of maybe 3-5 areas or so) which would allow for more interesting play. Also, it would be nice if I could have something like a "go home" option, which would be a reference to a central area. – Raxmo May 31 '18 at 21:43
  • How do you know when to stop looping? – Robert Harvey May 31 '18 at 21:48
  • well, the program will only step forward in the loop upon user request, and there will be multiple branching paths, the current code is just a test snippet in order to test edge cases in a small population. so, the quick answer would be when the player wants to – Raxmo May 31 '18 at 22:00
  • actually... for the option to "go home" I could just hard code that one in – Raxmo May 31 '18 at 22:10