Reputation:
I'm working on a player inventory system for a game.
I have a struct Slot which has a List<Loot> collection that represents which kinds of items are allowed in it. The abstract class Loot is subclassed by all items which are lootable - i.e.: would be valid Content values for the Slot struct.
I want to express that a Slot can have restrictions on what subclasses of Loot it can contain. For instance, if the Slot represents an ammo container, I want it to only hold Loot subclasses which are ammo containers, like "Quivers" and "Shot Pouches" (which would subclass Container somewhere along the line).
Loot class
public abstract class Loot : GameEntity, ILootable
{
public int MaxUnitsPerStack { get; set; }
public int MaxUnitsCarriable { get; set; }
public int MaxUnitsOwnable { get; set; }
public void Stack() { }
public void Split() { }
public void Scrap() { }
}
Container class
public abstract class Container : Loot
{
public List<Slot> Slots { get; set; }
public Container(int slots)
{
this.Slots = new List<Slot>(slots);
}
}
Slot struct
public struct Slot
{
public Loot Content;
public int Count;
public List<Loot> ExclusiveLootTypes;
public Slot(Loot[] exclusiveLootTypes)
{
this.Content = null;
this.Count = 0;
List<Loot> newList;
if (exclusiveLootTypes.Count() > 0)
{
newList = new List<Loot>(exclusiveLootTypes.Count());
foreach (Loot l in exclusiveLootTypes)
{
newList.Add(l);
}
}
else { newList = new List<Loot>(); }
this.ExclusiveLootTypes = newList;
}
}
PlayerInventory
public struct PlayerInventory
{
static Dictionary<Slot, string> Slots;
static void Main()
{
Slots = new Dictionary<Slot, string>();
/* Add a bunch of slots */
Slots.Add(new Slot(/* I don't know the
syntax for this:
Quiver, Backpack */), "Backpack"); // Container
}
}
I don't know how to provide the arguments for the Loot subclasses in the Slot constructor call in the Main method of the PlayerInventory class.
I hope this is clear. Thanks in advance.
EDIT
I was able to solve this (and by that, I mean get it to compile) using David Sieler's approach along with some Reflection.
Slot struct
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
public struct Slot
{
private Loot _content;
private int _count;
public List ExclusiveLootTypes;
public Loot Content
{
get { return _content; }
private set
{
if ((ExclusiveLootTypes.Contains(value.GetType())) && (value.GetType().IsSubclassOf(Type.GetType("Loot"))))
{
_content = value;
}
}
}
public int Count
{
get { return _count; }
set { _count = value; }
}
public Slot(params Type[] exclusiveLootTypes)
{
this._content = null;
this._count = 0;
List newList;
if (exclusiveLootTypes.Count() > 0)
{
newList = new List(exclusiveLootTypes.Count());
foreach (Type l in exclusiveLootTypes)
{
newList.Add(l);
}
}
else { newList = new List(); }
this.ExclusiveLootTypes = newList;
}
}
PlayerInventory call to Slot constructor
Slots.Add(new Slot(typeof(Backpack)));
Thanks again everyone for the discussion.
Upvotes: 2
Views: 882
Reputation: 71856
It sounds like your object design needs a slight adjustment.
What if the types of loot items were also interfaces, so for example, all ammo loot items inherit from IAmmoContainer.
Then you could pass in the IAmmoContainer type to restrict the slot.
public class Quiver : Container, IAmmoContainer
public class ShotPouch : Container, IAmmoContainer
// ...
new Slot(typeof(IAmmoContainer))
EDIT
Based on the discussion in the comments, here's how I'd go about designing this system.
The Loot
class is fine. It represents the base of the loot hierarchy.
Then you define interfaces for the "slots" that an item can occupy. For example:
public class LongSword : Loot, IRightHandItem
public class ShortSword : Loot, IRightHandItem, ILeftHandItem
Then the PlayerInventory class has "slots" as properties, that are restricted to the appropriate type.
public class PlayerInventory
{
public List<IRightHandItem> RightHandSlot { get; private set; }
public List<ILeftHandItem> LeftHandSlot { get; private set; }
// etc...
}
Upvotes: 2
Reputation: 133
and to combine the previous suggestion to use params, you can use a LINQ extension to convert the params into a List in one step:
public Slot(params Type[] exclusiveLootTypes)
{
this.Content = null;
this.Count = 0;
this.ExclusiveLootTypes = exclusiveLootTypes.ToList();
}
Upvotes: 1
Reputation: 9705
One approach would be to pass an array of Type
objects to the Slot constructor:
public Slot(Type[] exclusiveLootTypes) {
// be sure and check that each Type is a subtype of Loot!
}
// Construction looks like:
new Slot(new[] {GetType(AmmoContainer), GetType(GemContainer) /* or whatever */});
Then write a property setter for Content
that checks the type of the assigned object, and signals some sort of error if that type isn't contained in ExclusiveLootTypes.
Upvotes: 1
Reputation: 59973
You'd likely find it easier to use params
in the definition for the Slot constructor:
Slot(params Loot[] exclusiveLootTypes)
This would allow you to call it like:
new Slot(lootItem1, lootItem2, lootItem2 /*...etc*/);
Otherwise, you'd need to create an array and pass it in.
Upvotes: 2