Problems with C# lists, and ArgumentOutOfRangeException

I am in the process of making a farming/tower defense game and I am very new at programming. I seem to have a major problem with using Lists<> or arrays in XNA. I cannot get it to return the index that I want from the list.

The main question is inside my planting engine. I have successfully implemented a planting system that can generate a list of plants (spriteobjects) with varying properties and place them on the map. Now, I need a way to access a specific plant in the plant list based upon mouseclicking on that plant. I feel like I am very close, but I ended up with a ArgumentOutOfRangeException that I cannot solve. Here is a walkthrough of the code:

Initialization

 public void Addplants()
        {
            switch (Mode)
            {
            case "Wotalemon":
            NewPlant = new Plant(Texture, msRect);
            NewPlant.AddAnimation("seed", 0, 16, 64, 64, 1, 0.1f);
            NewPlant.AddAnimation("sprout", 64, 16, 64, 64, 1, 0.1f);
            NewPlant.AddAnimation("wota", 128, 16, 64, 64, 1, 1.0f);
            NewPlant.CurrentAnimation = "seed";
            NewPlant.DrawOffset = new Vector2(32, 48);
            NewPlant.Position = Position;
            NewPlant.Type = "wotalemon";
            NewPlant.Birthday = Days;
            NewPlant.IsSelected = false;
            plants.Add(NewPlant);
            thisPlant = NewPlant;
            //various plants after this

Update/Draw

I use some simple foreach loops to update and draw the plants, no problems here.

GetInfo (this method uses the spriteobject's hitbox property and a mouseRectangle)

public void GetInfo(Rectangle ms)
        {
            msRect = ms;
            for (int i = 0; i < plants.Count; i++)
            {
                foreach (Plant NewPlant in plants)
                {
                    if (NewPlant.BoundingBox.Intersects(msRect))
                    {
                        SelectedIndex = i;
                        NewPlant.Tint = Color.Black;
                    }
                    else
                        NewPlant.Tint = Color.White;
                }

            }
        }

finally, here is the problem:

 public void SelectPlant()
        {
            //if (SelectedIndex != null)
            if (SelectedIndex > plants.Count | SelectedIndex < 0)
                SelectedIndex = plants.Count;
            SelectedPlant = plants[SelectedIndex];

        }

The exception is thrown in this line:

SelectedPlant = plants[SelectedIndex];

The debugger shows the value as 0. I have tried various methods to try to prevent the index from being null. I feel like something in the Getinfo() method is key here. I am convinced that I am very close to success because the color test that I have inserted in there works perfectly. When I mouseover a plant, it turns black, when I remove the mouse, it returns to normal.

This is EXACTLY the type of behavior I want except that I want it to set selectedIndex to the index of the plant that I am mousing over. Any advice would be greatly appreciated.

Answers


I'll add this as a new answer since this is tackling a whole different issue. Taking a look at this code:

msRect = ms;
for (int i = 0; i < plants.Count; i++)
{
    foreach (Plant NewPlant in plants) // <-- this is redundant
    {
        if (NewPlant.BoundingBox.Intersects(msRect))
        {
            SelectedIndex = i;
            NewPlant.Tint = Color.Black;
        }
        else
            NewPlant.Tint = Color.White;
    }

}

You are looping through 'plants' twice inside each other! Once using an index (for (int i = 0 ...) and then inside that again using an iterator (foreach (Plant NewPlant ...).

Your options are to either change GetInfo to set the right index by using a single loop:

msRect = ms;
for (int i = 0; i < plants.Count; i++)
{
    Plant NewPlant = plants[i];
    if (NewPlant.BoundingBox.Intersects(msRect))
    {
        SelectedIndex = i;
        NewPlant.Tint = Color.Black;
    }
    else
        NewPlant.Tint = Color.White;
}

Or do the same thing and short circuit the need for SelectPlant() and SelectedIndex in the first place:

msRect = ms;
foreach (Plant NewPlant in plants) // no need for indexes
{
    if (NewPlant.BoundingBox.Intersects(msRect))
    {
        SelectedPlant = NewPlant; // this is everything you need
        NewPlant.Tint = Color.Black;
    }
    else
        NewPlant.Tint = Color.White;
}

You do however need to be careful using a 'global' variable like SelectedPlant to capture this logic. You're better off changing the whole GetInfo method to return the selected plant, rather than having it modify SelectedPlant directly. That is, change the method signature to return Plant not void, and change SelectPlant = NewPlant in the code above to return NewPlant. Or for even more fun as a single line:

return plants.Where(p => p.BoundingBox.Intersects(ms))

Firstly make this a proper or || and check for >= plants.Count - remember that the list is indexed at 0. Then as suggested set it to count - 1:

if (SelectedIndex >= plants.Count || SelectedIndex < 0)
    SelectedIndex = plants.Count - 1

Need Your Help

Use Node.js and ExtJS to build an enterprise-grade grid-based single page application?

node.js extjs smartgwt singlepage

While the title suggests I wanted to cram as many buzzwords into the question as possible, I am actually serious ;)