RPG-style floating damage HP displays

Started by Kinoko, Wed 10/11/2004 04:02:11

Previous topic - Next topic

Kinoko

I'm trying to create a little text damage report that appears underneath said damaged character everytime it takes damage. Most people familiar with RPGs will get what I mean here...

Using this:
Code: ags

function DisplaySpeechBackgroundAt(int charid, string message, int x, int y) {
  int overlay_id = DisplaySpeechBackground(charid, message);
  MoveOverlay(overlay_id, x, y);
}


I've managed to get part of the way there, but there are still a couple of things I'd like to do and I'm not sure if they can be done. Say, I have character MOLERAT take damage. After calculating the damage into dmgreport, I'll use this line:

Code: ags

DisplaySpeechBackground (MOLERAT, dmgreport, character[MOLERAT].x, character[MOLERAT].y);


At the moment, this text appears over MOLERAT, and stays in that screen position even if MOLERAT moves elsewhere, until the text disappears. I'd like to be able to have that text sort of glued to MOLERAT so it's constantly checking MOLERAT's x and y positions, and ... being there.

Another thing I'd like to achieve (IF it's possible) is to have the text "fall" a little after it appears, which again means altering the y position of the overlay, WHILE still following the x and y of the character. I figure that if I can achieve the first thing, I can work out how to do the second.

Given that this is going to be a blanket code for all damage reports, I was thinking of creating a string or an int identifying the CHARID of the damaged character at the time of damage, and inserting that into all the required lines of code in the function. That way, I hope, the function will work seperately for multiple characters on screen at a time.

Can anyone offer any advice on this?

RickJ

You could try using CreateTextOverlay(), MoveOverlay(), and RemoveOverlay()  functions instead of the DisplaySpeech...() functions. 

Cheers

poc301

I am using the same type of thing for my RPG I am making.  Here is what I am doing :

Create a 2nd character (invisible).

Do the calculation for damage, and pass that through to a GlobalInt.  For example set GlobalInt(1) to the damage.

Whenever a character or enemy takes damage, have it do this :

character[4].x=character[0].x; //Makes sure DAMAGE character is where EGO is.  This line is in my RepeatedlyExecute in Global Script.

int damage;
damage=GetGlobalInt(1);

//Then run any calculations for how you calulate damage modifiers, armor, etc, etc, in here.

SetGlobalInt(1, damage);

Then use the Display and %d command to have the DAMAGE character talk the GlobalInt(1) value every time damage is made.

Kinoko

poc: Uh yeah, but the problem I have is exactly like the line where you say, "...and then you report the damage", if you know what I mean.  THAT'S what I need help with. Whether it's a DisplaySpeechBackground, an overlay, or another character saying it... the 'what' isn't the problem, it's the 'how'.

I -am- thinking about overlays but... that's my problem. I'm still trying to think of exactly how to do it.

Radiant

#4
Code: ags

int ovly[5], onum, otime[5], o_x[5], o_y[5];

function DamageChar (int charid, int damage) {
  string buffer;
  onum = (onum + 1) % 5;
  StrFormat (buffer, "%s hit for %d!", character[charid].name, damage);
  if (IsOverlayValid (ovly[onum])) RemoveOverlay (ovly[onum]);
  o_x[onum] = character[charid].x - 30;
  o_y[onum]= character[charid].y - 30;
  ovly[onum] = CreateTextOverlay (o_x[onum], o_y[onum], 60, 0, 15, buffer);
  otime[onum] = 80;
}

function repeatedly_execute () {
  int i;
  while (i < 5) {
    if (IsOverlayValid (ovly[i])) {
      otime[i] --;
       if (otime[i] == 0) RemoveOverlay (ovly[i]); else 
       if (otime[i] % 3 == 0) {
        o_y[i] --;
        MoveOverlay (ovly[i], o_x[i], o_y[i]);
      }
     }
   }
   i++;
}


Kinoko

Radiant: That 'while' code apparently just keeps going and going and causes my game to crash.

Radiant

Oops. Move the 'i++' up one line.

Kinoko

Well, now I remember why I was trying to do it the way I originally proposed. Your code seems to be working fine now, except that the reason I was using DisplaySpeechBackground. My game's usual speech is using Sieera style w/ background, so I need to use displaySpeechBackground to get the HP damage text as normal non-border text. Also, of course, that solves the problem of removal given that DisplaySpeechBackground now uses automatic removal.

