Author Topic: Handling item properties  (Read 23117 times)

ghost

  • Newcomer
  • Posts: 1
  • Karma: +0/-0
    • View Profile
Handling item properties
« on: July 29, 2009, 10:02:44 PM »
I'm trying to find a good generic way to implement item properties for my roguelike (using C#). Right now I have an Item class that contains the basic information shared by all Items (name, etc). I would like a way to store a list of properties, but I want to leave open the implementation of those properties.

I thought about creating an ItemProperties class, and having each Item contain a List<ItemProperties>. This would avoid a whole mess of flags in the item class. The problem I'm having is that I'm not sure how to go about designing the ItemProperties class itself. Lets say two Item Properties are IsWeapon and ProcsOnHit:

For IsWeapon, I probably want to store the weapon type, damage, # of hands to wield it, etc.
For ProcsOnHit, I probably want to store an % chance to proc, and maybe a reference to some type of effect.

How can I 'fit' both these into the ItemProperties class? Subclassing seems to defeat the purpose of having a generic ItemProperties class in the first place..

Vanguard

  • Rogueliker
  • ***
  • Posts: 1112
  • Karma: +0/-0
    • View Profile
Re: Handling item properties
« Reply #1 on: August 02, 2009, 06:34:28 PM »
What I'm doing is using a boolean variable for whether each enchantment on an item affects whatever trait I'm working with.  I'm not sure how well that explains anything, so I'll use an example:  I have a boolean named modifiesAttributes, and only for enchantments where that's true does it check how the item affects the character's strength, agility, speed, etc.  That way I only need to actually assign attribute values to the enchantments that actually modify them.

I don't know if that's a good way to go about doing it, or if it helps you, but there it is.

Anvilfolk

  • Rogueliker
  • ***
  • Posts: 374
  • Karma: +0/-0
    • View Profile
Re: Handling item properties
« Reply #2 on: August 03, 2009, 12:37:13 PM »
As far as I can understand, the problem with class hierarchies (subclassing) is that it's very strict. In some languages, like Java, it also imples that the hierarchy is a tree, and not a graph - and this is very limiting in some situations (e.g. you want Throwable items, Burnable items, and all combinations of the two). In languages that do allow multiple subclassing, I've heard that it can be quite a mess to debug/deal with, but it should allow the previous example with Throwable Burnable items.

Either way, you're not going to get rid of "a whole mess of flags". They simply become implicit. I'm guessing each item would have, for example, a method "isWeapon", in which it simply goes over its properties and checks if it has one of that particular class. This check probably will have to use some sort of flag, or a variant thereof.

Still, I think that might be preferable to other alternatives. Each class only has properties which matter to it - as opposed to having allocated memory for data relating to all possible properties, and having 90% of them as null.

Plus, it opens up some interesting things. Say each property has a boolean method specifying whether or not it applies when the item is used on an attack. For a fire sword that applies fire damage on a successful attack, you could have some along the lines of:

Code: [Select]
void Character::attack(Monster m){
  ... bla bla bla attack rolls ...
  if(attackSuccessful){
    List<Effects> le = equippedItem.getAttackEffects();
    for(Effect e: le){
      e.apply(m);
    }
  }
}

The very simple damage roll could be encapsulated within an effect:

Code: [Select]
class DamageEffect extends Effect{
  int minDamage = 4;
  int maxDamage = 12;

  boolean appliesOnAttack(){ return true; }

  void apply(Monster m){
    m.damage(random()*(12 - 4) + 4);
  }
}
"Get it hot! Hit it harder!!!"
 - The tutor warcry

One of They Who Are Too Busy

justinhoffman

  • Newcomer
  • Posts: 33
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling item properties
« Reply #3 on: August 03, 2009, 04:14:34 PM »
Quote
I thought about creating an ItemProperties class, and having each Item contain a List<ItemProperties>. This would avoid a whole mess of flags in the item class. The problem I'm having is that I'm not sure how to go about designing the ItemProperties class itself. Lets say two Item Properties are IsWeapon and ProcsOnHit:

  I had to reread this to get what you are trying to do.


  I suppose you could use polymorphism to create a ItemProperties class, and then create as a sub class for each property.  So you would have a weaponProc class a subclass of properties.  You could then make a list of ItemProperties such as damage proc, attribute bonus etc.  You'd have to have a subclass of ItemProperties for each item attribute.

   You could then search the ItemProperties for appropriate behavior.

   So you'd have a weaponproc sub class of Item properties.  In that sub class you'd have something like 1d3 fire stored.  On attack you'd run a search through the properties to see if there was a weaponproc subclass of ItemProperties.  That sounds very messy.

  I think a better and more common solution is to just make weapon a subclass of item.  You'd have all the attributes an item has and all the attributes a weapon has.  How many flags can a weapon really have anyway over any regular item?

   You can even have equipment as a subclass of item and weapon a subclass of equipment.  So say item stores things that every item has, say weight.

    Equipment might have flags for resistances and attributes.  You could even have pointers in the equipment subclass to resistances and attribute bonuses.  So none of the flags are in the weapon itself.

    Weapon would just have proc % and type, and damage.
