Hello everyone.
I'm working on a fledgling game and I want implement a zelda-style life system where my main character has hearts that empty as he loses lives. I was also thinking that he might get into a scrape or too with other characters and with them then having hearts above their heads (as in not in a gui like the main character) that decrease as you take blows against them or what have you.
I have been scouring the forums and I found this handy little thing here: http://www.adventuregamestudio.co.uk/forums/index.php?topic=43954.msg584732#msg584732
But I can't really seem to get it working.
If anybody more experienced that me has any pointers then that would be great.
For the health bar, try looking at this: http://www.adventuregamestudio.co.uk/forums/index.php?topic=13649.220
You need
a) a global variable that stores health in some way. If the player can lose a 1/4 heart, I'd go with an integer variable that's set to number_of_hearts * 4 initially.
b) a global function that gets called whenever the player looses or gains hearts. It'll update the variable based on the change, then redraw the hearts. The easiest way is to create a non-clickable GUI with background and border color set to 0 (this makes it entirely transparent), then add a button to it.
The button's image is used to display the hearts; it is a DynamicSprite that is cleared, then gets hearts drawn to it after each change.
There are a few pitfalls involved, but we can only help you if you tell us the exact problem.
Made the variable and this little piece of literature for the gui:
GlobalScript.ash
import function set_heartgui();
this in the game start function in GlobalScript.asc
number_of_hearts = 4;
Then this for the gui:
function set_heartgui()
{
DynamicSprite *heart4 = DynamicSprite.CreateFromExistingSprite(103);
DynamicSprite *heart3 = DynamicSprite.CreateFromExistingSprite(104);
DynamicSprite *heart2 = DynamicSprite.CreateFromExistingSprite(105);
DynamicSprite *heart1 = DynamicSprite.CreateFromExistingSprite(106);
//DynamicSprite *heart1 = DynamicSprite.CreateFromExistingSprite(106.Graphic);
btnHearts.NormalGraphic = 102;
if (number_of_hearts == 4)
{
btnHearts.NormalGraphic = 102;
heart3.Delete();
heart2.Delete();
heart1.Delete();
heart4.Resize(40, 9);
btnHearts.NormalGraphic = heart4.Graphic;
Wait(100);
}
else if (number_of_hearts == 3)
{
btnHearts.NormalGraphic = 102;
heart4.Delete();
heart2.Delete();
heart1.Delete();
heart3.Resize(40, 9);
btnHearts.NormalGraphic = heart3.Graphic;
Wait(100);
}
else if (number_of_hearts == 2)
{
btnHearts.NormalGraphic = 102;
heart3.Delete();
heart4.Delete();
heart1.Delete();
heart2.Resize(40, 9);
btnHearts.NormalGraphic = heart2.Graphic;
Wait(100);
}
else if (number_of_hearts == 1)
{
btnHearts.NormalGraphic = 102;
heart3.Delete();
heart2.Delete();
heart4.Delete();
heart1.Resize(40, 9);
btnHearts.NormalGraphic = heart1.Graphic;
Wait(100);
heart1.Delete();
}
else if (number_of_hearts == 0)
{
btnHearts.NormalGraphic = 102;
//here is where you put the deathscreen and/or your gruesome death animation
}
}
Application is something like this:
Typical health potion (I'm using MI style):
function iHPpotion_Interact()
{
if (UsedAction(eGA_Drink))
player.LockView(22);
player.Animate(0, 7, eOnce, eBlock, eForwards);
player.UnlockView();
if (number_of_hearts == (4))
set_heartgui();
else
number_of_hearts += 1;
set_heartgui();
}
Damage taken for touching lava:
function hlava_Interact()
{
player.Say("Ouch! motherfuck!");
number_of_hearts -= 1;
set_heartgui();
}
I'm not really sure if I'm using the button's "NormalGraphic" definition correctly. Also, this is only a health bar for the player character right now. Furthermore the graphic vanishes after set_heartgui which would be cool if I could correct, because I would like health to be shown at the top of the screen (like in dangerous areas) without that happening, is there anyway to make the dynamicsprite graphic stick around?
As for health bar's for the enemies, I was thinking something along the lines of displaying their health bar's above their heads within in the character sprite and then just use maybe 4 different set's of views for each enemy with each sprite having one heart drained etc. I haven't got to that yet so i'm going to stop here before I get ahead of myself.
If you declare the DynamicSprite within the function, it is deleted at the end of the function. You need to declare it outside and above any function that uses it in order for it and its image to be retained.
I'm not sure how your sprites look and why you're resizing them, the good thing is that you don't need all that.
A button's image can be clipped (set its ClipImage property to true in the editor); therefore, putting a sprite with four hearts on the button and resizing the button when hearts are gained/lost is enough.
Plus, you can pass the change as function parameter and return the new value:
function UpdateHeartGUI(int change) {
number_of_hearts += change;
if (number_of_hearts < 0) number_of_hearts = 0;
if (number_of_hearts > 4) number_of_hearts = 4;
btnHearts.Width = number_of_hearts * 10;
return number_of_hearts;
}
Now you can simply call
// player gains two hearts
UpdateHeartsGUI(2);
// player loses one heart
if (UpdateHeartsGUI(-1) == 0) Die();
You only need a DynamicSprite if simply resizing the button isn't enough. In that case, you'd draw the hearts onto the DynamicSprite's DrawingSurface to update the button's image.
As for putting hearts above enemies, using four different views for each character is precisely what not to do. It gets the job done, but is the most inconvenient way to do it by a long shot.
There are multiple options:
- using one DynamicSprite for each frame in the view, the hearts are drawn to the original sprites dynamically during the game
- similar to the player's hearts bar, each player gets their own GUI and button
- only one GUI with multiple buttons is used
- not GUI buttons but Overlays are used
The best way to do it depends on the maximum number of enemies on screen at once and whether enemy hearts are supposed to disappear behind walkbehinds/objects along with the characters and possibly other factors as well.
Copied the UpdateHeartGui function and it returned this:
Error: Function declaration has wrong number of arguments to prototype.
However it looks very dynamic and is probably exactly what i'm looking for.
What I did do was uploading 4 different sprites, each with for hearts, so the code now looks like this:
function ChangeofHeartGUI()
{
if (number_of_hearts == 4)
{
btnHearts.NormalGraphic = 103;
}
else if (number_of_hearts == 3)
{
btnHearts.NormalGraphic = 104;
}
else if (number_of_hearts == 2)
{
btnHearts.NormalGraphic = 105;
}
else if (number_of_hearts == 1)
{
btnHearts.NormalGraphic = 106;
}
else if (number_of_hearts == 0)
{
btnHearts.NormalGraphic = 0;
//die
}
}
The import line has to reflect the declaration exactly:
// GlobalScript.ash
import function UpdateHeartGUI(int change);
How are your sprites different it they all show four hearts...?
I'm using four different images, each with, one, two, three & four hearts,respectively. Then I made them blink each time the player loses or gains a life, with the waiting doubling for animation and as a minor penalty.
function UpdateHeartGUI()
{
if (number_of_hearts == 4)
{
btnHearts.NormalGraphic = 103;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 103;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 103;
}
else if (number_of_hearts == 3)
{
btnHearts.NormalGraphic = 104;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 104;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 104;
}
else if (number_of_hearts == 2)
{
btnHearts.NormalGraphic = 105;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 105;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 105;
}
else if (number_of_hearts == 1)
{
btnHearts.NormalGraphic = 106;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 106;
Wait(10);
btnHearts.NormalGraphic = 0;
Wait(10);
btnHearts.NormalGraphic = 106;
}
else if (number_of_hearts == 0)
{
btnHearts.NormalGraphic = 0;
//die
}
}
I did however use your suggestion to make a very nice mana bar, so:
function UpdateMana(int change)
{
Manabar += change;
if (Manabar < 0) Manabar = 0;
if (Manabar > 12) Manabar = 12;
btnMana.Width = Manabar * 3;
return Manabar;
}
Your first piece of code represents a classic example of how not to code.
Take a look at this:
function UpdateHeartGUI()
{
btnHearts.NormalGraphic = 107 - number_of_hearts;
int i;
while (i < 2) {
Wait(10);
btnHearts.Visible = false;
Wait(10);
btnHearts.Visible = true;
i++;
}
}
This requires a single change should you want to use more than 4 hearts, and again a single change, should you want to make it blink longer.
Also note that again, you have to always change number_of_hearts, then call UpdateHeartsGUI(), when you could've easily kept the way I suggested to do it.