Full Demo Here: http://www.byond.com/developer/Zecronious/ ActionOrientationExample
I would like to introduce to you all what I believe to be a new style of programming in DM. This style has many advantages over the built in way of programming. As a lot of you will know DM does not support multiple inheritance. This is my work around.
To demonstrate I have written a comparison below of a typical approach and an action orientated AO approach.
----------------------------------------------------------------
The Problem
You are tasked to build two items. One is TNT and one is a stopwatch. The TNT is special because it will explode when it's time limit has expired. The stopwatch is special because it will beep when it's time has expired.
----------------------------------------------------------------
A Typical Approach
First creating an object that keeps track of time.
obj/Timed_Object
var/running = 0
var/time = 0
var/max_time = 10
proc
start()
running = 1
keepRunning()
stop()
running = 0
keepRunning()
spawn()
while(running)
if(time >= max_time)
finished()
return
time += 1
sleep(10)
finished()
getTime()
return time
getMaxTime()
return max_time
Making the stopwatch child of Timed_Object.
obj/Timed_Object/Stopwatch
getTime() // Counts downward
return max_time - time
finished()
world << "BEEP BEEP"
Making the TNT child of Timed_Object.
obj/Timed_Object/TNT
finished()
world << "BOOOOM"
What's Wrong With This Approach?
TNT and stopwatches are very different items. Explosives will share different functionality to a stopwatch. The only way to now group explosives together is to extend our object tree even further creating a horribly large and verbose object tree.
Do you really want to type this out? Of course you don't. It's ridiculous.
var/obj/Timed_Object/Explosive/TNT/myTNT = new/obj/Timed_Object/Explosive/TNT()
And what if the explosive is a remotely detonated explosive? Now it has to be a Timed_Object? That would be crazy.
----------------------------------------------------------------
An Action Orientated Approach
We start by implementing these methods. Datum is the parent to all objects. So we give the method to all objects.
It has error handling that I've made.
datum
proc/performAction(ACTION)
actionError(ACTION)
proc/actionError(ACTION)
world.log << "Runtime Error:\n\tsrc:[src] [src.type]\n\tusr:[usr] [usr.type]\n\taction:[ACTION]\nThis action was not found to exist in src."
Creating an independent Timer object.
obj/Timer
var/running = 0
var/time = 0
var/max_time = 10
var/atom/parent
New(PARENT) // Keep track of the owner of this object
parent = PARENT
proc
start()
running = 1
keepRunning()
stop()
running = 0
keepRunning()
spawn()
while(running)
if(time >= max_time)
parent.performAction("timer finished") // Tell the parent it's done.
return
time += 1
sleep(10)
getTime()
return time
getMaxTime()
return max_time
Creating an instance of the Timer object inside our stopwatch.
obj/Stopwatch
var/obj/Timer/timer
New()
timer = new/obj/Timer(src)
proc
soundAlarm()
world << "BEEP BEEP"
performAction(ACTION)
switch(ACTION)
if("timer finished") soundAlarm()
else
..() // Error
Creating an instance of the Timer object inside our TNT.
obj/TNT
var/obj/Timer/timer
New()
timer = new/obj/Timer(src)
proc
explode()
world << "BOOM"
performAction(ACTION)
switch(ACTION)
if("timer finished") explode()
else
..() // Error
What's Better About This Approach?
Now we have don't have to build such a huge tree of objects. We have our Timer objects for remembering the time, our explosives for exploding and our stopwatch for counting down for us. Everything does what it's supposed to do.
----------------------------------------------------------------