So... back to square one, I guess. I should have been more specific, sorry.

So... I have DisplaySpeechBackground looking right, automatically being removed and with x and y positioning. I just need to be able to, while the text is on screen, keep it moving with the character it's attached to, and to, -if- possible, have it "drop" a few pixels once it appears, just for visual effect.

stuh505

Well DisplaySpeechBackground is still a graphic overlay so you can just store it into a variable when you make it, and then use the MoveOverlay to move that around however you want, following the character if you wish.  Normally, these games do not have the damage follow the character, but appear where the character is and then float up off the screen.

Kinoko

#9
Not the kind of games I'm thinking of, but thanks for the suggestions, I'll have a think about them ^_^

EDIT: Okay, had a think. I really need someone to tell me how to do this in detail. I'm not a scripting virtuoso and this is all still very confusing to me.

So, say I create a function like this:

Code: ags

function DamageChar (int charID) {
  int buffer;
  buffer = DisplaySpeechBackgroundAt (EGO, dmgreport, character[charID].x, character[charID].y);
  MoveOverlay (...);
  }


When a particular character is hit, the string "dmgreport" is created with the value inside. After that, say I have the line "DamageChar (MOLERAT);"

I know the code for moving the overlay needs to go in RepeatedlyExecute, using a system of a constantly increasing integer, and "When int = something, MoveOverlay (...)" etc

I don't know how to create a system where several different overlays can be on screen at once. starting and ending at slightly different times. Doesn't that "buffer" variable have to be generated for each character/hit differently? ie A different named variable each time?

I'm pretty lost here. Can someone please give me some specific help?


Kinoko

I'm gonna do something I hate and bump this. Seeing as there's absolutely no other currently running topic in this forum, I figure it won't be a problem, and I seem to have the only currently unsolved problem here.

Sorry everyone!

Gilbert

#11
Heh I didn't read this thread until now (I'm a lazy busy guy, so I don't normally read long threads, especially when they seemed to be answered ;D ). I'll try and see if I can help.

I'll first define a variable array for holding infos.
int damchar[10]; //array to hold character ids whose damage texts are being displayed, I'll set 10 slots here, so there're at most 10 such messages at once on screen, you can change it if you want
int damoverid[10]; //to hold the corresponding overlay ids of the texts

Then, when a character get hurt (just copied your code and changed it):
Code: ags

function DamageChar (int hurtchar) {
Ã,  int i=0; //running index, to check if there's a free slot for text
Ã,  while (i<10){
Ã,  Ã,  Ã, if (damchar[i]==0){ //if a slot is available
Ã,  Ã,  Ã,  Ã,  damchar[i]=hurtchar; //set it to "occupied"
Ã,  Ã,  Ã,  Ã,  damoverid[i] = DisplaySpeechBackground (hurtchar, dmgreport); //I'd checked the manual, seems there's no DisplaySpeechBackgroundAt(). Well I suppose you had set up dmgreport somewhere else already, right?
Ã,  Ã,  Ã,  Ã,  i=20; //break out of loop
Ã,  Ã,  Ã,  } else i++;
Ã,  Ã,  }Ã,  //end while
Ã,  }


Then, in repeatedly-execute, add:
Code: ags

int i=0; //checks all slots for whether the speechs had expired
while (i<10){
Ã,  if (damchar[i]){ //if there was a message for this character
Ã,  Ã,  if (IsOverlayValid(damoverid[i])) { //Is it still displayed?
Ã,  Ã,  Ã,  MoveOverlay(damoverid[i],character[damchar[i]].x-10,character[damchar[i]].y-10); //You may need to work out the correct formula for the position yourself
Ã,  Ã,  } else damchar[i]=0; //Expired, mark it as "free"
Ã,  }
Ã,  i++;
}




Also, note that there's a limit of number of simultaneousÃ,  overlays on screen (counting speech, text and graphical overlays, too lazy to check the exact number from the manual), so be careful that you wont use too many of this or else the game may crash.

Kinoko

Thaaank you, Gilbot ^_^ I'll give your code a go right now. You picked the right number with 10, I've set that as a monster per room limit myself so that's perfect.

My biggest problem I think is that the concept of 'array's is still quite a mystery to me.

While I'm gonna try your code, I'll just post what I'd come up with myself (which I guess would only work for one character so... not much good).

