I was browsing some source code for a Dragonball Z game recently, when I spotted a little bug, one of many. It was possible for your character's stamina to end up being negative. It wasn't really a big bug, everything carried on working as expected when you had negative stamina, and you could rest to recover to a sensible stamina again. But this got me thinking.
This little bug simply should not have come to be in the first place. Some bugs you can forgive, programming is a complicated thing at times and you can't expect to catch every case first time. But this one should simply never have happened, and yet it does. So Samurais, let me show you a very simple trick that means you never have to worry about this kind of bug.
First, let's look at what we're trying to do with stamina. In this game, it's a percentage. Perhaps in another game it's a value that can grow. But here's the important bit, it is always a number, it can go up and down, and it is never negative. So what we actually have here is not just a number in a variable, there's a little logic involved to make sure the stamina you have is sensible.
Here's a clever little trick I like to use a lot. We'll make a datum to represent this. What's a datum? Whatever you like! It's pretty vague, so hopefully you'll see in our case we're going to make a number that can be restricted. Datums are basically just objects that don't exist on the map, and can be used for these kinds of neat tricks. Here we go, we need a minimum possible value, a current value (our stamina) and a maximum possible value:
restricted_number
var/minimum
var/current
var/maximum
New(var/minimum = 0, var/current = 100, var/maximum = 100)
src.minimum = minimum
src.current = current
src.maximum = maximum
Very exciting, I'm sure. But what's the point? Well, we need to have some way to increase / reduce the current stamina in a way that it doesn't break the minimum / maximum:
proc/set_current(var/number as num)
number = max(src.minimum, number)
number = min(src.maximum, number)
src.current = number
proc/reduce(var/number as num)
set_current(src.current - number)
proc/increase(var/number as num)
set_current(src.current + number)
That's a bit neater, now we can stop that weird negative stamina kind of bug. How do we check if we're at maximum / minimum stamina though? We could just compare the values, but lets add some procs to make our code easier to read:
proc/at_min()
return src.current == src.minimum
proc/at_max()
return src.current == src.maximum
Now we're ready to rock. How do we use it? Here's an example, with a rest() proc:
mob/player
var/restricted_number/stamina = new()
verb/Rest()
while(!src.stamina.at_max())
src.stamina.increase(10)
sleep(50) // We increase stamina by 10 every 5 seconds.
src << "You stretch, feeling refreshed!"
verb/Whats_My_Stamina()
src << "You currently have [src.stamina.current] stamina."
verb/Tiring_Work()
while (!src.stamina_at_min())
src.stamina.decrease(10)
sleep(50)
src << "Pfff, you're all tired out."
So what is so special about this? Well, the original bug was basically caused by a problem with checking the allowed values of stamina and rounding off stamina when it's below 0. It probably doesn't even happen in all the different places we wrote it, because we're basically just copying bits of code and possibly had to change it. With the restricted_number, if we need to change it, we do it once and only once, where the datum is defined. It will change how it behaves for all uses, and in the same way. One less avoidable bug!
This doesn't just need to be used to stats, it can be used in calculations where you need to restrict some number to a range of values. Anything, damage calculations, power-up calculations, where-ever you want. Same goes, you can extend this to have different behaviour that suits your games. It's a small weapon, Samurai, but it has many uses. Don't leave it undrawn.
Thank you for reading. Full source code is below, feel free to use as you please:
restricted_number
var/minimum
var/current
var/maximum
New(var/minimum = 0, var/current = 100, var/maximum = 100)
src.minimum = minimum
src.current = current
src.maximum = maximum
proc/set_current(var/number as num)
number = max(src.minimum, number)
number = min(src.maximum, number)
src.current = number
proc/reduce(var/number as num)
set_current(src.current - number)
proc/increase(var/number as num)
set_current(src.current + number)
proc/at_min()
return src.current == src.minimum
proc/at_max()
return src.current == src.maximum
---
Got any neat little snippets like this that you want to share with people? Have a question on the code shown here, or the article in general? Talk to us and share here:
http://www.byond.com/members/BYONDAnime/forum?id=37140