TWiki Home Tharsis . Design . ObjectModifiers Tharsis webs:
Design | Guilds | Combat | Website
Design . { Home | Changes | Index | Search | Go }
Related topics: 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...

Methods:

-- 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.

What does the 'values' in mapping contain?

-- 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:

Things I don't think are modifications

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:

--
Looking at ways modifications might be removed:

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.

--
Optimisations:

--
Disclaimer:

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


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.

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

-- FantoM - 24 Feb 2003

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


Topic ObjectModifiers . { Edit | Attach | Ref-By | Printable | Diffs | r1.5 | > | r1.4 | > | r1.3 | More }
Revision r1.5 - 24 Feb 2003 - 05:04 GMT - PumaN
Parents: WebHome > ObjectLibrary
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.