Help: Using tile graphics to represent health value. How do I make it scalable, as opposed to hardcoding values?

I'm trying to make a Zelda-styled health system with hearts and half hearts representing the amount of health the player has.

My initial implementation looks like this:

image

on load do

// set starting health and max health
hearts = 6
heartsMax = 6
heartPieces = 0

end

on draw do

// Hearts UI
if hearts==6 then
	tell 0,0 to
		swap "heart_full"
	end
	tell 1,0 to
		swap "heart_full"
	end
	tell 2,0 to
		swap "heart_full"
	end
end

if hearts==5 then
	tell 0,0 to
		swap "heart_full"
      end
	tell 1,0 to
		swap "heart_full"
	end
	tell 2,0 to
		swap "heart_half"
	end
end

if hearts==4 then
	tell 0,0 to
		swap "heart_full"
	end
	tell 1,0 to
		swap "heart_full"
	end
	tell 2,0 to
		swap "heart_empty"
	end
end

if hearts==3 then
	tell 0,0 to
		swap "heart_full"
	end
	tell 1,0 to
		swap "heart_half"
	end
	tell 2,0 to
		swap "heart_empty"
	end
end

if hearts==2 then
	tell 0,0 to
		swap "heart_full"
	end
	tell 1,0 to
		swap "heart_empty"
	end
	tell 2,0 to
		swap "heart_empty"
	end
end

if hearts==1 then
	tell 0,0 to
		swap "heart_half"
	end
	tell 1,0 to
		swap "heart_empty"
	end
	tell 2,0 to
		swap "heart_empty"
	end
end
end

This is working as intended, and I can gain and lose health as expected.

My question then, is how would I make this scalable to allow for increasing the max health? Is it possible without hardcoding every swap for every possible value of health? I'm trying to avoid a game where the player could theoretically gain up to 20 hearts, which means I would need to hard code 40 (because there are hearts and half hearts) blocks of swaps for every possible value of health.

If hardcoding it is my only option then just knowing that is helpful, but I was hoping there was some math solution of using variable values in the swap coordinates to sort of dynamically draw the hearts depending on any health value the character might have.

Thanks!
DMCA Quest.zip (5.3 KB)

You could try using the label function with embed commands. If you set your font size to half width then you should be able to embed as many hearts as you need

do you mean editing the font to replace some of the symbols with hearts? I hadn't thought of using label

Oh you could do that, but I meant using the special embed syntax, like follows:

label "{embed:heartLeft}{embed:heartRight}" at 0,0

Where heartLeft and heartRight are tiles in your room. You just need to note that when using a half width font, only the left half of the tile will be printed

Alternatively if you don't want to use a half width font project-wide, you could just make a tile that is a whole heart, and another that is just the left half, and use that with embed, as the half heart will only ever be at the end of the string

I see. would there be a way to tell it how many hearts to embed based on the value of hearts variable?

Otherwise, I would still be creating a conditional label command for every variation of possible heart values, but that does still look to be less total code if I went that route.

You could use division and modulo to tell you both how many hearts as well as whether or not you need a half.

[Edit] updated link

1 Like

Well that's above my current knowledge as a "literally just started programming when Pulp released" newbie, but I will try to learn how to use modulo and implement this. Thanks!

Yeah I think you could do that like this:

hearts = 3

heartCounter = hearts
if hearts>0 then
  heartString = "{embed:heart}"
  heartCounter--
end

while heartCounter>0 do
  heartString = "{heartString}{embed:heart}"
end

if heartString!=0 then
  label "{heartString}" at 0,0
end

Note that this doesn't take into account the half hearts, but it should get you started!

On its own, my above post isn't very clear, so here's a working player script showing what I mean. The a button increases the heart halves and the b button decreases them.

hearts

on load do
	halves = 15
	heartString = ""
	call "updateHearts"
end

on cancel do
	if halves>0 then
		halves--
		call "updateHearts"
	end
end

on confirm do
	if halves<20 then
		halves++
		call "updateHearts"
	end
end

on updateHearts do
	log "halves: {halves}"
	
	hearts = halves
	x = hearts
	y = 2
	call "modulo"
	
	heartString = ""
	if mod!=0 then
		hearts--
	end
	
	hearts /= 2
	while hearts>0 do
		heartString = "{heartString}{embed:heart}"
		hearts--
	end
		
	if mod!= 0 then
		heartString = "{heartString}{embed:half}"
	end
end

on draw do
	if heartString!="" then
		label "{heartString}" at 0,0
	end
end

on modulo do
	mod = x
	mod /= y
	mod -= floor mod
	mod *= y
end
5 Likes

Wow, that looks great! I'll have to take some time to study this since it's currently above my skill level, but it definitely seems like it solves my problem so I'll mark it as the solution.

Thanks for the help and providing a working example!

2 Likes

modulo in this case might be overkill, since you're always only dividing by 2. You just need to compare the value of hearts after dividing by 2 to the floor of hearts.

heartString = ""
hearts = halves
hearts /= 2

half = hearts
hearts = floor hearts
half -= hearts

while hearts > 0 do
  heartString = "{heartString}{embed:heart}"
  hearts--
end
if half != 0 then
  heartString = "{heartString}{embed:half}"
end

edit: I realize this isn't much of a contribution to the answer, it might be just a little easier for a new programmer to follow.

2 Likes

True, modulo would be more useful for something like 4 pieces per heart or if you wanted to split the hearts onto multiple rows. :+1:

I've been using your example code in a separate project to try and tweak it to my needs and it's working! Based off your example I was also able to make it draw the "empty" hearts as well so that the player always knows what their maximum health is, and it's all scalable!

hearts_test

Now you've essentially solved my problem, which I am grateful for, but if you're able could you offer some more insight into one part of your code that I don't quite understand?

I actually think I grasp the whole modulo function now and how it's determining whether to draw a half or whole heart, but I still don't actually understand how the while loop is working?

while hearts>0 do
	heartString = "{heartString}{embed:heart}"
	hearts--
end

I understand that it's decrementing hearts each loop until it reaches 0, but I'm just not understanding the syntax of how embed works. How is it drawing multiple hearts? Is the value of heartString telling it how many heart tiles to embed? Is it embeding an extra tile every loop until it reaches 0?

I guess just an overhead view of the logic would help, but again, you've already solved my problem so thank you for that!

(Here's my modified project in case anyone wants to check it out)
hearts test.zip (2.7 KB)

4 Likes

That's awesome! Well done on extending it to support empty hearts!

Maybe simplifying the code a bit might help. Here's an example without the embedding which might make it clearer:

s = "It goes on"
num = 3
while num>0 do
  s = "{s} and on"
  num--
end

log "{s}" // "It goes on and on and on and on"

It's worth noting that in many other programming languages, "{s} and on" could just as easily be written as s + " and on". The idea being that the string s, in this case, has " and on" appended to it each iteration.

So, in our hearts code, each iteration of the while loop will append a new heart to the end of the current heartString variable (which starts empty) so that we end up with the number of hearts we are expecting. Also, as above, the heartString = "{heartString}{embed:heart}" code can be thought of just as equivalent to heartString = heartString + heartTile.

Regarding the embed syntax, it just replaces the {embed:heart} with a single heart tile at that point in the string.

2 Likes