ID:1409008
 
This one's going to be a short one, we all know that libraries like S_Damage and F_Damage have been around for quite a number of years, and are some of the most widely used libraries on BYOND.

I'd like to take a minute to address some issues that I've had with such libraries, and offer some advice to anybody planning to use such a library until the issues I address are corrected in the core libraries.


#1: Numeric parsing.

Most of these libraries convert a number to text, and then copy each character individually to apply icon states to each value.

I use a different method, which is significantly faster, and frankly, much quite a lot more portable to another language.

Test method #1: (My approach)

Test1(damage)
var/digit
for(var/fac=1;fac<=damage;fac*=10)
digit = round(damage / fac) % 10


Comparison method:

Test2(damage) //taking the value as raw text
var/digit
for(var/pos=1;pos<=length(damage);pos++)
digit = copytext(damage,pos,pos+1)


Test run of 1,000,000 iterations:

Results:

Test1: 2.742 Total CPU
Test2: 4.754 Total CPU



I could go further into my approach, and the particular optimizations I have used. However, it's all quite a bit better shown than told.

I've increased some overhead slightly in handling my approach with images as opposed to overlays, however, using images allows me to cache the objects and only generate ten/eleven total objects used for the entire world's damage values.

I've also taken the liberty of switching my function over to using the new atom.color feature BYOND 500 has supplied.

#define DMG_LAYER 20

var
list/damage_numbers = newlist(/obj/damage_num{icon_state="0"},/obj/damage_num{icon_state="1"},/obj/damage_num{icon_state="2"},/obj/damage_num{icon_state="3"},
/obj/damage_num{icon_state="4"},/obj/damage_num{icon_state="5"},/obj/damage_num{icon_state="6"},/obj/damage_num{icon_state="7"},
/obj/damage_num{icon_state="8"},/obj/damage_num{icon_state="9"},/obj/damage_num{icon_state="miss"})

//only used for prototyping partial appearance data for damage numbers
obj
damage_num
icon = 'damage.dmi'
layer = FLOAT_LAYER

proc
DamageNum(damage, color=NORMAL_FONT)
var/image/gen = image(null,src,DMG_LAYER)
var/obj/place
if(isnum(damage))
var/offset = 0
var/st

//iterate through damage based on factors of 10
for(var/fac=1;fac<=damage;fac*=10)
place = damage_numbers[round(damage / fac) % 10 + 1] //grab the appearance object
place.pixel_x = -offset * 9 //set the pixel_x of the object
gen.overlays += place //add the overlay to the base object
offset++

gen.pixel_x = src.damage_offx + (offset-1)*9
else
place = damage_numbers[11] //position 11 is just the miss state
gen.overlays += place
//gen.pixel_x = src.damage_offx - 18 //ensure it is centered

gen.color = color //apply multiplicative blending
//gen.pixel_y = src.damage_offy //set the base object's pixel y

//add the base object to src overlays for 1 second
world << gen //this should probably be replaced with a more efficient qualifier
spawn(5)
del gen
You're not using animate() for the animation?

F_Damage wouldn't allow you to use animate() anyway.

Animate can now be used with floating damage since my approach generates a base /image object in which each digit is contained.
Just something I noticed that was off with this:

gen = damage_numbers[11]                         //position 11 is just the miss state


gen is an /image type and you are referencing it to an /obj type, so this itself seems like unintended behavior. It would be fine if you weren't trying to display it to world.

world << gen    //this should probably be replaced with a more efficient qualifier


Which basically just displays the "miss" object to the world's output instead.

The best solution I came up with is just use everything you've already been doing for !isnum(damage):

            else
var/obj/place
place = damage_numbers[11] //position 11 is just the miss state
gen.overlays += place //add the overlay to the base object
gen.pixel_x = src.damage_offx - 18 //ensure it is centered

Good catch there, product of 3 am programming.
But... Wouldn't a maptext-enabled object be much better for this?

Simply send the string ("Miss", or "[damage]" or whatnot) to the object, and then animate() it to do whatever you want? (rise, fade, change color, expand in size, combination of the above, etc.)

?
Unfortunately, MapText doesn't play nicely with animate().
Oh. Well that sucks. (though now that you've said it, I think that I now recall reading that once or twice around here)
In response to Ter13
Ter13 wrote:
Unfortunately, MapText doesn't play nicely with animate().

I'm not sure if I'm missing something here, but I don't understand what you mean when you say MapText doesn't play nicely with animate().

For example:
mob/var/health=10

mob/verb/Sucker_Punch(mob/M in get_step(src,src.dir))
var/tmp/damage=rand(1,10)
if(prob(50))
new/effect/damage(M.loc,"<font color=red><b>[round(damage)]</b></font>")
M.health-=damage
else
new/effect/damage(M.loc,"Miss")

effect/damage
parent_type = /obj
layer = FLOAT_LAYER
New(newloc, val)
maptext = val
src.loc=newloc
sleep(7)
animate(src,transform=matrix()*10,alpha=0,pixel_y=96,time=3)
spawn(10)
del(src)

Works fine without any problems and makes use of animate(), without over complicating anything.
^And suddenly, I just remember that animate() can modify pixel_x/pixel_y. It sure doesn't play nice with transform, though, which is what I was thinking.

Good correction there, MoI.
No worries, animate() is indeed proving itself to be useful in shaving down the amount of programming required to perform simple tasks. It also has control over maptext_width and maptext_height. So if you need to use transform and you're having trouble with it, then you might want to look into playing those two variables in animate().