TWiki Home Tharsis . Design . ObjectModifiers (r1.1 vs. r1.5) Tharsis webs:
Design | Guilds | Combat | Website
Design . { Home | Changes | Index | Search | Go }
 <<O>>  Difference Topic ObjectModifiers (r1.5 - 24 Feb 2003 - PumaN)

        • Taking your flame enchantment as example. It should add heat damage (could be cold flames too?), flames, and light. This adds another small note on mod-types, min/max-values, the flame-enchant would give the sword a min-light of 2, if someone puts a glow enchant with min-light 5 on the sword, light will become 5, not 7, since the light sources doesnt add up, just takes the biggest one around.
        • Enchantment-type is important, and also, not all modifiers are enchantments. 1. A flame enchantment can be dispelled, but if the sword is actually on fire (its a wooden sword now), you would be better of sticking it in water. 2. Blunten-enchant vs rust. (it turned back to iron! This sword sure is full of magic). I think it would be dumb keeping all this data in the modifier list, would make it very difficult to figure what enchantments the sword has on it, for one.
        • Lets call the list of things that adds modifiers to an object affects/effects?
        • If you are using closures, you can simply let the closure tie back into the affect/effect and pick up its value over there -- PumaN

 <<O>>  Difference Topic ObjectModifiers (r1.4 - 24 Feb 2003 - FantoM)

    • Enchantments are likely to hold a bunch of data, and their data should be kept separate in an enchantment list. Especially if seeing to that one enchantment might have several modifiers coupled to it. Same with curses, those do not differ from enchantments. -- PumaN

    • Enchantments are likely to hold a bunch of data, and their data should be kept separate in an enchantment list. Especially if seeing to that one enchantment might have several modifiers coupled to it. -- PumaN
      • That was my initial thought, but then I tried to find anything that an enchantment could alter that wasn't some facet of the existing item. Essentially an enchantment adds some amount of some attribute. Eg it adds fire, or it makes it sharper, or it makes it weigh less. I can't come up with any things that break this rule. If this is the case there becomes the question as to how we implement an "enchantment" spell. It might add several modifications, possibly linked (not supported in this design right now) rather than a big single modification. -- FantoM
    • Same with curses, those do not differ from enchantments. -- PumaN

 <<O>>  Difference Topic ObjectModifiers (r1.3 - 24 Feb 2003 - PumaN)

I agree decayable modifiers would be a benefit. -- PumaN


Check all my cursives above. -- PumaN - 24 Feb 2003

 <<O>>  Difference Topic ObjectModifiers (r1.2 - 23 Feb 2003 - FantoM)

Related topics: ObjectLibrary


A further thought on modifiers - rather than having add_wc_enchantment/add_elemental_enchantment/etc - it would be nice to have a modifible module, like the hook module, which provides methods

   varargs void add_modification(int modification_key, mixed modification, int expiry, int force)
   ... subsequent removal methods

This has possible problems with things like "remove curse" spells, how does it know that modifications are curses and which are not.

This module could provide hooks which are called whenever a modification is added/removed, allowing the user of the module to update cached values.

I'm thinking about this because it would be nice to have player as modifiable and support such modifications as "poison". Thus a poison potion/spell/whatever adds a poison modification with some expiry (and possibly a compilicated closure to slowly decrease the effect over time) and the heartbeat of players is updated to check for such modifications.

-- FantoM - 24 Feb 2003

 <<O>>  Difference Topic ObjectModifiers (r1.1 - 23 Feb 2003 - PumaN)

%META:TOPICINFO{author="PumaN" date="1046026842" format="1.0" version="1.1"}% %META:TOPICPARENT{name="ObjectLibrary"}%

The advantage of a modified-based system is that fewer static numbers are placed all over the lib.

The sword of Huffintot doesn't have 'strength 50', but instead the huffintot modifier is put on top of a normal sword to alter it's basic properties, by giving it 25% more strength. The sword in turn, has it's strength decided by importances, say 60/40 from material/craftmanship. So the static 'strength' numbers are not to be found until we get down to the material-lib. In this fashion we automatically get a flexible crafting-library at the same time.

Thus we don't have to change all instances that inherits 'sword' when we make a change to 'sword'. That is basically what we are trying to avoid with a library?

-- PumaN - 18 Feb 2003

Beyond my dislike of the word importance we should be able to work it into the above xml (with alterations). In the example above I specified exact numbers in many places. We could utilise defaults in as many cases as possible, eg Steel can have default values for durability and fragility and we can support alteration of the defaults via percentages rather than absolutes.

Ie - to make a sword 95% metal and have it slightly less durable than a normal block of metal and twice as fragile:

  <material name="metal" percentage="95" durability="80%" fragility="200%" /> 

