YOUR BROWSER IS TOO SKINNY!

Next

Sword of Something Dev Blog 2

Saturday, July 8, 2017

After watching Javascript Developer MPJ talk about using composition over inheritence, I realised that would work better for Sword of Something.

I currently have around 400 items (weapons, loot, monsters, quest-givers, places) and I don't want to define their properties for each one, I want to be able to say that this 'sword' is a type of 'medium range weapon', which is a type of 'weapon', which is a type of 'equippable'. But I don't want to create a fixed hierarchy of all the possible types of object, because when I change my mind about how that hierarchy should be structured, it is painful to re-configure it. Instead I want to be able to pull in any kind of properties and behaviours on the fly, to say this 'sword' is a type of 'quest item' and 'junk' and 'loot' and 'damagable', without having to care about how those types relate to each other.

So I've built a system in javascript that does this.

 

// item constructor
constructor(data){
  // I want to apply the properties from incoming 'data' to this new item.
  // I start by applying any types specified in data.
  applyTypes(data.types);
  // Finally apply the specific properties, that override any generic things from 'types'.
  applyPropertiesToThis(data);
}
applyTypes(types){
 // There can be many 'types' that need applying, and these might have their own 'types'.
 types.foreach(type => {
  if(type.types){
   applyTypes(type.types); // I would also check we haven't already applied this type.
  }
  applyPropertiesToThis(type); // Do a straight copy of all the properties from the 'type' to this item.
  this.typesApplied.push(type); // Remember what types have been applied.
 });
}

So if you didn't get that, it can take data like this:

{
 "name":"Beer",
 "itemType":"Drink,Quest Item",
 "icon":"food-beer",
 "actionConsume": {
  "healthChange":10,
  "text":"Hic! +Health 10"
 }
}


{
 "name":"Drink",
 "itemType":"Consumable"
}


{
 "name":"Consumable",
 "itemType":"Consumable"
 "verbs":"Consume",
 "actionConsume": {
  "healthChange":50,
  "text":"Nourishing! +Health 50"
 }
 "cost":10
}

{
 "name":"Quest Item",
 "xp":50
}

We see that Beer is a 'Drink', so we copy in the properties of 'Drink', which is 'Consumable' so we copy in the properties of consumable. This includes the default health change of 50.

We also see that Beer is a 'Quest Item', so we copy in the default 'xp' from their, which gives the player an experience reward or something.

Finally we apply the specific properties of the beer, eg the 'icon' is 'food-beer.png'. Also, because beer has it's own 'actionConsume' which is more specific than the 'Consumable' we override this.

We end up with an object like this:

{
 "name":"Beer",
 "itemType":"Drink,Quest Item,Consumable",
 "icon":"food-beer",
 "verbs":"Consume",
 "actionConsume": {
  "healthChange":10,
  "text":"Hic! +Health 10"
 }
 "xp":50,
 "cost":10
}

In the same way that CSS goes up the inheritance chain and applies properties, and allows more local, specific properties to override general ones.

What if I'd have done this using just plain old inheritance, and made a load of classes to handle this?

So, I was pretty pleased with the end result. I can now quite quickly make things and types of things and types of types of things. And it is all about making things easy for the level designers who have to build something with my system. (At least, if I had any level designers it would be.)
The only current drawbacks are that it isn't too easy to specify what a thing can not do. If you wanted some FREE beer that has no cost, but still has all the other properties of 'Consumable', you would have to actively state "cost":0 in the beer. Easy enough for properties, less so for cumulative lists. But this hasn't been a big issue yet.

Tags: Sword of Something Angular Javascript