« Last Edit: August 03, 2009, 05:56:56 PM by justinhoffman »

Anvilfolk

  • Rogueliker
  • ***
  • Posts: 374
  • Karma: +0/-0
    • View Profile
Re: Handling item properties
« Reply #4 on: August 04, 2009, 08:29:23 AM »
But then how'd you go about having a weapon that is also a container? It's a stupid example, I know, but with subclassing these kinds of things become a poblem.

Also, how'd you go about extra effects, like fire damage bonuses, or petrify, or anything of the like? You'd either end up using the system he's already proposed, or allocate unnecessary memory for the 99% of the items that don't do this kind of thing.
"Get it hot! Hit it harder!!!"
 - The tutor warcry

One of They Who Are Too Busy

Perdurabo

  • Rogueliker
  • ***
  • Posts: 99
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling item properties
« Reply #5 on: August 04, 2009, 09:34:52 AM »
Yeah, keeping it simpler is the way to go.

In Kharne, I have a TItem class, and Weapons are just a particular instance of TItem (with the member field FItemType set to 'iWeapon'). Where I differ slightly is that Magical Items have a number of instances of a class known as TItemEnchantment, attached to them. It is these that give them their properties.

For example, a Long Sword [+1,0] has a TItemEnchantment that gives a +1 to hit. A Long Sword [+1, +1] has two Enchantments, one the +1 to hit, and another that gives +1 to damage. A Long Sword [+1, +1] of Fire Resistance has, yes, a third enchantment as well, one that gives Fire Resistance. And so on. Each enchantment has a magnitude as well (which is dependent upon the level of the item). If an item is cursed, then these enchantment magnitudes are reversed.

Different tiers of magical items have different numbers and sizes of enchantments (yes, I completely ripped that off from WoW), and certain magical item types are predisposed to certain enchantments.

The Enchantment details are stored in a table in an SQLLite Database, and stored in a master list of TItemEnchantments, which I merely need to set up pointers to for the items. Both TItem and TItemEnchantment have common interfaces for producing a unique string value that can be saved to disk to represent uniquely the combination of Item and Enchantment.


purestrain

  • Rogueliker
  • ***
  • Posts: 172
  • Karma: +0/-0
    • View Profile
Re: Handling item properties
« Reply #6 on: August 04, 2009, 11:35:02 AM »
I would go the "messy" way: Having tons of Property-Classes like.. Lego-Like :-)

justinhoffman

  • Newcomer
  • Posts: 33
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling item properties
« Reply #7 on: August 04, 2009, 03:04:48 PM »
hmm, gonna have to edit this one two.  You could save a lot of space using the messy way.

You'd only have to create one instance of each attribute and just link it to items.  Duh lol.

So you could theoretically make your items all just linklists.  Where every attribute is a link beyond what is in each item.  Just have a link head in the item for all the attributes.

Would make it easy to handle removing a specific modifier too.  Say your equipment all becomes 'rusted' or something, which would lower weapon damage and AC from armor.  You could polish it with holy water or whatever and remove that attribute.

Hmm, would make it easy to enchant armor and stuff.  Temporary buffs to armor would be neat.

Dang I wish I had thought of that earlier, lol.   That would be sweet.

You could add new attributes on the fly too, and not wory about chaning the weapon class.   Each Item-property would just have a "what do I do" method.  Pass in a pointer to the wearer, and the equipment would update his stats accordingly.

Heck your items could even have random chances to do stuff....  Like a shield that randomly summons food.  Sheild of plenty =p.  Has a .01% chance at puting a loaf of bread in their inventory.

Or maybe a cursed hat that randomly summons angry bats around you.

But the point is, you could add an attribute that isn't pre-defined.  Say a sword that turns into a snake after being weilded for a long time.  Anything, it doesn't mater.  Because the behavior is all stored in the attribute.

It wouldn't be hard, you could just add stuff as you felt like it without changing anything aside from the function itself.  As long as it has a handle on the wearer/player it could effect the game accordingly.

Dang, I might eventually rewrite my item code now.  I never thought of that.
« Last Edit: August 04, 2009, 08:01:54 PM by justinhoffman »

RantingRodent

  • Newcomer
  • Posts: 13
  • Karma: +0/-0
    • View Profile
Re: Handling item properties
« Reply #8 on: August 06, 2009, 02:45:45 AM »
The way that I am dealing with this type of problem is through "Definition" classes. Rather than relying on class inheritance for Actors, I am building them through composition. A "Burning Sword of Boomerang" would be built up of several definitions:

The Item definition provides basic item interactivity (can be stored in a container, picked up, etc).
The Weapon definition provides functionality for equipping the item and understands how to modify stats during combat.
The Sword definition dictates what types of attacks are available and incorporates your Sword proficiency during combat.
The Burning definition sets the sword aflame and will handle spreading fire during combat (unless the physics system will do so inherently).
The Boomerang definition would override the behaviour for the item when it is airborne to make it return to you after travelling a certain distance.

