ID:39514
 

   Howdy, I'm YMIHere and I'll be your tour guide today. We will be discussing the joys of type casting on this trip. What? You don't know what that is? I was afraid of that, but if you've done any programming in the DM language you've probably used type casting. How? I don't know how you do it without knowing it; hopefully you know what it does and just don't know what to call it. Just stop asking questions and let me explain already.

   Type casting is implicitly telling the compiler (Dream Maker) what type a variable is, and despite what the compiler thinks it will listen to you. You typically do this so that you can access localized variables (variables that belong to a specific atom). Are you ready for an example? OK then, read on.

obj/pickle_sammich

mob/Login()
var/obj/pickle_sammich/ps=new(src)
..()

   Here, you are telling the compiler that the variable ps is of type /obj/pickle_sammich. That looks normal, right? If so, good; it's a nice way to do things since it allows you easy access to the object's local variables. Let me get more in-depth with this example.

obj/pickle_sammich
var/hasLettuce

New()
hasLettuce=rand(0,1) //Decide randomly if the sammich has lettuce.

mob/Login()
var/obj/pickle_sammich/ps=new(src)
if(ps.hasLettuce) //Checking a variable itself will return FALSE if it is 0, "", or null.
src <<"Ewww, lettuce. =("
else

src<<"Yum, pickle sammich!"
..()

   You can easily tell if the sammich has lettuce, but some of you know that this isn't the only way to access localized variables. I'll write an example of another way now, and then we can get back to the discussion.

mob/Login()
var/ps=new/obj/pickle_sammich(src) //No type casting (ps is just a var).

if(ps:hasLettuce)
src<<"Ewww, lettuce. =("
else
src<<"Yum, pickle sammich!"
..()

   Can anyone tell me why this won't work? Of course you can't, it does work. Why? I'm going to tell you -- don't be so impatient. The special part here is the colon. The colon, much like type casting, tells the compiler to keep its thoughts to itself. It will accept that ps has a variable called "hasLettuce", and will allow you to compile.

Well, if you can compile, you don't have any errors, right? Wrong; it only means there are no compile-time errors. Say, for instance, you accidentally misspelled your variable name as "haslettuce" or "hasLetuce", then you would get a run-time error, because it's trying to access a variable that doesn't exist. Sure it'd be easy to see when testing this example... but if you had a huge game with colons all over the place, you might not find the error at all during testing. That's a lot of trouble that could have been avoided by type casting. Now that we've covered the evilness of the colon, I should probably talk about the evilness of type casting.

   Type casting, as with anything that puts faith in the programmer, can be dangerous also. Allow me to demonstrate with another example.

obj/pickle_sammich
var/hasLettuce

New()
hasLettuce=rand(0,1)

obj/cucumber_sammich
var/hasCheese

New()
hasCheese=rand(0,1)

mob/Login()
var/obj/pickle_sammich/ps=new /obj/cucumber_sammich(src)
if(ps.hasLettuce)
src<<"Ewww, lettuce. =("

else
src<<"Yum, pickle sammich!"
..()

   Does that look strange? It should, but why does it compile? Again, the compiler takes your word for it that the cucumber_sammich it's making is a pickle_sammich. That's a really silly mistake to make, you say? I couldn't agree with you more. With new() creating the type you defined by default anyway, there's really no reason to make a mistake like that. We're not finished yet, though; there are other, harder to notice, ways of accidentally assigning one object to another type. It's example time!

obj/var/timesBumped=0 //Keep track of how many times an object has been bumped into.
atom/movable/Bump(obj/O)
O.timesBumped++

   Pop quiz! This compiles; does that mean nothing is wrong? Correct you are: just because it compiles does not mean everything works fine. DM is pretty good at catching errors, but it certainly can't catch them if you're telling it what to think. Do you know what is wrong? No? Not going to guess at all, huh? OK, you're no fun. Bump([dense atom]) is called when an atom's movement fails due to density. If the source of the collision is an obj, everything will be fine -- but other things can be dense. If it was a dense turf, you'd get a run-time error for trying to access a variable that doesn't exist. How are we to fix this, you ask? Well, for this one I'm going to use isobj() since I'm only checking if it's of the base /obj type. You can use ismob(), isturf(), and others like them, but if you want specific sub-types istype() is the way to go. Now it's time for another example.

obj/var/timesBumped=0

atom/movable/Bump(obj/O)
if(isobj(O))
O.timesBumped++
else
O<<"You're not an obj, go away!"

   Here I've just added an if() statement to check if O is actually of type /obj. Another, slightly safer, way to do this would be to use the default type casting of the arguments for that procedure. By the way, if you don't know the default type casting for a certain procedure, you can look it up in the Help File for Dream Maker, or the online reference. Now that we know that the default type casting for the sole argument (Obstacle) in Bump() is /atom, I will use it in my next example.

obj/var/timesBumped=0

atom/movable/Bump(atom/A)
if(isobj(A))
var/obj/O=A
O.timesBumped++
else
A<<"You're not an obj, go away!"

   Now what if you know that you have a mob, but want to do something different if it is a player? Let me show you (no, not every time, just right now. Don't page me just to get a /mob variable type cast to /mob/player).

mob/player/verb/Kill(mob/M as mob in oview(src))
if(istype(M,/mob/player))
var/mob/player/P=M
P.Die(src)
else

del M

mob/player/proc/Die(mob/killer)
loc=null // Take them off of the map.
src << "You have been killed by [killer]!"
spawn(30) // Wait approximately three seconds.

loc=locate(1,1,1) // Put them back on the map.

   I know what you're thinking "...but YMI, how do we know we have a mob?" Simple: as restricts input from a player to one of a few certain types (which can be found here). This is specifically for getting input from a player through verb arguments or the input() proc, though.

   Well, there you have it. Type casting saves you from errors that the colon can't, but it's not entirely safe either. I, like a lot of BYOND developers, prefer type casting because it's a lot easier to avoid errors with. There is really nothing wrong with either method, though; it's up to the programmer to use them correctly.

Lotsa formatting bugs in the code. :(
Whats withj the spans o.O
All better.
Thanks Nadrew! I thought it looked OK on my PC, but I guess I was wrong. Not the first time that's happened.
src<<<"Ewww, lettuce. =("
just to let u know there is an erroe here
Fixed! Thanks.