ID:164817
 
Currenty, I've been working on a simple health bar for my game, Rising Empire. Here's the code I have so far:

obj
Health_Bar
icon = 'Health_Bar.dmi'
icon_state = "4"
screen_loc = "2,1"


The code works wonderfully, and does exactly what it's designed to do. My question is, how would I make the icon_state of the Health_Bar change in fourth increments depending on your health?
Michael3131 wrote:
My question is, how would I make the icon_state of the Health_Bar change in fourth increments depending on your health?

Update it like so after every time you change the health. Basically, this means writing a proc to change the health if you don't already one, and in that proc do the above after setting the health and whatnot.
You will need some sort of update proc, which will keep track of the players HP and then change the bar based on a certain percent of the players health. I would also suggest at this time not to just have a update proc sitting in the player's stat panel. It can cause a lot of lag since it is basically a slow infinite loop until they log out and no longer exist. Instead have a side proc which you will can whenever they take damage or whatever. There are a few libs and demos for this out on BYOND.
In response to Kaioken
Ok, here's what I've got so far:

obj
Health_Bar
icon = 'Health_Bar.dmi'
icon_state = "100"
screen_loc = "9,10"
New()
Refresh()
proc
Refresh()
for(var/obj/Health_Bar/H in usr.client.screen)
var/is
// is = text2num(H.icon_state)
is = round(usr.hp/usr.maxhp,4)
H.icon_state = num2text(is)
sleep(5)
Refresh()


It seems like it should work, yet the health icon doesn't ever change, even though my health is 71/100.
In response to Michael3131
mob
proc
refresh()
for(var/obj/health_bar/A in usr.client.screen)
if(src.hp>=76)
A.icon = 'icon_file.dmi'
A.icon_state = "state"
if(src.hp<=75)
if(src.hp>=51)
A.icon = 'icon_file.dmi'
A.icon_state = "state"
if(src.hp>=50)
//You get it.
That seems easier to me.

In response to Revojake
That would work, but your HP rises everytime you gain a level. =P
In response to Michael3131
Ah. Then do something that is an if statement saying if(usr.level==3) And do it from there.
In response to Revojake
I would do that too, but it gives a random amount between 1 and 5. I like having random so everyone the same level doesn't have the same stats. That's why I really need the rounding code to work.
In response to Michael3131
Oh, that seems fair to me.
In response to Revojake
So you don't know how to get my current proc to work? >_<
In response to Michael3131
--No put usr in proc.

--No ignoring help you've been given [twice in a row].
In response to Kaioken
Well, you could always use a library too. I'm lazy so that's what I did lol.

I would recommend Shadowdarke's HUD meter.
In response to Michael3131
You have two major problems preventing it from working and a couple lesser problems that could cause problems eventually.

Your first problem is that you are using usr, but you have no way of knowing if usr is the mob related to the meter or not. You should pass the related mob to the Health_Bar as a proc argument.

It's probably also a good idea to store the related mob with the health bar, so that it can refer to that mob without having to explicitly pass it to each proc.
mob/Login()
// ... unrelated code ...
my_health_bar = new/obj/Health_Bar(src)

obj/Health_Bar
// ...
var
mob/owner
New(mob/new_owner)
..() // perform the default New() proc
owner = new_owner
if(owner && owner.client) owner.client.screen += src
Refresh()
proc
Refresh()
for(var/obj/Health_Bar/H in owner.client.screen)
// ...



Your second problem is in your state calculation:
is = round(usr.hp/usr.maxhp,4)


hp/maxhp will be a number between 0 and 1 (unless you allow hp to go outside of the maxhp.) 71 hp and 100 maxhp results in 0.71. The second argument in your round() command means you are rounding to the nearest multiple of 4. In this case, the nearest multiple of 4 will always be 0.

You probably meant to round the percentage to the nearest multiple of 4, which would be
is = round(100*usr.hp/usr.maxhp,4)


That's a little clunky though, because you'll have 26 icon_states named as "0", "4", "8", "12", ..., "96", "100". I find it a little easier to just number them from 0 to X, where X is one below the desired number of icon states. Then your formula would look something like:
is = round(X * owner.hp / owner.maxhp)

For a meter numbered 0 to 25, it would be
is = round(25 * owner.hp / owner.maxhp)

You should also be sure to limit it to the appropriate range. (no higher than 25, no less than 0.)
is = min(25, max(0,round(25 * owner.hp / owner.maxhp)))


Now for the errors that don't stop it from working, but will give you trouble in the long run:

You have an infinite recursive loop. Refresh() calls itself without allowing the previous Refresh() call to finish. Run this game long enough, and you'll get infinite loop crashes in that proc. You could rewrite the loop a few different ways to prevent this problem, but you don't even need a loop here at all because...

In periods when the mob's health isn't changing, this is causing a lot of needless work. It's probably not enough processing power to cause problems, but it could cause bandwidth problems if the game has a lot of players. Meters should only be updated when the related stats change. I usually handle this by providing an Update() proc on the meter (like your Refresh() proc) and calling it anywhere in the code that the related attributes change:
mob/proc
damage(attacker, amount)
hp = max(0, hp - amount)
if(my_health_bar) my_health_bar.Update()
// ...

GainLevel()
var/hpgain = rand(1, 5)
maxhp += hpgain
hp += hpgain
if(my_health_bar) my_health_bar.Update()
// ...