This approach dictates a lot about how things are structured, so I think it would be difficult to refactor into an existing game. For this to work well during combat, all Definitions for the weapons, armour, and creatures involved in that combat must be given a chance to modify the combat statistics before calculations are done, and then given an opportunity to trigger effects based on the combat result.

I create a singleton instance of each Definition class, and whenever a new Actor is created I call definition.apply(newActor). To simplify matters, some Definitions can imply other definitions (the Sword definition automatically calls apply() on the Item definition, for example). This mimics class inheritance without having to deal with the strictness that comes with it. It also allows me to implement multiple inheritance wherever it is beneficial.

george

  • Rogueliker
  • ***
  • Posts: 201
  • Karma: +1/-1
    • View Profile
    • Email
Re: Handling item properties
« Reply #9 on: August 06, 2009, 03:33:31 PM »
This approach dictates a lot about how things are structured, so I think it would be difficult to refactor into an existing game. For this to work well during combat, all Definitions for the weapons, armour, and creatures involved in that combat must be given a chance to modify the combat statistics before calculations are done, and then given an opportunity to trigger effects based on the combat result.

I create a singleton instance of each Definition class, and whenever a new Actor is created I call definition.apply(newActor). To simplify matters, some Definitions can imply other definitions (the Sword definition automatically calls apply() on the Item definition, for example). This mimics class inheritance without having to deal with the strictness that comes with it. It also allows me to implement multiple inheritance wherever it is beneficial.

I'm also using components, but as I'm learning as I go I'm interested in hearing more about how you go about it. How do you structure things so definitions apply their modifiers to a thing's state before an action is taken? Does the Definition modify the item's attributes upon creation? The way I'm handling it right now is to attach a new instance of a component to the thing upon creation, and the component contains component-specific state data. Do you think this could lead to problems?

Regarding the OP, I also have a hash in each thing that holds properties. It's not any different really than a bunch of attributes set individually on the thing (I'm using Python though, so I imagine my structure is a little looser by default).

RantingRodent

  • Newcomer
  • Posts: 13
  • Karma: +0/-0
    • View Profile
Re: Handling item properties
« Reply #10 on: August 07, 2009, 04:50:12 AM »
Here's my Item definition class.

Code: [Select]
public class DefinitionItem extends Definition
{
public function DefinitionItem()
{
super();
verbHandlers =
{
"pick up":new HandlePickUp(),
"drop":new HandleDrop()
}
}
override public function apply(in_actor:Actor):void
{
if(!isActor(in_actor))
{
super.apply(in_actor);
in_actor.pathable = true;
in_actor.obstruction = false;
in_actor.state = "inert";
in_actor.addStateVerb(lexicon.verbPickUp, "inert");
in_actor.verbRequirements[lexicon.verbPickUp] =  lexicon.verbGrasp;
in_actor.verbRequirements[lexicon.verbDrop] =  lexicon.verbGrasp;
in_actor.addStateVerb(lexicon.verbDrop, "held");
}
}
}

... and the Weapon definition

Code: [Select]
public class DefinitionWeapon extends Definition
{
public function DefinitionWeapon()
{
noun = "Weapon";
plural = "Weapons";
}
override protected function init(in_event:Event):void
{
super.init(in_event);
verbHandlers =
{
"wield":new HandleWield(),
"DEFAULT_HANDLER":lexicon.definitionItem
}
}
override public function apply(in_actor:Actor):void
{
if(!isActor(in_actor))
{
super.apply(in_actor);
lexicon.definitionItem.apply(in_actor);
in_actor.addStateVerb(lexicon.verbWield, "held");
in_actor.addStateVerb(lexicon.verbAttack, "wielded");
in_actor.verbRequirements[lexicon.verbAttack] = lexicon.verbGrasp;
}
}
}

... and the Sword definition class

Code: [Select]
public class ItemSword extends Definition
{
public function ItemSword()
{
noun = "Sword";
plural = "Swords";
}
override protected function init(in_event:Event):void
{
super.init(in_event);
verbHandlers =
{
"DEFAULT_HANDLER":lexicon.definitionWeapon
}
}
override public function apply(in_actor:Actor):void
{
if(!isActor(in_actor))
{
lexicon.definitionWeapon.apply(in_actor);
super.apply(in_actor);
in_actor.verbRequirements[lexicon.verbWield] =  lexicon.verbGrasp;
}
}
}

I left out the handler classes so this didn't take up a whole page on its own. Suffice to say they each have a single method.
Code: [Select]
handleVerb(verb:String, subject:Actor, object:Actor, converter:Actor=null)
Only the definitions which must implement new Handlers are hardcoded  implementations. The rest should just be instances. Even in my example here, the Sword definition should ultimately just be an instance of DefinitionWeapon with some parameters.

I have not implemented combat modifiiers yet, as I'm still doing groundwork in more important areas. The plan is to use a CombatResolution class to hold the stats for the current exchange, and pass this around to all of the involved Definitions on each side. Order of operations looks to be complicated, though, so I don't want to get into a lengthy explanation until it's actually implemented. What's in my head right now is likely to change.

I hope that's all pretty clear.