Code: ags

function DisplaySpeechBackgroundAt(int charid, string message, int x, int y) {
Ã,  int overlay_id = DisplaySpeechBackground(charid, message);
Ã,  MoveOverlay(overlay_id, x, y);
}

function DamageChar (int charid) {
Ã,  buffy = DisplaySpeechBackgroundAt (EGO, dmgreport, character[charid].x, character[charid].y);
Ã,  MoveOverlay (buffy, character[charid].x, character[charid].y);
Ã,  }


Then in repeatedly execute:
Code: ags

if (IsOverlayValid(buffy)) {
Ã,  it = 0;
Ã,  it++
Ã,  
Ã,  if (it == 5) MoveOverlay (buffy, character[charid].x, character[charid].y-1);
Ã,  else if (it == 10) MoveOverlay (buffy, character[charid].x, character[charid].y-1);
Ã,  else if (it == 15) MoveOverlay (buffy, character[charid].x, character[charid].y-2);
Ã,  else if (it == 20) MoveOverlay (buffy, character[charid].x, character[charid].y-4);
Ã,  else if (it == 25) MoveOverlay (buffy, character[charid].x, character[charid].y-6);
Ã,  else if (it == 30) MoveOverlay (buffy, character[charid].x, character[charid].y-9);
Ã,  else if (it == 35) MoveOverlay (buffy, character[charid].x, character[charid].y-12);
Ã,  else if (it == 40) MoveOverlay (buffy, character[charid]x, character[charid].y-16);
Ã,  else if (it == 45) MoveOverlay (buffy, character[charid].x, character[charid].y-20);
Ã,  else if (it == 50) MoveOverlay (buffy, character[charid].x, character[charid].y-21);
Ã,  else if (it == 55) MoveOverlay (buffy, character[charid].x, character[charid].y-18);
Ã,  else if (it == 60) MoveOverlay (buffy, character[charid].x, character[charid].y-17);
Ã,  else if (it == 65) MoveOverlay (buffy, character[charid].x, character[charid].y-16);
Ã,  else if (it == 70) MoveOverlay (buffy, character[charid].x, character[charid].y-18);
Ã,  else if (it == 75) MoveOverlay (buffy, character[charid].x, character[charid].y-19);
Ã,  else if (it == 80) MoveOverlay (buffy, character[charid].x, character[charid].y-20);
Ã,  else if (it == 85) MoveOverlay (buffy, character[charid].x, character[charid].y-21);
Ã,  }


Right now, I can't even text it because I keep getting an error relating to "buffy = DisplaySpeechBackgroundAt (EGO, dmgreport, character[charid].x, character[charid].y);", telling my "dmgreport" is wrong and I must use string functions. No matter what I put in that slot, whether I use "something", or something, or even a number... it's always the same error message.

EDIT: Okay, tried your code. It seems to work fine, as far as I can tell. The only problem I can see is that the damage appears over the character's head for a quick moment before appearing at it's feet.

Gilbert

Actually I'd mucked up a test game, seems that it worked okay. :)
You can try this:
http://www.2dadventure.com/ags/damgtext.zip
(This was made with V2.6SP1, I think it should work with newer versions too)

Just make a folder, extract the stuffs in it, load the game in AGSEDIT and compile it.

To test it, just click the non-main-character rogers with the hand icon (watch teh suckers go! :=).
Note that for testing every roger is just saying the same line of text, furthermore, when the overlays are moved, I just adopted the fake formula of x-10, y-10, which of course is not a good position (so the texts appear on the legs), you may need to take into account the size of characters, etc. to make the position correct.

Kinoko

Thanks for that. I can see now how it works with multiple characters ^_^ I'll assume for the moment until I get multiple characters working myself that it will display the different dmgreports for different characters...

Kinoko

Well, I've been away from here for a few weeks, and have just started work on the game again. Seeing as it's the same problem, I'll just resurect this old thread rather than start a new one about the same thing.

Anyway, I have this code working quite well, and have the HP text going in the general pattern I'd like it to. Basically, I have this function:

Code: ags

