ID:138454
 
I thought it would be nice for everything in my world to have some physical properties, so I decided to have a base material for my objects, I thought I might even investigate having multiple materials for my objects.

What I tried to do was to create material objects e.g.
/obj/material/wood

and then give my real objects a variable that connected them to the material obj, e.g.
obj
var/obj/material/mater

and then for my objs say mater = /obj/material/wood

I'd hoped I could use it along the lines of src.mater.name to get wood, but all I can ever output is obj/material/wood, if I try to ref a variable of this e.g. .name I get a null src. I 've tried using 'as obj' in my variable def. , but I suspect I'm doing this fundamentally wrong. I don't really want to just make all my obj the children of a material - it seems a bit limiting.

I think I remember Deadron posting something about this when discussing C, but I can't find the post.
On 8/1/00 8:31 am Al wrote:
I thought it would be nice for everything in my world to have some physical properties, so I decided to have a base material for my objects, I thought I might even investigate having multiple materials for my objects.

What I tried to do was to create material objects e.g.
/obj/material/wood

and then give my real objects a variable that connected them to the material obj, e.g.
obj
var/obj/material/mater

and then for my objs say mater = /obj/material/wood

You have the right idea here. The problem is that you are assigning an obj var to a type, rather than an actual object. This is a very common theme in DM, so perhaps we should have some clean notation around. There is a document discussing types & objects in the tutorial section somewhere, so that might help.

With the new associative lists, it is pretty easy to make a general function to handle this. Let me give it a try:

// this stuff would be better wrapped up in its own "class",
// but we'll do it this way for now:

var/matList[0] // global material list
proc/matInit() // call in world/New() perhaps
var/mat
for(mat in typesof(/obj/material)) // enumerate all types
matList[mat] = new mat // create new placeholder type

and then you can do:
obj/sword
New() // must assign material at runtime
mater = matList[/obj/material/metal]

I think that should work.

However, this would better be done if you could assign the material via a type at compile time (your way) and then use it later as an object at runtime (my way). I think we can accomplish this by doing some behind-the-scenes tricks. Let me get back to you on it.
On 8/1/00 8:31 am Al wrote:
What I tried to do was to create material objects e.g.
/obj/material/wood

and then give my real objects a variable that connected them to the material obj, e.g.
obj
var/obj/material/mater

and then for my objs say mater = /obj/material/wood


The problem with this specific code is that you need to create a wood object in order to use it. Whenever you define an object, you can't actually use it until you've created it using new().

However, it's unlikely you would want to make an obj subclass in this case. /obj is the superclass for objects that have a physical presence on the map, taking up space and having an icon. It sounds like you just want a "data object", which is an object that is used by your objects, but does not take up space on the map.

If I'm right, then you'd want to do something like this.

