ID:138642
 
I must not be doing lists right. Consider the following:

area/var/atmosphere[]

area {
Test_room {
atmosphere.Add("It rains.")
}
}

area/proc/Atmosphere() {
var/list/thelist = src.atmosphere
src << src.atmosphere[rand(1,thelist.len)]
}

Compiling the above will result in an error of "bad argument definiton" for the line AFTER atmosphere.Add(). One error for every argument I put into Add().

If I comment the Add() line out and compile, running Atmosphere() in the game will result in:

Cannot read null.len.
proc name: Atmosphere (/area/proc/Atmosphere)
source file: cerulareas.dm,331
usr: Zilal (/mob/gm)
src: /area/Test_room (/area/Test_room)
call stack: atmosphere

Adding a text value to list/atmosphere during runtime (with the + operator) has no result, but generates no error. What am I missing?

Z
The line...
src << src.atmosphere[rand(1,thelist.len)]

Is different in my actual code, containing a proc (instead of src << ) that broadcasts text to everyone IN src. I just wasn't thinking when I edited the code for the purposes of the post.

Z
On 4/11/00 11:36 am Zilal wrote:
Compiling the above will result in an error of "bad argument definiton" for the line AFTER atmosphere.Add(). One error for every argument I put into Add().

I moved the Add() line into the room's New() proc and it worked... but can't I change the value of a script without using a proc? Nothing I've tried has worked.

And it's probably still of note that the bad argument error was appearing for the line after the erroneous one.

Z
On 4/11/00 11:36 am Zilal wrote:
I must not be doing lists right. Consider the following:

area/var/atmosphere[]

I believe you want:

area/var/atmosphere[0]

to declare an initially empty list.

You've stumbled upon one of our worst notational problems, which we really ought to clear up before we really publicize. In a nutshell:

var/list/L == var/L[] == "reference to a list"

in order to use L, you must assign it to an existing list, such as the object.contents lists, etc. Alternatively, you can create L with the list() or new /list operations.

var/L[0] == "empty list"

this is effectively the same thing as

var/list/L = list()

So the bottom line is that if you want to use a list, you have to make sure that it is valid, either through creation or by the [N] declaration (N being the number of null elements initially present in the list).

Incidentally, the reason for this notation is, oddly enough, just consistency. Lists are objects just like mobs, files, etc, so have to be created. The [0] convention is really just a shorthand for this.

I'm not sure why the compiler is complaining about "Add" though. I'll take a look.
On 4/11/00 11:36 am Zilal wrote:

area {
Test_room {
atmosphere.Add("It rains.")
}
}

Whoops. I didn't even notice this, which is the reason the Add() is complaining. You cannot make runtime assignments (barring a few convenience exceptions) inside an object declaration. You can, however, do it when the object is created, inside New():

area/Test_room/New()
..()
atmosphere.Add("It rains.") // or atmosphere += "It rains"



