0

So let's say I have an interface in my game, IItem. IItem represents an object with a render component, a weight, and an attached "process". The process may be attached by someone using some ability to affect the item. Maybe something like an "enchantment" in an RPG. Let's say I want that attached process to be able to modify the weight of the IItem. However, I only want a process which is a component of the IItem to be able to do so. Objects outside of the IItem need to be able to get the weight though.

Do I implement a Weight property, with just a getter in the interface? Then does the base class implement a "SetWeight" method, which is declared internal to the Item namespace? That still does not really guarantee that only a component owned by the particular IItem can affect it of course, but at least something in the Input namespace cannot directly change the weight of an item. In C I could do something similar to linked lists in the kernel which can get their container, this would give me a way to make sure the weight and the process had the same container object, however, I do not see an easy way to do this in C#. Furthermore, I think it kind of damages the component based design for a component to have to have a reference back to its parent.

This is a general problem I am running in to as I design my interfaces and classes.

bodangly
  • 2,473
  • 17
  • 28

2 Answers2

1

Well, I think I get what you're talking about.

To ensure that your process, e.g. IEnchantment may take an IItem as a dependency(keep it as a member), and have IItem have Update/Set methods that take an IEnchantment, and then you can check that, for example:

public void Update(IEnchantment enchantment)
{
    if (enchantment.AttachedItem != this)
        throw new Exception("Cannot Update Item!");
    /*
     * Execute Updating Logics
     */
}

Here's one library I tried designing for my own games, maybe it can help you think about something. It's far from perfect, it's barely anything at all, but I hope it can help in any way.

Giora Guttsait
  • 1,279
  • 15
  • 29
  • This would work, but maintaining a reference back to the parent is something I want to avoid. It tightly couples the two classes in a way I am not particularly comfortable with. I guess I could pass "this" to the Update or SetWeight method in the IItem and make sure "this" IEnchantment is the same reference as the IEnchanment attached to the IItem that way. It feels like kind of a kludge too though. – bodangly Dec 05 '15 at 22:50
  • Or, you can do the other way around. I would guess that enchantments are kept in some list or in some way and are not totally deattached from the IItem, so you can: `if (ActiveEnchantments.Contains(enchantment)) { };`. More information about the classes might help – Giora Guttsait Dec 05 '15 at 22:52
1

Have you considered using a Dictionary, instead of properties? Something like this:

// Item base class
public Dictionary<string, object> Attributes { get; private set; }

public List<Process> Enchantments { get; private set; }

public virtual T Get<T>(string identifier)
{
    var att = this.Attributes.FirstOrDefault(att => att.Key == identifier);
    if (att == null) throw new MissingAttributeExeption(identifier); // Or perhaps just return default(T)
    if ((att.Value is T) == false) throw new InvalidAttributeCastException(identifier, typeof(T));  

    var value = att.Value;
    foreach (var ench in this.Enchantments)
    {
        ench.Modify(identifier, ref value);
    }

    return value as T; // Maybe you need to cast value to Object, and then to T, I can't remember.
}

// Process class

public string ValueToModify { get; set }

public virtual void Modify(string identifier, ref object value)
{
    if (identifier != this.ValueToModify) return;

    // In an inherited class, for example a Weightless-Enchantment: Halfs all weight
    var castedVal = value as int
    value = castedVal / 2;
    // Now this one item weights 50% of normal weight, and the original value is still stored in Item's Attributes dictionary.
}

// Some random class

public void Update()
{
    var totalWeight = 0;
    foreach (var item in this.itemCollection)
    {
        int weight = item.Get<int>("Weight");
        totalWeight += weight;
    }
    Console.WriteLine("The player weights: {0}", totalWeight);
}

Obviously this means you can't really hardcode attributes, but... Do you ACTUALLY want to do this, in the long run? I mean once you start adding Intelligence, Agility, Strength, Stamina, Endurance, etc.

I know this doesn't solve your posed question, but I should think it's a pretty decent alternative.

Falgantil
  • 1,290
  • 12
  • 27
  • I like this as it is pretty elegant. I worry about doing a lot of string comparisons though, but I guess I could key things by numbers instead. On the other hand, it is a turn based game so it would not be doing lots of comparisons at once in real time (except maybe for AI processes running in the background). – bodangly Dec 09 '15 at 19:25
  • When you say you "worry about doing a lot of string comparisons", are you talking performance-wise, or that you think it will be a hassle while you're actually developing the game? Because I don't think you need to worry about performance in this case. Comparing strings isn't a heavy operation at all. – Falgantil Dec 10 '15 at 07:28