First, decide what attributes are common to all materials and put those in the materials class. Because the materials class (as I'm using it) is not a "game object", that is, not a subclass of /obj or /mob intended to show up on the map, it doesn't automatically have a name variable and we have to give it one if we want it.

material
var
name
weight
color
flammability


Now that material has been defined, we can define the wood class. If wood has any unique attributes that not all materials have, you can specify them here.

material/wood
var
graininess // 1 - 100% grainy
rings // Number of rings = age
old_growth // 1 or 0 -- is this from an old growth forest?


Now that we've defined a wood class, you need to decide how you will use it. Will you have one or more globally available wood objects or will you define the attributes of the wood for each class using wood?

For this example I will have a couple globally available kinds of wood.

/var/material/wood/old_wood
/var/material/wood/young_wood

Now to use these, I need to actually create the objects somewhere. You can't use an object you have defined if you haven't called new() somewhere. Since these are globals, I will do this in world.New():

world/New()
// Set up the old wood
old_wood = new()
old_wood.name = "Fine aged wood"
old_name.weight = 2
old_name.flammability = 100
old_name.old_growth = 1
// etc...

young_wood = new()
young_wood.name = "Supple green wood"
// etc...
return ..()

Now that these global objects have been defined, you can use them wherever. So if we add a matter variable to all objects...

obj/var/material/matter

We can finish things off like so:

obj/tree
New()
matter = old_wood


In response to Tom H.
On 8/1/00 8:57 am Tom H. wrote:

However, this would better be done if you could assign the material via a type at compile time (your way) and then use it later as an object at runtime (my way). I think we can accomplish this by doing some behind-the-scenes tricks. Let me get back to you on it.

It hit me. Here we go:

obj
var/obj/material/mater // initially assign to a prototype as you were doing
New()
if(mater)
var/materInst = locate(mater)
if(!materInst) // doesn't exist in the world yet
materInst = new mater // make a new one at null
mater = materInst // now we are the instance

Do you see the idea? This is a bit inefficient because we do a bit of computation for each new obj, but I think it is negligeable. If not, then you can use the associated list mechanism above in place of the locate() call.

HTH
In response to Tom H.
On 8/1/00 9:13 am Tom H. wrote:
On 8/1/00 8:57 am Tom H. wrote:

However, this would better be done if you could assign the material via a type at compile time (your way) and then use it later as an object at runtime (my way). I think we can accomplish this by doing some behind-the-scenes tricks. Let me get back to you on it.

It hit me. Here we go:

obj
var/obj/material/mater // initially assign to a prototype as you were doing
New()
if(mater)
var/materInst = locate(mater)
if(!materInst) // doesn't exist in the world yet
materInst = new mater // make a new one at null
mater = materInst // now we are the instance

Do you see the idea? This is a bit inefficient because we do a bit of computation for each new obj, but I think it is negligeable. If not, then you can use the associated list mechanism above in place of the locate() call.

HTH


Hmm...I'm thinking this is all a rather complicated and clever approach to present for someone who is dealing with the basic concept of creating objects...

Let's say, this is material for the mid-term, not for the beginning of class...!
In response to Deadron
On 8/1/00 9:16 am Deadron wrote:

Let's say, this is material for the mid-term, not for the beginning of class...!

Point well taken. At any rate, clear, sensical code is better for everyone-- regardless of experience. Your example is a lot better anyway!
Thanks Tom and Deadron, very helpful, I didn't realise you could create data objects outside the 4 main types. V. useful!

Al
In response to Al
That's AI as in Al's Intelligence...
Sorry about this (I did read your tutorial Tom!).
I decided to try out the Deadron approach, which with some minor mods compiles OK.

My problem is actually referencing the data.
obj
chest
New()
matter = old_wood

...

usr << src:matter
and out comes null.

So I tried usr << src:matter.name
error null.name

The only bit I couldn't really follow was
obj/var/material/matter
I thought that it should have been
obj/var/matter
but that didn't work either.
however usr << "[old_wood.name]" works fine.

If you could spare a minute :)

Thanks
In response to Al
On 8/1/00 11:11 am Al wrote:
I decided to try out the Deadron approach, which with some minor mods compiles OK.

Yeah I noticed I had mistyped some stuff in there. Sorry about that.


My problem is actually referencing the data.
obj
chest
New()
matter = old_wood

...

usr << src:matter
and out comes null.

Are you creating the chest anywhere? At some point your code needs to have something like:

var/obj/chest/myChest
myChest = new()


The only bit I couldn't really follow was
obj/var/material/matter
I thought that it should have been
obj/var/matter

Either approach would compile. What my declaration was doing was being specific about what kind of object this variable is.

The first part of the declaration just tells DM that we are talking about a variable for the /obj class:

obj/var

If you then just give a name for the variable:

obj/var/matter

Then you are saying that this variable could represent anything. Which is fine if you want it to be that generic, but it means that DM can't provide compile-time checking to make sure you are using procs and vars that are valid. So you can't call things directly using the period like so:

matter.name // Causes compilation error because DM doesn't know if matter has a name

And you are forced to tell DM not to pay attention to this, by using the colon operator:

matter:name // Will compile, but if matter doesn't really have a name, you'll get a crash while running the game

In general your code will be less buggy if you are as specific as possible about what the variable is.

So you can say specifically that the matter variable will always be a material object by adding it do the variable declaration:

obj/var/material/matter

Now if you try to call something that doesn't exist, DM will tell you as soon as you compile:

matter.bogusProc() // DM will error because the material object doesn't have a bogusProc()...