Side issue: Right now that xml implies that an item inherits from only one other item - we may want to change this.

-- FantoM - 18 Feb 2003

I dont think material should be hardcoded in lib, then we get inflexibility, the ObjectGenerator? should set up material at the creation of the actual object. The library might only hold rules for how to set up objects and their functionality, not actual data on specific objects. It is for the generator to combine the library-bits to an actual working unit.

Another specific problem is changing values on the fly, when the sword gets an 'evil' enchantment cast on it, if we change the static numbers, it would become difficult to remove the enchantment.

-- PumaN - 20 Feb 2003

Perhaps we are both talking about the same thing here (quite possible ;)). The definition I had above was only that - a template from which an actual sword would be cloned or extended.

If you choose to extend the lib-sword then you can do so in your own .c file, with a call something like "configure-from-lib(sword)" followed by code to alter the values.

A cloned instance of the sword has it's own values and these can be altered as a result of enchantements/etc and will not effect the original template.
Almost wrote something else here, but this should work as long as we only allow percentage changes, as opposite to point/value-changes - PumaN

As for the ObjectGenerator? (which is roughly analogous to the "configure-from-lib" call) - yes it adds the materials at clone time, the definition above just told the object generator how to do it. I don't see how you can expect the ObjectGenerator? to be able to set up the materials for an object without some hints as to what they should be. Certainly they should not be specified by the person doing the cloning.

-- FantoM - 21 Feb 2003

Wrt (With respect to) to altering the template values and PumaN's comment on percentage values. The same can be said to apply to a sword that extends the template and alters the default values for that sword.

We need to provide alteration methods for this, otherwise people just use "set_wc(10)" because doing "set_wc(get_base_wc() * 0.1)" takes too long.

You'll notice the implication that there is a get_wc() and a get_base_wc(). If we want to retain the original, un-modified value then we need get_base_wc(). Note that this value is the value you get when you clone it, not the template value. A sword that starts with the template can alter the base_wc - eg special sword with magical stone which is extra-sharp should have it's base_wc altered. This value may be further altered (temporarily) with enchantments/curses/etc.

-- FantoM - 21 Feb 2003

Better would be a add_modify_wc(1.1) or modify_wc(1.1), and thus simply disallow a set_wc. Problem is how to code that effectively since its not supported by LPC. - PumaN

Yes - I agree on the modify_wc, and probably modify_base_wc and the disallowing of set_wc. Not sure what is not supported? the disallowing? We could make it a private method. We could make it fail if there is already a wc set, thus making it possible to do set_wc just once, which the ObjectGenerator? will do. Making it fail on the second call to set_wc has advantages for people writing objects that are not based on the lib - they can still call set_wc, where calling modify_wc() is unlikely to produce any good results... 0 * anything being 0. -- FantoM - 21 Feb 2003

I seem to be stuck in some old thoughts...youre right...


  • modify_[data](float percent)
  • multiplies the value with percentage (does this mean all values will have to be float or suffer to bad/abuseable roundingeffects?)
  • unmodify_[data](float percent)
  • divides by percentage, thus cancelling effect

-- PumaN - 21 Feb 2003

I like the unmodify idea.

As for float vs int - My initial reponse is to go for int's and ignore the rounding errors. The catch is that unmodify_data might have a problem...

-- FantoM - 21 Feb 2003

More thinking.

An example of how modify/unmodify fail:

If the base is 10 and the percentage is 30% ( we are cursing an item! ) then we modify to get 10 * 0.3 = 3.333 which rounds to 3.  When unmodifying we do 3 / 0.3 = 9. Wups!

How about applying alterations and recording them in modify and in unmodify removing them rather than reversing them.