In response to Tom H. (#3)
Oh, I see. I'd thought [] essentially meant an empty list.

By the way... how do lists save? I can't just write a whole list to a buffer, can I?

Z
In response to Zilal (#5)
On 4/11/00 12:32 pm Zilal wrote:

By the way... how do lists save? I can't just write a whole list to a buffer, can I?

Sure you can!

F << usr.contents
F >> usr.contents

Cool, huh?

In response to Tom H. (#6)
On 4/11/00 1:58 pm Tom H. wrote:
On 4/11/00 12:32 pm Zilal wrote:

By the way... how do lists save? I can't just write a whole list to a buffer, can I?

Sure you can!

F << usr.contents
F >> usr.contents

Cool, huh?

Oh... but what exactly happens in the above code? All the objs in usr.contents, how do all their variables get saved? What about objs in objs in usr.contents?

I am struggling with saving my characters in a game where one could have a diamond in a pouch in a box in a backpack.

Z
In response to Zilal (#7)
Oh... but what exactly happens in the above code? All the objs in usr.contents, how do all their variables get saved? What about objs in objs in usr.contents?

I am struggling with saving my characters in a game where one could have a diamond in a pouch in a box in a backpack.

Yeah, I just experimented with that and nothing I tried really worked. I'm also wondering how you might save a list of runtime generated things with runtime generated names and still be able to reopen them later in their entirety... I need that for my ecosystem and economy simulations in my Iron Glory game to function.

(More about those later... but right now, they're looking pretty sweet! Now if I could just save it for future generations, it would be a starting point for my planet in the making... Iron Glory sort of looks a little like Cerulea with a large bit of changes and different processes... like I said, you inspired me... I'm going to try to get away from making a different version of the same game, though, because my conscience is getting guilty... ack, brain, stop making these stupid run-on sentences... =P)
In response to Zilal (#7)
On 4/11/00 5:14 pm Zilal wrote:

Oh... but what exactly happens in the above code? All the objs in usr.contents, how do all their variables get saved? What about objs in objs in usr.contents?

You've hit upon one of the fundamental problems we've been dealing with in trying to construct an elegant savefile system. Essentially, the question is: "what do we save"? One could just save every property of every reference (ie- save all of the objs in the contents), but oftentimes that isn't desired. For instance, you may have members in your group stored in the mob.group list; saving your guy shouldn't save all of them too, but if we saved all references then it would.

Dan and I have debated a number of possible general savefile schemes. Our current inclination (which changes daily) is to make general saves possible in DM by allowing looping through vars and such, so he will probably put some such provisions into the compiler. Then we can write utility functions to do entire world saves.

But I digress. The answer to your question is that you must define, with each object, what should be saved when the object is written to the savefile. You can do this within the obj (or mob) Write() and Read() procs. These get called whenever you save an entire object to a file. So doing:

F << usr.contents

will call obj.Write() for each obj in usr.contents. That proc might look like this:

obj/Write(savefile/F)
F << name
F << icon
...

And similarly, if inside of obj.Write() you save lists of other objs or mobs, then those Write() procs will be called. This enables as much to be saved as is desired. Be careful when saving references that refer to themselves, though, or bad things will happen!

For an extensive example, lookup the old Night Soil demo on the dung pages. I'll try to move that stuff over tonight.

Does this help?


In response to Tom H. (#9)
One could just save every property of every reference (ie- save all of the objs in the contents), but oftentimes that isn't desired. For instance, you may have members in your group stored in the mob.group list; saving your guy shouldn't save all of them too, but if we saved all references then it would.

I don't think that's too terrible... because when I load the mob from the savefile, I can just change mob.group to have a length of 0 at the end of the proc. Or reset any variable I wanted to.

Does this help?

Kinda... sometimes it's hard to get my mind around new concepts. So if I do the following...

Savemob(M) {
var/savefile/F = new /savefile("Characters.sav")
F.cd = "/[M:name]"
F << M
F << M.contents
}

mob/Write(savefile/F) {
F << src.name
//etc. (and the same with objects)
}

How do I load these things? Do I have to state which buffers I'm saving things to with Write()? What do I do when I load M.contents... what will "F >> M.contents" do? I imagine I'd make obj/Read() include "var/obj/O = new /obj" or something... I guess... do you think I should wait on it until looping through variables is possible? I mean, I have over 100 mob variables, and it gets to be a pain to write all that out.

Z
In response to Zilal (#10)
Savemob(M) {
var/savefile/F = new /savefile("Characters.sav")
F.cd = "/[M:name]"
F << M
F << M.contents
}

mob/Write(savefile/F) {
F << src.name
//etc. (and the same with objects)
}

Actually, you would want to write the mob's contents in mob/Write() rather than Savemob(). You don't have to, but since you are writing all the other mob properties in mob/Write(), it would be best to handle contents there too.

Another nit is that you should not use the mob's name as the savefile directory, since that might not be unique (depending on your game) and since it might contain punctuation or other illegal characters. Instead, use mob.ckey, which is intended for the purpose.

How do I load these things?

99% of the time, you load things in exactly the same way you wrote them except you use >> instead of <<. The only time this is not true, is when you wrote some complex expression rather than simply saving the contents of a variable.



What will "F >> M.contents" do?

That will reconstruct the list you originally saved, creating each object, calling their Read() procs, and finally assigning this to M.contents.

I imagine I'd make obj/Read() include "var/obj/O = new /obj" or something...

No. When you read an object (either explicitely or implicitely as an item in a list), the object will be recreated for you before its Read() proc is called to load any additional values. Otherwise, there would be no way to call Read(), because there would be no object to call it from!

Do you think I should wait on it until looping through variables is possible? I mean, I have over 100 mob variables, and it gets to be a pain to write all that out.

It's your call. I would recommend just getting a skeleton save/load system in place. That will still be useful regardless of whatever labor-saving features we add in the future. For now, you can at least generate the Read() code by cut-and-paste from the Write() code and simply switching the direction of the i/o operators.
In response to Dan (#11)
On 4/12/00 9:30 am Dan wrote:
Another nit is that you should not use the mob's name as the savefile directory, since that might not be unique (depending on your game) and since it might contain punctuation or other illegal characters.

Hi Dan! Long time no see.

Names in my game must be unique and contain no illegal characters. And any account may have a number of characters on it, so I decided to save under character name rather than ckey.

I'm sure after I go fiddle with the system I'll have a whole bunch of problems to bring to the boards.

Z