function DamageChar (int hurtchar) {
  sit=0;
  int it=0; //running index, to check if there's a free slot for text
  while (it<10){
     if (damchar[it]==0){ //if a slot is available
        damchar[it]=hurtchar; //set it to "occupied"
        damoverid[it] = DisplaySpeechBackground (hurtchar, dmgreport); //I'd checked the manual, seems there's no DisplaySpeechBackgroundAt(). Well I suppose you had set up dmgreport somewhere else already, right?
        it=20; //break out of loop
      } else it++;
    }  //end while
  }


With this in repeatedly execute:

Code: ags

int it=0; //checks all slots for whether the speechs had expired
while (it<10){
  if (damchar[it]){ //if there was a message for this character
    if (IsOverlayValid(damoverid[it])) { //Is it still displayed?
       sit++;
       if (sit == 1) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-30);
       else if (sit == 5) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-29); 
       else if (sit == 10) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-27); 
       else if (sit == 15) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-25); 
       else if (sit == 20) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-20); 
       else if (sit == 25) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-15); 
       else if (sit == 30) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-10);
       else if (sit == 35) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-5); 
       else if (sit == 40) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+3); 
       else if (sit == 45) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+12); 
       else if (sit == 50) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+20); 
       else if (sit == 55) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+15); 
       else if (sit == 60) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+13); 
       else if (sit == 65) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+14); 
       else if (sit == 70) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+17); 
       else if (sit == 75) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+20);
    } else damchar[it]=0; //Expired, mark it as "free"
  }
  it++;
}


This gives it the appearance of appearing high on the character, then dropping down to the ground with a little bounce. It's not perfect, but I can work on the timing. The only problem is that as soon as the text appears, it appears in the usual top-centred position above the character's head for just a nanosecond before moving where I put it. I'm not sure how to to get it to appear in the position I specify in the line "if (sit == 1) ..."

EDIT: Oh, and I had already tried this code:

Code: ags

       if (sit == 0) {
         MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-30);
         sit++;
       }
       else if (sit>0) {
         sit++;
         if (sit == 1) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-30);
         else if (sit == 5) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-29); 
         else if (sit == 10) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-27); 
         else if (sit == 15) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-25); 
         else if (sit == 20) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-20); 
         else if (sit == 25) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-15); 
         else if (sit == 30) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-10);
         else if (sit == 35) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-5); 
         else if (sit == 40) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+3); 
         else if (sit == 45) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+12); 
         else if (sit == 50) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+20); 
         else if (sit == 55) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+15); 
         else if (sit == 60) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+13); 
         else if (sit == 65) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+14); 
         else if (sit == 70) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+17); 
         else if (sit == 75) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+20);
       }


Only to get the same result. I still can't stop the text appearing in it's default place before being moved.

strazer

QuoteI still can't stop the text appearing in it's default place before being moved.

Why don't you put the first MoveOverlay line in the DamageChar function then?

Kinoko

You know, I do wish my brain would send me postcards from wherever it goes sometimes.

*sigh* This is why I need the tech forum, these little incredibly obvious things I just can't see.

THANKYOU Strazer ^_^

EDIT: Am I allowed to stick a thread about this in the Tech Archives? I think this kind of function would be helpful for a lot of people, and I have it tweaked really well now.

strazer

I have bookmarked this thread and will move it there once it has faded beyond the front page.

RickJ

Kinoko, the reason the "if (sit == 0)"  block is never executed is that when sit==1 it is incremented to a value of 2 before y ou check it.  If you move the "sit++" line to the end of the "if else" block, as shown below, it should work the way you expect.

Code: ags

   if (sit == 0) {
         MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-30);
         sit++;
   }
   else if (sit>0) {
         if (sit == 1) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-30);
         else if (sit == 5) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-29);
         else if (sit == 10) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-27);
         else if (sit == 15) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-25);
         else if (sit == 20) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-20);
         else if (sit == 25) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-15);         else if (it == 35) MoveOverlay(damoverid[it],character[damchar[it]].x-10,character[damchar[it]].y-10);
         else if (sit == 30) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-10);
         else if (sit == 35) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y-5);
         else if (sit == 40) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+3);
         else if (sit == 45) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+12);
         else if (sit == 50) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+20);
         else if (sit == 55) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+15);
         else if (sit == 60) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+13);
         else if (sit == 65) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+14);
         else if (sit == 70) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+17);
         else if (sit == 75) MoveOverlay(damoverid[it],character[damchar[it]].x,character[damchar[it]].y+20);
         sit++;
   }


SMF spam blocked by CleanTalk