ID:154589
 
Since I started Cerulea I've been trying to figure out how I wanted to do spells and effects. I wanted something that could easily save with the character and pick up where it left off when the mob was loaded from a savefile. Eventually I settled on making them objects, something like...

obj/effect/drunk
Effect()
usr << pick("You feel woozy.","You hiccup.","You barf all over the damn place.","You claim Mary was, in fact, a goblin.")
strength--
if (!strength) del(src)
spawn(60) Effect()

But now I'm afraid of the, um, effect all these objects would have on the game. Backgammon was pretty damn slow when it had to scroll through 635532 objects to do anything, and while Cerulea won't have *that* many, it will have a lot. Maybe a thousand "real" objects in the game, plus 0-10 or so "effect" objects per character.

So I've been wondering if the game would run quicker if, instead of adding objs to the mob.effectlist, I'd add more lists.

mob/verb/drink(O as obj in view())
usr << "You drink [O]!"
Effect("drunk",10)

mob/proc/Effect(theeffect,thestrength)
if(theeffect == "drunk")
var/list/thelist = list("initialize","proceed","strength")
thelist["initialize"] = "Drunkinit"
thelist["proceed"] = "Drunkproceed"
thelist["strength"] = thestrength

call(src,thelist["initialize"])(theeffect,thelist)
src.effectlist += theeffect
src.effectlist["theeffect"] = thelist

mob/proc/Drunkinit(theeffect,list/thelist)
usr << "You feel yourself getting drunk!"
Drunkproceed(theeffect,thelist)

mob/proc/Drunkproceed(theeffect,list/thelist)
usr << pick("You feel woozy.","You hiccup.","You barf all over the damn place.","You claim Mary was, in fact, a goblin.")
thelist["strength"]--
src.effectlist[theeffect]["strength"]--
if (!thelist["strength"])
src.effectlist -= theeffect
return

spawn(60) Drunkproceed(theeffect,list/thelist)

Then when mobs are loaded from savefiles...

var/list/l
for (l in src.effectlist)
if (l["proceed"]) call(src,l["proceed"])(theeffect,l) //how do I access the name of a list? Am I doing this right?

Anyway... do you think that would make a speed difference in the game or is it more work for little benefit? (Effects may often have more variables than just "strength".) Any other ways to do it?

And is there any difference in CPUsage between spawning something and just using do... while? (I may have asked that before.)

Z
obj/effect/drunk
Effect()
usr << pick("You feel woozy.","You hiccup.","You barf all over the damn place.","You claim Mary was, in fact, a goblin.")
strength--
if (!strength) del(src)
spawn(60) Effect()

Z--I think you'll be fine with the approach you're currently using (and it's a heck of a lot more readable than the alternative you proposed). :) However, you might consider using data objects ("datums") instead of objs--there will be less memory used both at runtime and in your savefiles.

How would you do this? Simple. Just change the first line of your code to this:

datum/effect/drunk

or even

effect/drunk

(Both would work fine, but the one starting with "datum" is probably the "right way" to do it.)

The reason is that objs are atomic objects--that is, they can be drawn on the map, which means they have extra overhead that really isn't needed for this purpose (like a loc, and an icon, and so on). See the chapter on "Data Objects" in The Dream Maker for more details!
In response to Guy T.
On 12/22/00 1:18 pm Guy T. wrote:
Z--I think you'll be fine with the approach you're currently using (and it's a heck of a lot more readable than the alternative you proposed). :)

Hehehe. It was easier to write too!

However, you might consider using data objects ("datums") instead of objs--there will be less memory used both at runtime and in your savefiles.

The reason is that objs are atomic objects--that is, they can be drawn on the map, which means they have extra overhead that really isn't needed for this purpose (like a loc, and an icon, and so on). See the chapter on "Data Objects" in The Dream Maker for more details!

Ohhhhh! I wonder if I could do this with all my objects? I'd have to give them "loc" vars, I suppose, and I wonder if those would work the same as the built-in loc vars. Do datums... uh, data... end up in world.contents? Could I make all my mobs datums too? Can you log in as a datum?

All right, all right, I'll go read the chapter. I had always assumed that "data objects" referred to using objs to hold data!

Z
In response to Zilal
On 12/22/00 2:22 pm Zilal wrote:
On 12/22/00 1:18 pm Guy T. wrote:
Z--I think you'll be fine with the approach you're currently using (and it's a heck of a lot more readable than the alternative you proposed). :)

Not only would your original approach be more readable, but your alternative is just recreating what a data object is without the efficiencies built into DM.


Ohhhhh! I wonder if I could do this with all my objects?

If something has a persistent physical presence in the world, it should be an obj or a mob.

If it does not have a persistent physical presence (a spell effect is an example) then it should not be an obj or mob. If it is not an obj/mob/turf, then it is not in world.contents. world.contents holds the physical stuff.


I'd have to give them "loc" vars, I suppose, and I wonder if those would work the same as the built-in loc vars. Do datums... uh, data... end up in world.contents? Could I make all my mobs datums too? Can you log in as a datum?

If you do this I will have to come spank you.

Unless of course that would just be encouragement.


All right, all right, I'll go read the chapter. I had always assumed that "data objects" referred to using objs to hold data!

Yeah it's confusing. When editing the book, I proposed a new terminology that I think Dantom liked but it was too late to make the switch... I'll mention it here in case it helps...

Objects are any kind of object. The definition of an object is a package of data and functions to operate on that data.

Game objects are objects with a physical presence in the world, that can move, have a loc, can be seen if you have a map, etc.

Data objects are objects that do not have a physical presence.

Something should not be a game object unless it has a specific reason to be.


As for the general question of bloat...I'm just guessing, but I suspect that Cerulea does a lot of stuff all at once. Like running through all spell effects in the world in one tick and such.

The only way to avoid slowness here is to not do everything at once.

Have an event loop. Create mobs on a slightly randomized event schedule, and every X ticks have a mob check itself for spell effects.

For my games that have zones and such, what I do is this:

The game has an event loop that calls each zone each tick.

Each zone puts together a list of the things it needs to do in the near future. When it gets a turn, it first lets all the players in that zone do their thing. Then it does a set number, say 5, of other things. This may be NPC turns, spawning, whatever. Then it waits for the next turn and does the next 5 things on the list. When the list is empty it puts together a new list of stuff to do.

This way in one second a zone may do 50 things, but those 50 things are all evenly spaced out.

The entire game might do 200 things in that second, which is better than doing 200 things in a tick and grinding the game to a halt.

A final note...you should avoid lots of

for (x in world)

stuff. Each time you do that, every single object/mob/turf in the world is searched through. Very expensive.

For anything where you are commonly doing such a search, consider maintaining a global list yourself. Whether of players or mobs or objects or a subset of those...then you can do

for (x in my_list_of_mobs)

and it will be about a zillion times faster.

It's very easy to have things add themselves to a global list in their New() proc.