12 Mar, 2010, Deimos wrote in the 81st comment:
Votes: 0
I'm not sure I don't agree with shasarak's line of thought here. It would seem to extend to other complex problems that I've run into before, as well. I've yet to find solutions for these problems because the languages I'm used to pretty much indirectly prohibit it (or I'm not sufficiently advanced enough to figure it out). Maybe Ruby will be my knight in shining armor!
12 Mar, 2010, Koron wrote in the 82nd comment:
Votes: 0
David Haley said:
Runter said:
wouldn't the safest way to solve this dilemma be to save the unmodified stat score and effect math?

vs.
I said:
Right off the top of my head – and so this might have issues – define a mapping from attribute name to list of effects on that attribute. (…) When you ask a character for the value of some attribute, you first get the attribute from its attribute map, then you apply ("reduce") that value through the chain of relevant effect callback functions.

Either I'm a shameless and blatant plagiarizer or you've misattributed that first quote. :P

If I'm interpreting your jargon correctly, you want to dynamically calculate the stat with each call to it? It looks like the difference is I'm caching the value every time something happens to alter it. I suppose you could invent a scenario where the 100% dynamic calculation is more accurate, but it also produces more work for the cpu that moderately thoughtful coding practice would avoid.
12 Mar, 2010, David Haley wrote in the 83rd comment:
Votes: 0
Oops, sorry, I did indeed misattribute it. My bad. :redface: (It would appear that it's too late to edit my post to fix it.)

And no, I wasn't arguing that you had to recompute it every time. You could cache the calculation, and invalidate that cache as soon as the inputs change. However, depending on just how arbitrary those inputs are, you might not be able to determine the inputs. That is why I did not discuss caching explicitly in post 56, as I was referring to Shasarak's request to modify any old random stuff arbitrarily. If you have callbacks that edit attributes, then you really have no idea what the inputs to those callbacks are, and therefore you cannot know when to invalidate the cache.

For example, a callback might modify your strength by using your wisdom as a modifier. If your wisdom moves, your strength should too. But if you can't look inside the callback, how will you know to invalidate strength when wisdom changes? What if the callback modifies my strength based on your wisdom? Then I can't even invalidate my cache whenever stuff changes for me, because in this case, the cache is invalid when stuff changes on you! I don't think these examples are far-fetched or contrived, incidentally.

Anyhow if you set aside the complexities of caching (and indeed they appear or disappear based on what other stuff is allowed) the basic idea is the same, namely that you store your base value, and then not the modified value but the sequence of modifications to apply.
13 Mar, 2010, shasarak wrote in the 84th comment:
Votes: 0
Koron said:
That said, wouldn't the safest way to solve this dilemma be to save the unmodified stat score and effect math? So instead of the mud calculating "aha, pc has 5 str, so now that pc has quaffed a potion of str*2, we save an aff of str+5," you say "pc has str 5 and a str*2 effect." You then save a current_str score and run update_str() that recalculates this value every time something happens that affects str.
Deimos said:
What I got from what shasarak was saying is that it would break encapsulation for update_str() to have to know about any magical effects, whether you're actually saving the math or the values. I could be completely wrong, though, since I think these concepts have become sufficiently hard enough to describe without a whiteboard or something :tongue:

As David H want on to explain very nicely in the post just above this one, one problem with Koron's system is that if you're caching values, you can't necessarily tell when the cache needs to be invalidated. You could get around this by having the PlayerCharacter.Strength method calculate a value dynamically - but that still falls foul of my other criticism, which is that it's poor encapsulation.

Deimos, you are interpreting me pretty much correctly, yes. To put it in terms of the strength example, if we adopt Koron's approach then that means we have added code to the PlayerCharacter class which anticipates the possibility of strength-modifying spells; and it also means that strength-modifying spells cannot work unless the PlayerCharacter class is written in such a way that it enables them.

So, if you want to add a whole new type of magical effect which didn't previously exist in the MUD, you can't just add code for the effect itself; you also have to modify the code of anything which could potentially be a target of that effect.

That means you have at least twice as many classes to edit; it means that the coder responsible for coding new magical effects needs to have write-access to very low-level and important base-classes; and it's likely to lead to bloat in the base classes, because they have to accomodate the possibility of several hundred different effects, virtually none of which will actually be in place at any given moment.

A fairly basic criterion of well-written OO code is that the operation of any given class should depend as little as possible on the operation of any other one; or, to put it another way, if you want to change the code that deals with something, there should be the smallest possible number of places that you need to change.
13 Mar, 2010, Deimos wrote in the 85th comment:
Votes: 0
PlayerCharacter and Effect are so intertwined (because one necessarily has to save state about the other) that it's hard for me to picture any implementation which doesn't break the strictest definition of encapsulation. Like I said before, though, it's getting hard for me to just "envision" without any code, so I might whip up a little example later.
13 Mar, 2010, shasarak wrote in the 86th comment:
Votes: 0
Deimos said:
PlayerCharacter and Effect are so intertwined (because one necessarily has to save state about the other)

"Necessarily"?

Them's fightin' words! :tongue:

Please elaborate on why you feel this to be the case! :smile:
13 Mar, 2010, David Haley wrote in the 87th comment:
Votes: 0
shasarak said:
Deimos, you are interpreting me pretty much correctly, yes. To put it in terms of the strength example, if we adopt Koron's approach then that means we have added code to the PlayerCharacter class which anticipates the possibility of strength-modifying spells; and it also means that strength-modifying spells cannot work unless the PlayerCharacter class is written in such a way that it enables them.

The short-sighted way of implementing the suggestion would indeed be to have methods like getStrength, getWisdom, etc., and if you needed to have an effect on agility you'd have to add or recode getAgility.

But you can also construct a generic map from attribute name to attribute value. Then methods like what Koron and I described (although I think I described this more generically, so it might make what I'm saying now clearer) can modify the process of looking up some attribute in that map. Now, yes, you still need to allow that only things in that map can have modifications, however you can stick whatever you want in that map as an attribute.

I still don't buy at all that it's desirable for completely arbitrary object methods to be overridden. I don't really want a spell effect to be mucking around with methods for 'implementation details'. The potential for screw-up is considerable because stuff starts happening beyond predictable interfaces. I know that you think this is fine and dandy, so perhaps we might simply agree to disagree, but basically this is a key difference between the approaches. One enforces limits on what can be modified, and the other lets you do whatever you please wherever you see fit.
13 Mar, 2010, Deimos wrote in the 88th comment:
Votes: 0
shasarak said:
"Necessarily"?

Them's fightin' words! :tongue:

Please elaborate on why you feel this to be the case! :smile:

How would you do it without Effect saving PlayerCharacter state? I can't wrap my brain around it. I've tried to code up an example, but I keep running into the same problem no matter which way I do it…
13 Mar, 2010, shasarak wrote in the 89th comment:
Votes: 0
David Haley said:
The short-sighted way of implementing the suggestion would indeed be to have methods like getStrength, getWisdom, etc., and if you needed to have an effect on agility you'd have to add or recode getAgility.

But you can also construct a generic map from attribute name to attribute value. Then methods like what Koron and I described (although I think I described this more generically, so it might make what I'm saying now clearer) can modify the process of looking up some attribute in that map. Now, yes, you still need to allow that only things in that map can have modifications, however you can stick whatever you want in that map as an attribute.

You need the ability to modify behaviour, and not merely attribute values. Consider, for example, a spell that causes you to lose one hit point every time you draw your weapon inside a particular city. That's an effect which is not dependent on any one attribute - you actually have to intercept the weapon-wielding code and make it test for the player's location, and behave accordingly.

Again, if you just wanted to do that in isolation, you could handle it by having events triggered before and after a weapon is wielded, with the wielder and weapon passed in as event arguments, and the before-event testing for some sort of Veto exception; but that falls foul of the same poor-encapsulation problem.
13 Mar, 2010, shasarak wrote in the 90th comment:
Votes: 0
Deimos said:
How would you do it without Effect saving PlayerCharacter state? I can't wrap my brain around it. I've tried to code up an example, but I keep running into the same problem no matter which way I do it…

Possibly you mean something other than what I think you mean by the word "saving". But one aspect of what I'm describing is that everything happens dynamically, nothing is cached.

The Effect object doesn't necessarily need to hold a reference to its target (certainly not in the strength-modification case - it might in more sophisticated examples); the PlayerCharacter class probably does need to know that it is wrapped up in a proxy, but it doesn't need to know about any effects the proxy is currently managing; the proxy does need to hold a reference to the effect, but it has no knowledge of what the effect does, other than knowing the name of the method it intercepts; it's in the nature of proxies that they need to hold a reference to the object they are a proxy for, but, again, they don't need to know anything about what that object does.

When you want to know the character's strength, you ask the proxy; the proxy asks the PlayerCharacter for its unmodified strength score; that is then fed into the Effect's strength method as an argument; and the value that method returns is the character's current effective strength. (If there are multiple effects, then the result of each one is fed into the next, and the proxy returns the last one).

So which aspect of PlayerCharacter state is being saved by the Effect?
13 Mar, 2010, David Haley wrote in the 91st comment:
Votes: 0
shasarak said:
Again, if you just wanted to do that in isolation, you could handle it by having events triggered before and after a weapon is wielded, with the wielder and weapon passed in as event arguments, and the before-event testing for some sort of Veto exception; but that falls foul of the same poor-encapsulation problem.

Perhaps – although I'm not convinced that it has to or that your example even does – but IHMO your let's-modify-anything-arbitrarily falls foul of interfaces remaining predictable and well-defined. :tongue:
13 Mar, 2010, Deimos wrote in the 92nd comment:
Votes: 0
Okay, using your example from earlier, say our PC gets hit with those 2 effects: one that halves his strength every tick, and one that adds 3 to it. From what you're describing, here's the chain of events that would take place:

- Our PC has 20 strength to start with.
- Both Effects get stored in the collection.
- Tick.
- Proxy asks PC what its unmodified score is ( 20 ).
- Proxy feeds this score into the first Effect, which halves it ( 20 / 2 = 10 )
- This Effect feeds its modified score into the next Effect, which adds 3 to it ( 10 + 3 = 13 )
- This Effect returns 13 to the proxy.
- Tick.
- Proxy asks PC what its unmodified score is ( 20 ).
- Etc…..

You won't end up accomplishing what you were describing, which is a cumulative effect over time. To do this, you have to save state somewhere. If you save a "modified" score in the PC, which the proxy asks for instead of the unmodified score, and this values changes via some other game event (like wearing a piece of equipment, for instance), then you've botched your calculations.
14 Mar, 2010, David Haley wrote in the 93rd comment:
Votes: 0
Presumably the first effect would have to know the state relevant to its own logic, such as the number of ticks that it has existed for. So it would divide its input by num_ticks^2. (or (num_ticks+1)^2 depending on whether the first tick counts)
But what this means is that now, all of your effects need to be aware of the value and where to get all the inputs they require. It's not clear to me that this is terribly different than the presumed bad state of things needing to know about other things. (simplifying, because I don't feel like typing it all again :tongue:)
14 Mar, 2010, Deimos wrote in the 94th comment:
Votes: 0
Even if the effects know how long they've existed, they aren't aware of outside factors like equipment, shapechanging (hehe), etc. To make them aware of such things would, again, break encapsulation.

If there's a way to do this, though, I'd love to see it!
14 Mar, 2010, David Haley wrote in the 95th comment:
Votes: 0
I think that this is an interesting comment on games and MUDs in general, by the way, in that so many things are so interrelated.

This is why I think it's sensible to lay down clearly what kinds of effects are possible, implement those, and then say that that is that. Otherwise you end up with complex systems to allow flexibility that might not be giving you that much in the end of the day. Even if examples aren't far-fetched, one should think if they're really worth the trouble.

An example I like to think about along these lines is magic canceling out other magic. Let's say I have a spell that makes my magic strong against orcs. But orc shamans have magic that cancels magic strong against them. This is already hard to model. But what if I also have a spell to negate protection spells? The mind starts to boggle pretty quickly. While it's easy to wave one's hands about and say that proxies and event handlers and message passing and smart programming and stuff will fix this, I've yet to see a practical system that actually does all this stuff.
14 Mar, 2010, Deimos wrote in the 96th comment:
Votes: 0
TBH, I think the examples you gave would be much easier for me to model than the ones we've been talking about. The "over time" paradigm is really quite a bit more complicated than it first appears to be.
14 Mar, 2010, shasarak wrote in the 97th comment:
Votes: 0
Deimos said:
You won't end up accomplishing what you were describing, which is a cumulative effect over time. To do this, you have to save state somewhere. If you save a "modified" score in the PC, which the proxy asks for instead of the unmodified score, and this values changes via some other game event (like wearing a piece of equipment, for instance), then you've botched your calculations.

David more or less answered this one already, but basically each effect should be saving its ownstate, and not that of the PlayerCharacter. The magnitude of the effect varies with time. So, after the first tick, effect A divides base strength by 2, and effect B adds 3 to strength; after the second tick, effect A divides base strength by 4, effect B adds 6 points to it; after the third tick, effect A divides base strength by 8, while effect B adds 9 points to it; and so on. It doesn't store what the PlayerCharacter's strength is, it stores what impact it (dynamically) has on whatever value is fed into it.

Obviously the order in which the effects influence the outcome is significant (as it would have been in my original example, in fact) so you may need to add some kind of attribute onto the effect which tells the proxy which order to sort them in. If we assume for the sake of example that effect A is evaluated first, and that the character's default strength is 20, then if we ask the proxy for the character's dynamically calculated strength after each tick, the result will be:

tick 0: 20
tick 1: (20 / 2) + 3 = 13
tick 2: (20 / 4) + 6 = 11
tick 3: (20 / 8) + 9 = 12 (assuming we round to an integer).
tick 4: (20 / 16) + 12 = 13
tick 5: (20 / 32) + 15 = 16

and so on. At no point does either effect store what the state of the PlayerCharacter is, nor does it store the result of any previous calculation; it's simply that the impact of the effect itself varies over time. The impact of wearing pieces of equipment, etc. is modelled by using other effect objects (whose impact might perhaps be constant while the equipment is worn, and stop as soon as it is removed, although it doesn't have to work like that).
14 Mar, 2010, shasarak wrote in the 98th comment:
Votes: 0
David Haley said:
An example I like to think about along these lines is magic canceling out other magic. Let's say I have a spell that makes my magic strong against orcs. But orc shamans have magic that cancels magic strong against them. This is already hard to model. But what if I also have a spell to negate protection spells? The mind starts to boggle pretty quickly. While it's easy to wave one's hands about and say that proxies and event handlers and message passing and smart programming and stuff will fix this, I've yet to see a practical system that actually does all this stuff.

That's certainly doable, but it's pretty fiddly. Basically the whole effect/proxy system needs to become recursive, so that effects themselves are wrapped up in proxies and their modification of their target's behaviour can, in turn, be overidden.

However, even I am willing to admit that this example smacks of over-engineering. :smile: It's reasonable to have an effect which causes aggressive spells to do more damage against orc targets; it's reasonable for the shaman to be able to cast protective spells which protect the target against damage by other spells; but to have a spell whose only effect is to modify the impact of another spell which in turn has no effect other than to modify the effect of otherspells, is probably more complexity than you really need!
14 Mar, 2010, David Haley wrote in the 99th comment:
Votes: 0
shasarak said:
Obviously the order in which the effects influence the outcome is significant (as it would have been in my original example, in fact) so you may need to add some kind of attribute onto the effect which tells the proxy which order to sort them in.

Doesn't this break encapsulation, though, because suddenly effects need to be aware of the notion of being sorted with other effects they know absolutely nothing about? :wink:

shasarak said:
it's reasonable for the shaman to be able to cast protective spells which protect the target against damage by other spells; but to have a spell whose only effect is to modify the impact of another spell which in turn has no effect other than to modify the effect of other spells, is probably more complexity than you really need!

When you put it like that, it does sound silly. But if you think of it as plain old spells and counterspells, where counterspells are spells and can be countered, it doesn't sound as silly.

But I am glad we've established that this complexity is more than is needed. This was part of my argument: as you design these systems you make decisions regarding what is and isn't too complicated. There's not much need to have a system flexible enough to handle situations you don't have and don't even want. Conversely, it seems fishy to criticize a system for not being flexible enough to handle such situations. :wink:
14 Mar, 2010, Runter wrote in the 100th comment:
Votes: 0
David Haley said:
shasarak said:
Obviously the order in which the effects influence the outcome is significant (as it would have been in my original example, in fact) so you may need to add some kind of attribute onto the effect which tells the proxy which order to sort them in.

Doesn't this break encapsulation, though, because suddenly effects need to be aware of the notion of being sorted with other effects they know absolutely nothing about? :wink:

shasarak said:
it's reasonable for the shaman to be able to cast protective spells which protect the target against damage by other spells; but to have a spell whose only effect is to modify the impact of another spell which in turn has no effect other than to modify the effect of other spells, is probably more complexity than you really need!

When you put it like that, it does sound silly. But if you think of it as plain old spells and counterspells, where counterspells are spells and can be countered, it doesn't sound as silly.

But I am glad we've established that this complexity is more than is needed. This was part of my argument: as you design these systems you make decisions regarding what is and isn't too complicated. There's not much need to have a system flexible enough to handle situations you don't have and don't even want. Conversely, it seems fishy to criticize a system for not being flexible enough to handle such situations. :wink:


You've met your wink quota. Subsequent winks may be lost.
80.0/159