Thus we get a chain of modifications that are applied to the base and used to provide the value (in the above example we have a base of 10 and one modification of 30%). Unmodify could either match a given value (eg unmodify 30% would look for a 30% modification in the chain and remove it) or could use a key ( modify() returns a number which you use and pass to unmodify().

This has the advantage of being able to hold a bunch of things that need to be done to the base and only incurring a rounding error once. We perform all the modifucations in floating point and then round the result and cache it until a new modify/unmodify is performed.

Also provides the ability to perhaps store more than just a number as the modification... perhaps you cast "flaming halo" on your sword. This provides an enchantment which adds to the damage dealt by fire. Someone else casting "extinguish" could remove this enchantment, but someone casting "blunten" could not (although it would probably cause the damage dealt due to a direct blow to decrease)....

-- FantoM - 21 Feb 2003

Floating points calculations are so expensive you rather store them than use them, huh? Both ints and floats are 32 bits, are they not? I also changed the unmodify, you dont have to invert anything, just divide by the same number for effect. Course, if you store the values, you might as well use other things than just percentages (whatever that would be).

You dont really need float for percent by the way, you could let an int of 1000, 10000 or 100000 equal 100%. The idea of only calculating it when we have a new value is good.

As for fire/blunt damage, Id say we dont really have a simple 'wc' anymore if we have these kind of damages. And the object of course needs to know what enchantments (effects) it has on it, not just a number (if its not a unique id).

-- PumaN

Floating point are more expensive, but in the whole scheme of things not worth worry about the difference. The only reason I was trying to avoid using then was a couple of problems I've had when storing floats, and the issues of having people choosing arbitrarily accurate values for things like wc. I'd prefer to set a scale (0->100 or whatver - certainly bigger than our current 0->20) and use ints.

As for percentage - I agree we don't need to use floats - and I'd go with 0->100 in most cases, tho maybe 0->1000 might be needed sometimes, although why we should support 1/2 percentage changes I don't know...

As for fire/blunt - I agree we should remove wc. this isn't a simple change tho and should probably be considered as part of a future upgrade to combat.

-- FantoM - 23 Feb 2003

Hmm, Im confused, some things on the page seem to not have been saved.

Question: How do we store the modifiers? Its gonna be a lot of data. If we only recalculate the value, this doesn't have the optimizing problem I first thought it would have.

  • One mapping? Each 'value' (wc/weight/color/whatever) could have a unique int assigned to it, mappings index faster on ints.
What does the 'values' in mapping contain?
  • An array of items, each item having an appropriate form for the modification of this modifier?
  • OR a mapping (you suggested they have unique-ids returned) so it can be removed based on ints.

-- PumaN - 23 Feb 2003

We can presume that additions and removals of modifications are much less frequent than usage. Thus we can use a data structure that is efficient for access but doesn't have to be too concerned about insertion/deletion costs.

We might also be able to argue that the usage of the modifications only occurs when we add/remove one because we cache all the possible results - not that this alters the fact that we don't need to concern ourselves with insertion/deletion costs.

First lets investigate what kind of things are going to be considered modifications.

We might be able to argue that there is a known set of modification types, and that coders can use them but can not design one-off new modifications. This is possibly not ideal. It would certainly be nice if there is at least a set of reusuable modifications.

Possible modifications:

  • Permanent alterations to any attribute of the item - eg durability, color, weight (Are these just enchantments as defined below?)
  • Enchantments (wear off over time) - extra damage, elemental damages, light, faster swing. Essentially these are just modifications to other attributes of the item.
    • Enchantments are likely to hold a bunch of data, and their data should be kept separate in an enchantment list. Especially if seeing to that one enchantment might have several modifiers coupled to it. Same with curses, those do not differ from enchantments. -- PumaN
  • Curses (wear off over time?) - probably modellable as negative enchantments, although they might be removed in the same way, a remove curse should not remove a nice enchantment.

Things I don't think are modifications

  • Extra abilities - spell casting eg detect traps

At the moment it appears that modifications are only going to alter the values returned by get methods of an item, methods that would be called during the use of the item. Eg "get_damages() - returning sharp:20,fire:10,ice:3" or "get_light" or "get_swing_rate". This also implies that it might be possible to restrict the allowable modifications.

If this is the case it will be possible for these methods to incorporate alterations to their base return value via the modifications, possibly caching the alteration, possibly not.

There is a middle-road option, you can make a mark in modification whether the modifiers has changed since last recalculation, and look at the mark each time we get the value. As you say it would be good if the method was possible to choose for each value-type. -- PumaN

Looking at ways modifications might be added:

  • By the coder. This is a flaming sword, thus he codes a normal sword and adds a permanent "increase fire damage" modification. What happens when someone casts "no-flame" on it? I'd say perhaps any enchantment has a chance of surviving an attempted removal and that the coder could, if he chose, set it to be hard to remove the flame modification.
    • Again, rather he adds a permanent flaming-enchantment, which then adds the appropriate modifiers. The enchantments should be in lib too, and not coded by hand. (Remember what happens in the long run when you have 'variable = value') -- PumaN
  • By some external device. Eg player spell or some other item. Such a modification is applied in exactly the same fashion as if by the coder, just later on in the life of the item. It may be a permanent modification, it might be time delayed.

Looking at ways modifications might be removed:

  • Timed expiry. A non-permanent modification should remove itself.
  • Attempted forced removal. A "disenchant" equivalent spell. This should call expire() on the modification with some level of force and the modification will be removed if the disenchant is strong enough. Should it be possible to target a particular form of modification when disenchanting? I'd say it probably shouldn't be, although this is getting into the realm of actual command design. The "no-fire" disenchant should just attempt to remove the(all?) "increase flame damage" modification(s).
  • Destruction. The item is destroyed, all modifications go with it.
  • Targetted removal by coder. It may be desirable for a coder to remove a particular modification. This removal should never fail. It is probably possible to just destruct the modification (if it's an object), or have some special removal method.

I think it is better if the things that adds modifiers to an object, and the modifiers themselves, are two separate. We rather not poke around manually in the goo. -- PumaN

Interesting side thought - what happens when you cast a fire spell on a backpack? Or put a flaming sword into a backpack? What about the encumberance of an item that deals elemental damage...

Applies fire damage to the object? Bleh, complex stuff. Lets wait... ;) -- PumaN

So - how to implement all this.

I agree we need some list of modifications for each attribute of the object.

I think we should implement modifications as closures, similar to hooks.

A modification closure would look like:

   float modify( float current_value )

So you would call each modification in turn, passing it the return value from the call to the previous modification. You'll note I pass/return floats. This does not preclude the rounding of the final value to an int if we choose to do so. Such rounding may be desirable at first to enable us to support existing code - eg lots of code expects weapon_class to be an int.

Creation of these closures - in many cases they would be very simple, we could get people to write:

   add_wc_enchantment( lambda( ({ 'current }), ({ #'*, 'current, 1.1 }) ) ) // 10% enchantment

Maybe we have a add_modifier(MOD_WC, closure/whatever) instead of having one fun for each kind of value? -- PumaN

Lots of opportunity for buggering it up tho.

So - we add some daemon which provides modifications closures.

   add_wc_enchantment( MODIFICATION_D->get_percentage_modification( 10 ) ) ); // permanent 10% 
   add_elemental_enchantment( FIRE, MODIFICATION_D->get_modification( 50 ) ), 30 ); // 30 second, up to 50 fire damage, implies flaming?

To remove a specific enchantment you could record the return closure and use it to remove the enchantment:

   remove_wc_modification( ten_percent_enchantment ); 

To curse, I'd suggest we add add_wc_curse() ... methods, utilising the same MODIFICATION_D methods to produce the closures.

I've missed adding the details of the "force" of an enchantment, to be used when removing the enchantment via a "remove enchantment" spell. For now presume that is an optional argument on the end of the call do add the enchantment, like the expiry time.

Data structure

We could either go with a single mapping to hold all attributes of the object, in which each key is the attribute id and each value is the list of modifications, or we could go with a separate list of modifications per attribute.

I'm in favour of the mapping containing all attributes because it allows easy future addition of new attributes - ease of code maintenance in favour of execution time, although with LPC's wierdo variable handling who knows whether one big mapping or lists is less efficent than a set of lists.

So - how to store the list. It might as well just be an array.

Each element in the array would store details of a single modification. For this we need to at least store the modification (int/float/closure)as described above. We also need to store the expiry time and force and probably other things that will come along later. We could have a "modification object which holds all these values (and if we went this route we could use it for adding/removing the modification as well). I'm not especially in favour of this due to the potentially very large number of such objects that would have to be created.

An alternative is for each attribute to have an array of values ({ modification, time, force }), a mapping (seems too inefficient).

A further alternative is to have 3 lists for each attribute, one for each of these values.

My preference at this time is to go with a single array of arrays - it's easy to implement and until it's proven to be too inefficient it might as well be used.


  • Allow mixed types rather than just closures to be passed to the add_??_enchantment methods.
    • int would be an exact value alteration
    • float would be a percentage alteration
    • closure would be as described above.
  • The structure described above is horribly inefficent for detecting expiry of a modification over time - you have to traverse the entire list of modifications every time interval checking for any that have expired. We may decide to have a call-out for each modification with an expiry (I think most will NOT have an expiry ). If we don't go with call-outs, or we find we have too many call-outs, then we will have to introduce a central controller which will track all expirable modifications and call remove_enchantment/whatever on the objects when required.


I've got off the topic of an Object Library a bit here, and more worked on modifications made AFTER the object is designed. The Object Library needs a technique for modifications that is targetted at one object being based on another with some modifications. These ideas presented here may well be applicable in some form tho, and certainly should be considered for a post-design modification system.

-- FantoM - 23 Feb 2003

Topic ObjectModifiers . { View | Diffs | r1.5 | > | r1.4 | > | r1.3 | More }
Revision r1.1 - 23 Feb 2003 - 19:00 GMT - PumaN
Revision r1.5 - 24 Feb 2003 - 05:04 GMT - PumaN
Copyright © 2001 by the contributing authors. All material on this collaboration tool is the property of the contributing authors.
Ideas, requests, problems regarding Tharsis? Send feedback.