Since meters are a common part of any game, it makes since to use a common library to handle them so you don't have to write the same code for every new game you write. Unless you really want to enjoy the learning experience of creating your own, you can use my HUDmeter library and save yourself a lot of work. It allows for some very versatile meters with very little effort on your part. Feed the meter some parameters when you create it, and then just call its Update() proc whenever appropriate.

Pyro_dragons calls that lazy, but I call it smart. ;)
In response to Shadowdarke
No I call myself lazy lol.
In response to Shadowdarke
Shadowdarke wrote:
It's probably also a good idea to store the related mob with the health bar, so that it can refer to that mob without having to explicitly pass it to each proc.

It's a better idea anyway to use 1 screen object for multiple mobs instead of unique ones for each. Actually then it will also be less good to store references to the mobs on the health bar obj.

Also, about your example code:

-Its creating the health bar obj INSIDE the player. That won't be wanted.

-If the mob doesn't have a client you should simply 'del src' and abort the whole thing, rather than continuing to call related procs.
In response to Kaioken
Kaioken wrote:
Shadowdarke wrote:
It's probably also a good idea to store the related mob with the health bar, so that it can refer to that mob without having to explicitly pass it to each proc.

It's a better idea anyway to use 1 screen object for multiple mobs instead of unique ones for each.

How so? One screen obj for multiple players is definitely the way to go for static objs, but I don't see any significant gain when doing it for dynamic objs.

You still need to send individualized icon_state information to each client, so presumably you would be attaching an image in your single obj system and passing just as much data through the network. You end up with (number of clients + 1) datums to manage your meter, one more than if you had simply used objs to begin with.

If one object per client puts you over BYONDs obj limits, then you have very significant problems in obj management and I highly doubt if cutting out this meter obj will compensate.


Also, about your example code:

-Its creating the health bar obj INSIDE the player. That won't be wanted.

Very true. I should not assume anyone knows to override the default placement. The example does however serve its purpose to illustrate how to pass arguments.


-If the mob doesn't have a client you should simply 'del src' and abort the whole thing, rather than continuing to call related procs.

Another good point. There are cases where you might want the object to persist for non-player mobs but within the context I described in the brief demo, it should only belong to mobs with clients.
In response to Shadowdarke
Shadowdarke wrote:
How so? One screen obj for multiple players is definitely the way to go for static objs, but I don't see any significant gain when doing it for dynamic objs.

You still need to send individualized icon_state information to each client, so presumably you would be attaching an image in your single obj system and passing just as much data through the network [...]

No. That would be just plain stupid. Are you calling me stupid?

Anyway, the number of different states for the health bar would be something like just ~5. If you have more than 6 players, your way will be wasting objects.
A simple way you can use to avoid this is using 5 global objects with the different icon_states. Then you can switch player's client.screen health bar objects around when a change is needed. Sure, if you had 1 player that used all states, you'd be wasting 4 objects, but presumably you'd expect your game to mostly have more than 5 players, which is when you'll be saving objects.

If one object per client puts you over BYONDs obj limits, then you have very significant problems in obj management and I highly doubt if cutting out this meter obj will compensate.

Calling me stupid again? :o
Theres more to wanting to keep low object usage then simply the limits, including plain cleanliness.
In response to Kaioken
Kaioken wrote:
Shadowdarke wrote:
How so? One screen obj for multiple players is definitely the way to go for static objs, but I don't see any significant gain when doing it for dynamic objs.

You still need to send individualized icon_state information to each client, so presumably you would be attaching an image in your single obj system and passing just as much data through the network [...]

No. That would be just plain stupid. Are you calling me stupid?

I'm not a judgmental person. You provided no additional information in your initial post for me to discern how you intended to implement the idea, so I asked.


Anyway, the number of different states for the health bar would be something like just ~5. If you have more than 6 players, your way will be wasting objects.
A simple way you can use to avoid this is using 5 global objects with the different icon_states. Then you can switch player's client.screen health bar objects around when a change is needed. Sure, if you had 1 player that used all states, you'd be wasting 4 objects, but presumably you'd expect your game to mostly have more than 5 players, which is when you'll be saving objects.

That's a pretty clever solution and great for situations when the number of players exceeds the number of icon_states in your meter. I've never used a meter with less than 31 states except for demonstration purposes and so far none of my games are designed for so many simultaneous players on a single server, so it's not particularly useful in my case.

I am curious about the amount of data passed using your method though. Changing an icon_state in the current BYOND architecture also passes additional information. I'll have to play with it when I have some time.


If one object per client puts you over BYONDs obj limits, then you have very significant problems in obj management and I highly doubt if cutting out this meter obj will compensate.

Calling me stupid again? :o
Theres more to wanting to keep low object usage then simply the limits, including plain cleanliness.

Perceived cleanliness in this case is largely a matter of personal preference. Is it cleaner to keep all your rubbish in a single bin, or to separate it into recyclables, green trash, and general trash? Either way, the garbage is out.
In response to Shadowdarke
Shadowdarke wrote:
I've never used a meter with less than 31 states

Ugh. O_o
Professional, huh? I'll see if I can think up something better to go with a lot of states. It won't be as simple, though. The aforementioned way works though for your usual (well, I think it is :o) small 5-state bars.


Perceived cleanliness in this case is largely a matter of personal preference.

Yes, of course. Just saying that I don't like using many objects regardless of the limits.

Is it cleaner to keep all your rubbish in a single bin, or to separate it into recyclables, green trash, and general trash? Either way, the garbage is out.

Well, not a very good example since the garbage is out, uh, differently, but yeah. Technically I think its cleaner to have it all in a single bin, but one may prefer to recycle, n stuff, and whatnot.