Legionnaire (C#/XNA)

Spring 2010 | Project Length: 9 weeks (Coursework)

A Metroidvania game programmed from scratch in C# 3.0 using the XNA 3.1 framework

  • Worked as a group lead (group of 4 total): scheduling, setting goals and setting up SVN and communication
  • Responsibilities: programming basic engine framework (esp. design and code stubbing), custom data structures (esp. hash table), sound, animations, data I/O, merging/cleaning code

Lessons Learned

  • Clean up bugs as you see them, rather than hacking in a fix. Our collision detection broke early and often. Once we changed over to our final animations, the player collision for touching the platform was way off with no easy fix.
  • Stay organized! We managed to get as much done as we did because we regularly held group meetings and set goals and objectives; however, we would fall behind when we failed to set tasks and deadlines.
  • XNA is pretty flexible and powerful!

Downloads

Legionnaire’s Manual (PDF – 678kb)

Source Code (ZIP – 151mb)

Installer (ZIP – 71.6mb)

Screenshots

The start screen in Legionnaire Screenshot of Legionnaire's gameplay, with a powerup on-screen Screenshot of gameplay in Legionnaire Screenshot of Legionnaire's menu screen Screenshot of Legionnaire's game over screen

Code Sample

For part of this project, I coded Tile Sets to allow us to change the appearance of levels on the fly. Here is an example tileset:

. intangible
# Content\\Blocks\\wallblock.png solid
- Content\\Blocks\\platform.png p

Here is the source code for the Tile Set class, which is utilized like a Dictionary:


using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework.Graphics;

namespace Metroidvania
{
    /// <summary>
    /// Implements tile sets for different levels using a dictionary interface.
    ///
    /// Author: Zachary Hoefler
    /// </summary>
    /// <remarks>
    /// This allows for easy re-skinning of levels, and greater
    /// variety in tile appearances available. It also enables
    /// cusomizable map file formats without having to hard code them.
    /// </remarks>
    class Tileset : IDictionary
    {
        public const int DictionarySize = 10007;
        private int count;

        List<Tile>[] values;
        List<char>[] keys;

        public Tileset()
        {
            values = new List[DictionarySize];
            keys = new List[DictionarySize];
            count = 0;

            for (int i = 0; i < DictionarySize; ++i)
            {
                values[i] = new List(1);
                keys[i] = new List(1);
            }
        }

        /// <summary>
        /// Test method
        /// </summary>
        public static void Main(string[] args)
        {
            Console.WriteLine("Testing Tileset class!");
            Console.WriteLine("If it all works, everything should return true!\n");
            Tileset tileset = new Tileset();
            tileset.Add('x', new Tile());
            tileset.Add('_', new Tile());

            Console.Write("Testing ContainsKey...");
            Console.Write(tileset.ContainsKey('x') + ", ");
            Console.WriteLine(tileset.ContainsKey('_'));

            tileset.Remove('_');

            Console.WriteLine("Testing remove... " + tileset.ContainsKey('x') + ", "
                + !tileset.ContainsKey('_'));

            tileset['p'] = new Tile();

            Console.WriteLine("Testing indexer set... " + tileset.ContainsKey('p'));
            Console.WriteLine("Checking indexer get... " + tileset['p']);

            try
            {
                Console.Write("Checking for nonexistant key...");
                Console.WriteLine(tileset['&'] + "SHOULD NOT PRINT");
            }
            catch (KeyNotFoundException knfe)
            {
                Console.WriteLine(knfe.Message);
            }

            Console.WriteLine("Test complete!");
            Console.ReadLine();
        }

        /// <summary>
        /// Loads in the key-value pairs for the tileset
        /// from a properly formatted file
        /// </summary>
        /// <remarks>
        /// FORMAT:
        /// char texture tangibility
        /// -or-
        /// char tangibility (for textureless tiles)
        /// EXAMPLES:
        /// x wall.png t
        /// _ platform.png p
        /// </remarks>
        /// <param name="<span class=" />filepath">The path of the file to load
        public static Tileset LoadFromFile(string filepath, GraphicsDevice graphicsDevice)
        {
            List tileData = new List();
            Tileset myTileset = new Tileset();

            using (TextReader textReader = new StreamReader(filepath))
            {
                string nextLine = textReader.ReadLine();
                while (nextLine != null)
                {
                    tileData.Add(nextLine);
                    nextLine = textReader.ReadLine();
                }
            }

            foreach (string s in tileData)
            {
                string[] tokens = s.Split(' ');

                if (tokens.Length == 2) // no texture
                {
                    char tileChar = (tokens[0].ToCharArray())[0];

                    TileTangibility tangibility = GetTangibility(tokens[1]);
                    myTileset.Add(tileChar, new Tile(null, tangibility));
                }
                if (tokens.Length == 3) // textured
                {
                    // tokens[0] == tile character
                    // tokens[1] == filename
                    // tokens[2] == tangibility
                    char tileChar = (tokens[0].ToCharArray())[0];

                    Texture2D texture;

                    try
                    {
                        texture = Texture2D.FromFile(graphicsDevice, tokens[1]);
                    }
                    catch (FileNotFoundException fnfe)
                    {
                        Console.WriteLine("Couldn't find the texture!");
                        throw fnfe;
                    }

                    TileTangibility tangibility = GetTangibility(tokens[2]);
                    Tile myTile = new Tile(texture, tangibility);

                    myTileset.Add(tileChar, myTile);
                }
            }

            return myTileset;
        }

        /// <summary>
        /// Helper method for loading in a Tileset from a file. Converts
        /// string to tile tangibility. Returns intangible by default if
        /// nothing matches.
        /// </summary>
        /// <remarks>
        /// Provides a lot of redundancy to allow flexibility in
        /// file naming conventions.
        /// </remarks>
        private static TileTangibility GetTangibility(string tangibility)
        {
            tangibility = tangibility.ToLower();
            switch (tangibility)
            {
                case "s":
                    return TileTangibility.Solid;
                case "t": // t for tangible
                    return TileTangibility.Solid;
                case "solid":
                    return TileTangibility.Solid;
                case "i":
                    return TileTangibility.Intangible;
                case "intangible":
                    return TileTangibility.Intangible;
                case "p":
                    return TileTangibility.Platform;
                case "platform":
                    return TileTangibility.Platform;
                default:
                    return TileTangibility.Intangible;
            }
        }

        /// <summary>
        /// Gets the hash code of a character
        /// </summary>
        /// <returns>The character's hash code</returns>
        public static int GetHash(char key)
        {
            return (int)key;
        }

        #region IDictionary<char,Tile> Members

        /// <summary>
        /// Adds a new key-value pair to the tileset
        /// </summary>
        /// <param name="key">The char used to represent a tile</param>
        /// <param name="value">The Tile associated with the key char</param>
        /// An element with the same key already exists in the .
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
        public void Add(char key, Tile value)
        {
            if (ContainsKey(key))
                throw new ArgumentException();

            if (IsReadOnly)
                throw new NotSupportedException();

            int hashCode = GetHash(key);
            keys[hashCode].Add(key);
            values[hashCode].Add(value);
            ++count;
        }

        /// <summary>
        /// Determines whether the  contains an element with the specified key.
        /// </summary>
        /// <param name="key" />The key to locate in the .
        /// <returns>
        /// true if the  contains an element with the key; otherwise, false.
        /// </returns>
        public bool ContainsKey(char key)
        {
            foreach (List charList in keys)
            {
                foreach (char tileKey in charList)
                {
                    if (tileKey.Equals(key))
                        return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <value></value>
        /// <returns>An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.</returns>
        public ICollection<char> Keys
        {
            get
            {
                char[] keyArray = new char[count];

                int currentIndex = 0;
                foreach (List charList in keys)
                {
                    foreach (char c in charList)
                    {
                        keyArray[currentIndex] = c;
                        ++currentIndex;
                    }
                }

                return keyArray;
            }
        }

        /// <summary>
        /// Removes the element with the specified key from the .
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <returns>
        /// true if the element is successfully removed; otherwise, false.  This method also returns false if <paramref name="key"/> was not found in the original .
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
        public bool Remove(char key)
        {
            if (IsReadOnly)
                throw new NotSupportedException();

            int hashCode = GetHash(key);
            List keyList = keys[hashCode];
            List valueList = values[hashCode];

            // find key location
            for (int i = 0; i < keyList.Count; ++i)
            {
                if (keyList[i].Equals(key))
                { // found; remove key and value
                    keyList.RemoveAt(i);
                    valueList.RemoveAt(i);
                    --count;
                    return true;
                }
            }

            // key not in tileset
            return false;
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key whose value to get.</param>
        /// <param name="value" />When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.
        /// <returns>
        /// true if the object that implements  contains an element with the specified key; otherwise, false.
        /// </returns>
        public bool TryGetValue(char key, out Tile value)
        {
            if (ContainsKey(key))
            {
                value = this[key];
                return true;
            }
            else
            {
                value = Tile.Empty;
                return false;
            }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <value></value>
        /// <returns>An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/>.</returns>
        public ICollection<Tile> Values
        {
            get
            {
                Tile[] tileArray = new Tile[count];

                int currentIndex = 0;
                foreach (List tileList in values)
                {
                    foreach (Tile t in tileList)
                    {
                        tileArray[currentIndex] = t;
                        ++currentIndex;
                    }
                }

                return tileArray;
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="Metroidvania.Tile"/> with the specified key.
        /// </summary>
        /// <value>The Tile associated with the passed-in key</value>
        public Tile this[char key]
        {
            get
            {
                return GetTileFromList(key);
            }
            set
            {
                Add(key, value);
            }
        }

        /// <summary>
        /// Helper method, gets a tile from a list
        /// </summary>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException`1"/>
        /// The requested key was not found in the collection.
        /// </exception>
        private Tile GetTileFromList(char key)
        {
            int hashCode = GetHash(key);
            List keyList = keys[hashCode];

            for (int i = 0; i < keyList.Count; ++i)
            {
                if (keyList[i].Equals(key))
                    return (values[hashCode])[i];
            }

            throw new KeyNotFoundException();

        }

        #endregion

        #region ICollection<KeyValuePair<char,Tile>> Members

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
        public void Add(KeyValuePair<char, Tile> item)
        {
            if (IsReadOnly)
                throw new NotSupportedException();

            Add(item.Key, item.Value);
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
        public void Clear()
        {
            if (IsReadOnly)
                throw new NotSupportedException();

            values = new List[DictionarySize];
            keys = new List[DictionarySize];
            count = 0;

            for (int i = 0; i < DictionarySize; ++i)
            {
                values[i] = new List(1);
                keys[i] = new List(1);
            }
        }

        /// <summary>
        /// Determines whether the  contains a specific value.
        /// </summary>
        /// <param name="item" />The object to locate in the .
        /// <returns>
        /// true if <paramref name="item"/> is found in the ; otherwise, false.
        /// </returns>
        public bool Contains(KeyValuePair item)
        {
            if (ContainsKey(item.Key) && this[item.Key].Equals(item.Value))
                return true;
            else
                return false;
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
        /// </summary>
        /// <param name="array" />The one-dimensional  that is the destination of the elements copied from . The  must have zero-based indexing.
        /// <param name="<span class=" />arrayIndex">The zero-based index in  at which copying begins.
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="array"/> is null.
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="arrayIndex"/> is less than 0.
        /// <exception cref="T:System.ArgumentException">
        /// 	<paramref name="array"/> is multidimensional.-or- is equal to or greater than the length of .-or-The number of elements in the source  is greater than the available space from  to the end of the destination .-or-Type  cannot be cast automatically to the type of the destination .
        public void CopyTo(KeyValuePair[] array, int arrayIndex)
        {
            if (array == null)
                throw new ArgumentNullException();

            if (arrayIndex < 0)
                throw new ArgumentOutOfRangeException();

            if (arrayIndex > array.GetUpperBound(0) ||
                (array.GetUpperBound(0) - arrayIndex) < count)
                throw new ArgumentException();

            foreach (KeyValuePair keyValPair in this)
            {
                array[arrayIndex++] = keyValPair;
            }
        }

        /// <summary>
        /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <value></value>
        /// <returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</returns>
        public int Count
        {
            get { return count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.</returns>
        public bool IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        /// <returns>
        /// true if <paramref name="item"/> was successfully removed from the ; otherwise, false. This method also returns false if  is not found in the original .
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
        public bool Remove(KeyValuePair item)
        {
            if (IsReadOnly)
                throw new NotSupportedException();

            if (ContainsKey(item.Key) && this[item.Key].Equals(item.Value))
            {
                Remove(item.Key);
                return true;
            }
            else
                return false;
        }

        #endregion

        #region IEnumerable<KeyValuePair<char,Tile>> Members

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A  that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator> GetEnumerator()
        {
            for (int i = 0; i < DictionarySize; ++i)
            {
                for (int j = 0; j < keys[i].Count; ++j)
                {
                    yield return new KeyValuePair(keys[i][j], values[i][j]);
                }
            }
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (List tileList in values)
            {
                foreach (Tile tile in tileList)
                {
                    yield return tile;
                }
            }
        }

        #endregion
    }
}

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework.Graphics; 

namespace Metroidvania
{
/// <summary>
/// Implements tile sets for different levels using a dictionary interface.
///
/// Author: Zachary Hoefler
/// </summary>
/// <remarks>
/// This allows for easy re-skinning of levels, and greater
/// variety in tile appearances available. It also enables
/// cusomizable map file formats without having to hard code them.
/// </remarks>
class Tileset : IDictionary
{
public const int DictionarySize = 10007;
private int count;

List<Tile>[] values;
List<char>[] keys;

public Tileset()
{
values = new List[DictionarySize];
keys = new List[DictionarySize];
count = 0;

for (int i = 0; i < DictionarySize; ++i)
{
values[i] = new List(1);
keys[i] = new List(1);
}
}

/// <summary>
/// Test method
/// </summary>
public static void Main(string[] args)
{
Console.WriteLine(“Testing Tileset class!”);
Console.WriteLine(“If it all works, everything should return true!\n”);
Tileset tileset = new Tileset();
tileset.Add(‘x’, new Tile());
tileset.Add(‘_’, new Tile());

Console.Write(“Testing ContainsKey…”);
Console.Write(tileset.ContainsKey(‘x’) + “, “);
Console.WriteLine(tileset.ContainsKey(‘_’));

tileset.Remove(‘_’);

Console.WriteLine(“Testing remove… ” + tileset.ContainsKey(‘x’) + “, “
+ !tileset.ContainsKey(‘_’));

tileset['p'] = new Tile();

Console.WriteLine(“Testing indexer set… ” + tileset.ContainsKey(‘p’));
Console.WriteLine(“Checking indexer get… ” + tileset['p']);

try
{
Console.Write(“Checking for nonexistant key…”);
Console.WriteLine(tileset['&'] + “SHOULD NOT PRINT”);
}
catch (KeyNotFoundException knfe)
{
Console.WriteLine(knfe.Message);
}

Console.WriteLine(“Test complete!”);
Console.ReadLine();
}

/// <summary>
/// Loads in the key-value pairs for the tileset
/// from a properly formatted file
/// </summary>
/// <remarks>
/// FORMAT:
/// char texture tangibility
/// -or-
/// char tangibility (for textureless tiles)
/// EXAMPLES:
/// x wall.png t
/// _ platform.png p
/// </remarks>
/// <param name=”filepath”>The path of the file to load</param>
public static Tileset LoadFromFile(string filepath, GraphicsDevice graphicsDevice)
{
List<string> tileData = new List<string>();
Tileset myTileset = new Tileset();

using (TextReader textReader = new StreamReader(filepath))
{
string nextLine = textReader.ReadLine();
while (nextLine != null)
{
tileData.Add(nextLine);
nextLine = textReader.ReadLine();
}
}

foreach (string s in tileData)
{
string[] tokens = s.Split(‘ ‘);

if (tokens.Length == 2) // no texture
{
char tileChar = (tokens[0].ToCharArray())[0];

TileTangibility tangibility = GetTangibility(tokens[1]);
myTileset.Add(tileChar, new Tile(null, tangibility));
}
if (tokens.Length == 3) // textured
{
// tokens[0] == tile character
// tokens[1] == filename
// tokens[2] == tangibility
char tileChar = (tokens[0].ToCharArray())[0];

Texture2D texture;

try
{
texture = Texture2D.FromFile(graphicsDevice, tokens[1]);
}
catch (FileNotFoundException fnfe)
{
Console.WriteLine(“Couldn’t find the texture!”);
throw fnfe;
}

TileTangibility tangibility = GetTangibility(tokens[2]);
Tile myTile = new Tile(texture, tangibility);

myTileset.Add(tileChar, myTile);
}
}

return myTileset;
}

/// <summary>
/// Helper method for loading in a Tileset from a file. Converts
/// string to tile tangibility. Returns intangible by default if
/// nothing matches.
/// </summary>
/// <remarks>
/// Provides a lot of redundancy to allow flexibility in
/// file naming conventions.
/// </remarks>
private static TileTangibility GetTangibility(string tangibility)
{
tangibility = tangibility.ToLower();
switch (tangibility)
{
case “s”:
return TileTangibility.Solid;
case “t”: // t for tangible
return TileTangibility.Solid;
case “solid”:
return TileTangibility.Solid;
case “i”:
return TileTangibility.Intangible;
case “intangible”:
return TileTangibility.Intangible;
case “p”:
return TileTangibility.Platform;
case “platform”:
return TileTangibility.Platform;
default:
return TileTangibility.Intangible;
}
}

/// <summary>
/// Gets the hash code of a character
/// </summary>
/// <returns>The character’s hash code</returns>
public static int GetHash(char key)
{
return (int)key;
}

#region IDictionary<char,Tile> Members

/// <summary>
/// Adds a new key-value pair to the tileset
/// </summary>
/// <param name=”key”>The char used to represent a tile</param>
/// <param name=”value”>The Tile associated with the key char</param>
/// An element with the same key already exists in the .
/// <exception cref=”T:System.NotSupportedException”>The <see cref=”T:System.Collections.Generic.IDictionary`2″/> is read-only.</exception>
public void Add(char key, Tile value)
{
if (ContainsKey(key))
throw new ArgumentException();

if (IsReadOnly)
throw new NotSupportedException();

int hashCode = GetHash(key);
keys[hashCode].Add(key);
values[hashCode].Add(value);
++count;
}

/// <summary>
/// Determines whether the contains an element with the specified key.
/// </summary>
/// The key to locate in the .
/// <returns>
/// true if the contains an element with the key; otherwise, false.
/// </returns>
public bool ContainsKey(char key)
{
foreach (List charList in keys)
{
foreach (char tileKey in charList)
{
if (tileKey.Equals(key))
return true;
}
}

return false;
}

/// <summary>
/// Gets an <see cref=”T:System.Collections.Generic.ICollection`1″/> containing the keys of the <see cref=”T:System.Collections.Generic.IDictionary`2″/>.
/// </summary>
/// <value></value>
/// <returns>An <see cref=”T:System.Collections.Generic.ICollection`1″/> containing the keys of the object that implements <see cref=”T:System.Collections.Generic.IDictionary`2″/>.</returns>
public ICollection<char> Keys
{
get
{
char[] keyArray = new char[count];

int currentIndex = 0;
foreach (List charList in keys)
{
foreach (char c in charList)
{
keyArray[currentIndex] = c;
++currentIndex;
}
}

return keyArray;
}
}

/// <summary>
/// Removes the element with the specified key from the .
/// </summary>
/// <param name=”key”>The key of the element to remove.</param>
/// <returns>
/// true if the element is successfully removed; otherwise, false.  This method also returns false if <paramref name=”key”/> was not found in the original .
/// </returns>
/// <exception cref=”T:System.NotSupportedException”>The <see cref=”T:System.Collections.Generic.IDictionary`2″/> is read-only.</exception>
public bool Remove(char key)
{
if (IsReadOnly)
throw new NotSupportedException();

int hashCode = GetHash(key);
List keyList = keys[hashCode];
List valueList = values[hashCode];

// find key location
for (int i = 0; i < keyList.Count; ++i)
{
if (keyList[i].Equals(key))
{ // found; remove key and value
keyList.RemoveAt(i);
valueList.RemoveAt(i);
–count;
return true;
}
}

// key not in tileset
return false;
}

/// <summary>
/// Gets the value associated with the specified key.
/// </summary>
/// <param name=”key”>The key whose value to get.</param>
/// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name=”value”/> parameter. This parameter is passed uninitialized.
/// <returns>
/// true if the object that implements contains an element with the specified key; otherwise, false.
/// </returns>
public bool TryGetValue(char key, out Tile value)
{
if (ContainsKey(key))
{
value = this[key];
return true;
}
else
{
value = Tile.Empty;
return false;
}
}

/// <summary>
/// Gets an <see cref=”T:System.Collections.Generic.ICollection`1″/> containing the values in the <see cref=”T:System.Collections.Generic.IDictionary`2″/>.
/// </summary>
/// <value></value>
/// <returns>An <see cref=”T:System.Collections.Generic.ICollection`1″/> containing the values in the object that implements <see cref=”T:System.Collections.Generic.IDictionary`2″/>.</returns>
public ICollection<Tile> Values
{
get
{
Tile[] tileArray = new Tile[count];

int currentIndex = 0;
foreach (List tileList in values)
{
foreach (Tile t in tileList)
{
tileArray[currentIndex] = t;
++currentIndex;
}
}

return tileArray;
}
}

/// <summary>
/// Gets or sets the <see cref=”Metroidvania.Tile”/> with the specified key.
/// </summary>
/// <value>The Tile associated with the passed-in key</value>
public Tile this[char key]
{
get
{
return GetTileFromList(key);
}
set
{
Add(key, value);
}
}

/// <summary>
/// Helper method, gets a tile from a list
/// </summary>
/// <exception cref=”T:System.Collections.Generic.KeyNotFoundException`1″/>
/// The requested key was not found in the collection.
/// </exception>
private Tile GetTileFromList(char key)
{
int hashCode = GetHash(key);
List keyList = keys[hashCode];

for (int i = 0; i < keyList.Count; ++i)
{
if (keyList[i].Equals(key))
return (values[hashCode])[i];
}

throw new KeyNotFoundException();

}

#endregion

#region ICollection<KeyValuePair<char,Tile>> Members

/// <summary>
/// Adds an item to the <see cref=”T:System.Collections.Generic.ICollection`1″/>.
/// </summary>
/// <param name=”item”>The object to add to the <see cref=”T:System.Collections.Generic.ICollection`1″/>.</param>
/// <exception cref=”T:System.NotSupportedException”>The <see cref=”T:System.Collections.Generic.ICollection`1″/> is read-only.</exception>
public void Add(KeyValuePair<char, Tile> item)
{
if (IsReadOnly)
throw new NotSupportedException();

Add(item.Key, item.Value);
}

/// <summary>
/// Removes all items from the <see cref=”T:System.Collections.Generic.ICollection`1″/>.
/// </summary>
/// <exception cref=”T:System.NotSupportedException”>The <see cref=”T:System.Collections.Generic.ICollection`1″/> is read-only. </exception>
public void Clear()
{
if (IsReadOnly)
throw new NotSupportedException();

values = new List[DictionarySize];
keys = new List[DictionarySize];
count = 0;

for (int i = 0; i < DictionarySize; ++i)
{
values[i] = new List<Tile>(1);
keys[i] = new List<char>(1);
}
}

/// <summary>
/// Determines whether the contains a specific value.
/// </summary>
/// The object to locate in the .
/// <returns>
/// true if <paramref name=”item”/> is found in the ; otherwise, false.
/// </returns>
public bool Contains(KeyValuePair item)
{
if (ContainsKey(item.Key) && this[item.Key].Equals(item.Value))
return true;
else
return false;
}

/// <summary>
/// Copies the elements of the <see cref=”T:System.Collections.Generic.ICollection`1″/> to an <see cref=”T:System.Array”/>, starting at a particular <see cref=”T:System.Array”/> index.
/// </summary>
/// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing.
/// The zero-based index in <paramref name=”array”/> at which copying begins.
/// <exception cref=”T:System.ArgumentNullException”>
///     <paramref name=”array”/> is null.
/// <exception cref=”T:System.ArgumentOutOfRangeException”>
///     <paramref name=”arrayIndex”/> is less than 0.
/// <exception cref=”T:System.ArgumentException”>
///     <paramref name=”array”/> is multidimensional.-or- is equal to or greater than the length of .-or-The number of elements in the source is greater than the available space from to the end of the destination .-or-Type cannot be cast automatically to the type of the destination .
public void CopyTo(KeyValuePair[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException();

if (arrayIndex < 0)
throw new ArgumentOutOfRangeException();

if (arrayIndex > array.GetUpperBound(0) ||
(array.GetUpperBound(0) – arrayIndex) < count)
throw new ArgumentException();

foreach (KeyValuePair keyValPair in this)
{
array[arrayIndex++] = keyValPair;
}
}

/// <summary>
/// Gets the number of elements contained in the <see cref=”T:System.Collections.Generic.ICollection`1″/>.
/// </summary>
/// <value></value>
/// <returns>The number of elements contained in the <see cref=”T:System.Collections.Generic.ICollection`1″/>.</returns>
public int Count
{
get { return count; }
}

/// <summary>
/// Gets a value indicating whether the <see cref=”T:System.Collections.Generic.ICollection`1″/> is read-only.
/// </summary>
/// <value></value>
/// <returns>true if the <see cref=”T:System.Collections.Generic.ICollection`1″/> is read-only; otherwise, false.</returns>
public bool IsReadOnly
{
get { return false; }
}

/// <summary>
/// Removes the first occurrence of a specific object from the <see cref=”T:System.Collections.Generic.ICollection`1″/>.
/// </summary>
/// <param name=”item”>The object to remove from the <see cref=”T:System.Collections.Generic.ICollection`1″/>.</param>
/// <returns>
/// true if <paramref name=”item”/> was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original .
/// </returns>
/// <exception cref=”T:System.NotSupportedException”>The <see cref=”T:System.Collections.Generic.ICollection`1″/> is read-only.</exception>
public bool Remove(KeyValuePair item)
{
if (IsReadOnly)
throw new NotSupportedException();

if (ContainsKey(item.Key) && this[item.Key].Equals(item.Value))
{
Remove(item.Key);
return true;
}
else
return false;
}

#endregion

#region IEnumerable<KeyValuePair<char,Tile>> Members

/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A that can be used to iterate through the collection.
/// </returns>
public IEnumerator<KeyValuePair<char, Tile>> GetEnumerator()
{
for (int i = 0; i < DictionarySize; ++i)
{
for (int j = 0; j < keys[i].Count; ++j)
{
yield return new KeyValuePair<char, Tile>(keys[i][j], values[i][j]);
}
}
}

#endregion

#region IEnumerable Members

/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An object that can be used to iterate through the collection.
/// </returns>
IEnumerator IEnumerable.GetEnumerator()
{
foreach (List tileList in values)
{
foreach (Tile tile in tileList)
{
yield return tile;
}
}
}

#endregion
}
}

Follow

Get every new post delivered to your Inbox.

Join 672 other followers

%d bloggers like this: