Character

A sample class that inherits Entity and implements IHasAttributes, IHasSkills, and IHasInventory.

Instantiating a Character

The following scenario assumes we're writing code in some game management class (like the Game1 class in XNA) with an existing instance of the FigmentShare singleton mapped to a property named "Share". We'll demonstrate adding some attributes, skills, and so on to Share so that we can use them to build our Character. In this example we've created properties in our game manager for each of our attributes, which will significantly ease referencing them later on.

public void Initialize()
{
    // Core Attributes
    _attrVit = Share.CreateCoreAttribute("Vitality", "VIT", "");
    _attrStr = Share.CreateCoreAttribute("Strength", "STR", "");
    _attrAgi = Share.CreateCoreAttribute("Agility", "AGI", "");
    _attrDex = Share.CreateCoreAttribute("Dexterity", "DEX", "");
    _attrSen = Share.CreateCoreAttribute("Senses", "SEN", "");
    _attrInt = Share.CreateCoreAttribute("Intellect", "INT", "");
    _attrWil = Share.CreateCoreAttribute("Willpower", "WIL", "");
    _attrPer = Share.CreateCoreAttribute("Charisma", "PER", "");

    // Derived Attributes
    _attrHp = Share.CreateDerivedAttribute("Health", "HP", "");
    _attrMp = Share.CreateDerivedAttribute("Mana", "MP", "");
    _attrEn = Share.CreateDerivedAttribute("Energy", "EP", "");

    // Skill Groups
    var skGrpGeneral = Share.CreateSkillGroup("General", "Skills common to all entities.");
    var skGrpMedicine = Share.CreateSkillGroup("Medicine", "Skills which treat injuries and afflictions.");

    // Skills
    _skillAthletics = Share.CreateSkill("Athletics", "Aptitude at strenuous physical tasks.", skGrpGeneral);
    _skillSurvival = Share.CreateSkill("Survival", "Aptitude at surviving in the wilderness.", skGrpGeneral);
    _skillFirstAid = Share.CreateSkill("First Aid", "Aptitude at basic medical tasks.", skGrpMedicine);

    // Slots
    _slotPack = Share.CreateEquipmentSlot<Bag<LootBase>>("Pack");
    _slotBag1 = Share.CreateEquipmentSlot<Bag<LootBase>>("Bag 1");
    _slotBag2 = Share.CreateEquipmentSlot<Bag<LootBase>>("Bag 2");

    //---- So now we have some material in our FigmentShare instance, and our backing fields reference it. Our game manager class would 
             expose it via properties like "AttrStrength", "SlotPack", etc. From here we can start building characters.

    // Temp collections for our character

    var coreAttr = new Dictionary<CoreAttribute, ushort>();
    var dervAttr = new Dictionary<DerivedAttribute, Func<IHasCoreAttributes, IHasDerivedAttributes, IHasSkills, IHasInventory, ushort>>();
    var skills = new Dictionary<Skill, ushort>();
    var slots = new List<EquipmentSlot>();

    // Finally we'd add some items to each of the temp collections

    coreAttr.Add(AttrStrength, 10); // And so on...

    // Now we can create and initialize our character:

    var myChar = new Character(Share, "Joe", "Blow");

    foreach (var kv in coreAttr) { myChar.CoreAttributes.Add(kv.Key, kv.Value); }
    foreach (var kv in dervAttr) { myChar.DerivedAttributes.Add(kv.Key, kv.Value); }
    foreach (var kv in skills) { myChar.Skills.Add(kv.Key, kv.Value); }
    foreach (var v in slots) { myChar.Inventory.Add(v, null); }
} 

Now realistically you wouldn't want to shove all your implementing types' instantiation into your game manager. Instead you could use the implementor's constructor or a factory pattern. You can also use a template approach by subclassing the built-in EntityTemplate class, below.

EntityTemplate

This class provides the means to define CoreAttribute, DerivedAttribute, Skill, and Inventory collections once and use them for any Entity type that implements the respective interfaces. You override the LoadX methods appropriate for your implementor and then pass the template to the constructor of your Entity; the EntityTemplate checks to see if the entity implements the appropriate interfaces and populates those collections with the contents you defined.

public class WarriorTemplate : EntityTemplate
{
    private readonly Game1 _game;

    // EntityTemplate only requires a FigmentShare instance but we've added a parameter for our
    // game management class (Game1) for easier access to Share's data via Game1's properties.
    // We could also have used: share.GetCoreAttributes("Strength") to get the 'Strength' CoreAttribute, etc.

    public WarriorTemplate(FigmentShare share, Game1 game) : base(share)
    {
        _game = game;
    }

    protected override void AddCoreAttributes(IHasCoreAttributes implementor)
    {
        // Let's add all the CoreAttributes to our character, and then set values for specific ones.

        foreach (var a in Share.CoreAttributes) { CoreAttributes.Add(a, 10); }

        CoreAttributes[_game.AttrVit] = 16;
        CoreAttributes[_game.AttrStr] = 16;
        CoreAttributes[_game.AttrWil] = 12;
        CoreAttributes[_game.AttrAgi] = 14;
        CoreAttributes[_game.AttrDex] = 14;
    }

    protected override void AddDerivedAttributes(IHasDerivedAttributes implementor)
    {
        DerivedAttributes.Add(_game.AttrHp,
                                (c, d, s, i) => (ushort)(c.CoreAttributes[_game.AttrVit] + c.CoreAttributes[_game.AttrWil]));
        DerivedAttributes.Add(_game.AttrEn,
                                (c, d, s, i) => (ushort)(c.CoreAttributes[_game.AttrVit] + s.Skills[_game.SkillAthletics]));
    }

    protected override void AddSkills(IHasSkills implementor)
    {
        Skills.Add(_game.SkillAthletics, 10);
        Skills.Add(_game.SkillSurvival, 10);
        Skills.Add(_game.SkillFirstAid, 10);
    }

    protected override void AddEquipmentSlots(IHasInventory implementor)
    {
        Inventory.AddEquipmentSlot(_game.SlotPack);
        Inventory.AddEquipmentSlot(_game.SlotBag1);
        Inventory.AddEquipmentSlot(_game.SlotBag2);
    }
} 

Now you can simply pass this template to the constructor of your implementing type.
 Character myCharacter = new Character(game.Share, "Joe", "Blow", new WarriorTemplate(game.Share, game)); 

Last edited Aug 26, 2011 at 1:26 AM by Furiant, version 10