I hope that makes some sense...
In response to Deadron
On 8/1/00 11:58 am Deadron wrote:
Are you creating the chest anywhere? At some point your code needs to have something like:

Well, I have an instance of the chest on my initial map and I'm using that as the src. I can reference its name and all its variables apart from matter, interestingly the compiler accepts O.matter.name , but at run time matter is null. I'm definitely setting it in New() under obj/chest.

Here is an example of the problem, the map has a single instance of a chest by the player start.

world
mob = /mob/player
turf = /turf/floor
New()
old_wood = new()
old_wood.name = "Fine aged wood"
old_wood.weight = 2
return ..()
mob
player
icon = 'player.dmi'
verb
test()
var/obj/O
for(O in view(3)){
usr << O
usr << O.matter
usr << O.matter.name
}
turf
floor
icon = 'floor.dmi'
obj
chest
icon = 'chest.dmi'
New()
matter = old_wood
return ..()
material
var
name
material/wood
var
weight
var/material/wood/old_wood
obj/var/material/matter

Thanks for your time on this.
In response to Al
world
mob = /mob/player
turf = /turf/floor
New()
old_wood = new()
old_wood.name = "Fine aged wood"
old_wood.weight = 2
return ..()

obj
chest
icon = 'chest.dmi'
New()
matter = old_wood
return ..()


I think world.New() is only called after the map is populated, which would mean obj.New() is called too soon. You might consider giving objs a proc called SetMaterial() -- which can be overridden for different obj types -- and calling it on all objs in world.contents at the end of world.New(). Or you could change the line in New() to read "spawn(1) matter = old_wood".

Hope that helps...
In response to Guy T.
On 8/1/00 1:58 pm Guy T. wrote:
world
mob = /mob/player
turf = /turf/floor
New()
old_wood = new()
old_wood.name = "Fine aged wood"
old_wood.weight = 2
return ..()

obj
chest
icon = 'chest.dmi'
New()
matter = old_wood
return ..()


I think world.New() is only called after the map is populated, which would mean obj.New() is called too soon. You might consider giving objs a proc called SetMaterial() -- which can be overridden for different obj types -- and calling it on all objs in world.contents at the end of world.New(). Or you could change the line in New() to read "spawn(1) matter = old_wood".

Hope that helps...

Aaaaaaaaagh, thanks, everything works now, that feature is a bit of tricky one!

In response to Guy T.
Hope that helps...

Also, just for fun, here's an method that kind of combines Tom's and Deadron's approaches... hopefully it works.

var/list/materials

world
New()
. = ..()
materials = list()

// Create the "eidos" here--single instances of each abstract type
materials["old wood"] = new /material/old_wood()

var/obj/O
for(O in world)
if(O.matter)
O.matter = materials[O.matter]

//List the materials and their properties here
material
wood
old_wood

//Example of how to use it with an obj
obj
var/material/matter

chest
matter = "old wood"
In response to Guy T.
Bah, poopie... that will only work for objects on the map at startup. To handle new objects created during the course of the game, something like this would be needed:

obj
New()
. = ..()
if(materials)
matter = materials[matter]
In response to Guy T.
On 8/1/00 1:58 pm Guy T. wrote:
world
mob = /mob/player
turf = /turf/floor
New()
old_wood = new()
old_wood.name = "Fine aged wood"
old_wood.weight = 2
return ..()

I think world.New() is only called after the map is populated, which would mean obj.New() is called too soon.

Oops -- how I handle this in my code is simply to do this:

world
New()
var/result = ..()
// Do my stuff
return result
I was just pondering the problem of trying to initiate the variables before the objs and I thought about Guy T's ATOM post and I wondered if the creation order was maintained... it is!

Instead of using world/New(), choose an area that only occurs once and that you are not likely to create anywhere during the game (I chose my player starting area) and put your variables there e.g. /area/start/New() It appears that areas are created first and so set up the vars for the turfs and objects that follow.

Elegant?
In response to Al
Instead of using world/New(), choose an area that only occurs once and that you are not likely to create anywhere during the game (I chose my player starting area) and put your variables there e.g. /area/start/New() It appears that areas are created first and so set up the vars for the turfs and objects that follow.

Elegant?

Yes!