const int HEALTHBAR_DRAW_DAMAGE = 1; //Set to 1 if you want to draw damage numbers
const int HEALTHBAR_DAMAGE_COUNT = 96; //How many frames damage numbers last for
const int HEALTHBAR_DRAW_CHIP = 1; //Set to 1 if you want to draw the health draining away when hit
const int HEALTHBAR_CHIP_RATE = 16; //How fast the damage drains from the bar after a hit
const int HEALTHBAR_ENDDELAY = 40; //How many frames the health bar lasts for after all enemies are dead when disappearOnDeath is set
//Health bar dimensions
const int HEALTHBAR_X = 8;
const int HEALTHBAR_Y = 16;
const int HEALTHBAR_WIDTH = 240;
const int HEALTHBAR_HEIGHT = 4;
//Offsets for the font of the NPC's name
const int HEALTHBAR_FONT_X_OFFSET = 0;
const int HEALTHBAR_FONT_Y_OFFSET = -6;
//Y offset for duplicate health bars
const int HEALTHBAR_DUPE_OFFSET = 12;
const int FONT_HEALTHBAR_TITLE = 2; //Font for the title and damage numbers. See FONT_ in std_constants.zh
const int C_HEALTHBAR_FONT = 0x01; //Color of the health bar's font
const int C_HEALTHBAR_FONTBG = 0x0F; //Color of the health bar's font background
const int C_HEALTHBAR_OUTLINE = 0x0F; //Color of the health bar's outline
const int C_HEALTHBAR_BAR = 0x81; //Color of the health bar
const int C_HEALTHBAR_DRAIN = 0x82; //Color of the section of the health bar being removed
const int C_HEALTHBAR_BG = 0x00; //Color of the health bar's background
int HealthBar_GetHP(npc n){
//Swap the commented lines if you're not using ghost
return GetEnemyProperty(n, ENPROP_HP);
// return n->HP;
}
ffc script HealthBar_Single{
void run(int npcid, int str, int npcNumber, int disappearOnDeath){
Waitframes(4);
npc n = HealthBar_GetNPC(npcid, npcNumber);
if(!n->isValid()) //If an enemy isn't found, quit out
Quit();
int hp = HealthBar_GetHP(n);
int maxHP = HealthBar_GetHP(n);
int lastHP = HealthBar_GetHP(n);
int drainHP = HealthBar_GetHP(n);
int lastDrainHP = HealthBar_GetHP(n);
int damage;
int damageCounter;
//If there's more than one enemy with health bars on the screen, offset them by the
int ffcCount;
for(int i=1; i<=32; i++){
ffc f = Screen->LoadFFC(i);
if(f->Script==this->Script){
if(f==this)
break;
else
ffcCount++;
}
}
int nameString[256];
if(str>0){ //Get nameString from the string editor
Game->GetMessage(str, nameString);
}
else{ //Get nameString from the enemy editor
n->GetName(nameString);
}
HealthBar_CapString(nameString);
this->InitD[0] = 0; //Mark the FFC as "Alive"
//Begin main loop that runs until HP drains to 0
while(drainHP>0){
if(n->isValid()) //If the enemy isn't there, assume it's dead
hp = Max(HealthBar_GetHP(n), 0);
else
hp = 0;
if(damageCounter>0)
damageCounter--;
//Keep track of when the enemy takes damage
if(hp!=lastHP){
if(hp<lastHP){
if(damageCounter>0)
damage += lastHP-hp;
else
damage = lastHP-hp;
damageCounter = HEALTHBAR_DAMAGE_COUNT;
}
lastDrainHP = lastHP;
lastHP = hp;
}
//Decrease drainHP towards the current HP
if(drainHP!=hp){
if(drainHP>hp){
drainHP = Max(drainHP-(Abs(lastDrainHP-hp)/HEALTHBAR_CHIP_RATE), hp);
}
else
drainHP = hp;
}
HealthBar_Draw(nameString, hp, maxHP, drainHP, damage, damageCounter, HEALTHBAR_DUPE_OFFSET*ffcCount);
Waitframe();
}
this->InitD[0] = 1; //Mark the FFC as "Dead"
while(true){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, HEALTHBAR_DUPE_OFFSET*ffcCount);
//If marked to disappear, wait until all health bars are "Dead" before removing
if(disappearOnDeath){
if(HealthBar_CheckDone(this)){
break;
}
}
Waitframe();
}
//Wait extra frames before quitting out so it doesn't look as abrupt
for(int i=0; i<HEALTHBAR_ENDDELAY; i++){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, HEALTHBAR_DUPE_OFFSET*ffcCount);
Waitframe();
}
}
//Returns true if all FFCs with this script are "Dead"
bool HealthBar_CheckDone(ffc this){
for(int i=1; i<=32; i++){
ffc f = Screen->LoadFFC(i);
if(f->Script==this->Script){
if(f->InitD[0]==0)
return false;
}
}
return true;
}
//Returns the nth enemy with a certain ID on the screen
npc HealthBar_GetNPC(int id, int extra){
for(int i=Screen->NumNPCs(); i>=1; i--){
npc n = Screen->LoadNPC(i);
if(n->ID==id){
if(extra<=1)
return n;
else
extra--;
}
}
npc nullnpc;
return nullnpc;
}
//Draws a string with an outline
void HealthBar_DrawString(int layer, int x, int y, int font, int c1, int c2, int format, int str){
Screen->DrawString(layer, x, y-1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y+1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x-1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x+1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y, font, c1, -1, format, str, 128);
}
//Draws damage numbers as a string
void HealthBar_DrawDamage(int layer, int x, int y, int font, int c1, int c2, int number){
int istr[8];
int i;
//Add every digit to the string from largest to smallest
if(number>=10000){
istr[i] = '0'+(Floor(number/10000)%10);
i++;
}
if(number>=1000){
istr[i] = '0'+(Floor(number/1000)%10);
i++;
}
if(number>=100){
istr[i] = '0'+(Floor(number/100)%10);
i++;
}
if(number>=10){
istr[i] = '0'+(Floor(number/10)%10);
i++;
}
istr[i] = '0'+(number%10);
i++;
istr[i] = 0;
HealthBar_DrawString(layer, x, y, font, c1, c2, TF_RIGHT, istr);
}
//Draws the entire health bar
void HealthBar_Draw(int str, int HP, int maxHP, int drainHP, int damage, int damageCounter, int offset){
int x1 = HEALTHBAR_X;
int y1 = HEALTHBAR_Y+offset;
int x2 = HEALTHBAR_X+HEALTHBAR_WIDTH-1;
int y2 = HEALTHBAR_Y+HEALTHBAR_HEIGHT-1+offset;
int hpLength = (x2-x1-2)*(HP/maxHP);
int drainLength = (x2-x1-2)*(drainHP/maxHP);
//Draws the main body of the health bar
Screen->Rectangle(6, x1, y1, x2, y2, C_HEALTHBAR_BG, 1, 0, 0, 0, true, 128);
if(drainHP>0&&HEALTHBAR_DRAW_CHIP)
Screen->Rectangle(6, x1+1, y1+1, x1+1+Clamp(drainLength, 0, x2-x1-2), y2-1, C_HEALTHBAR_DRAIN, 1, 0, 0, 0, true, 128);
if(HP>0)
Screen->Rectangle(6, x1+1, y1+1, x1+1+Clamp(hpLength, 0, x2-x1-2), y2-1, C_HEALTHBAR_BAR, 1, 0, 0, 0, true, 128);
Screen->Rectangle(6, x1, y1, x2, y2, C_HEALTHBAR_OUTLINE, 1, 0, 0, 0, false, 128);
//Draw the string
if(str>0){
HealthBar_DrawString(6, x1+HEALTHBAR_FONT_X_OFFSET, y1+HEALTHBAR_FONT_Y_OFFSET, FONT_HEALTHBAR_TITLE, C_HEALTHBAR_FONT, C_HEALTHBAR_FONTBG, TF_NORMAL, str);
}
//Draw the damage
if(HEALTHBAR_DRAW_DAMAGE){
if(damageCounter>0){
HealthBar_DrawDamage(6, x2-HEALTHBAR_FONT_X_OFFSET, y1+HEALTHBAR_FONT_Y_OFFSET, FONT_HEALTHBAR_TITLE, C_HEALTHBAR_FONT, C_HEALTHBAR_FONTBG, damage);
}
}
}
//Remove trailing spaces from a string
void HealthBar_CapString(int str){
for(int i=SizeOfArray(str)-1; i>=0; i--){
if(str[i]>32){
str[i+1] = 0;
return;
}
}
}
}
ffc script HealthBar_Group{
void run(int npcid1, int npcid2, int npcid3, int npcid4, int npcid5, int npcid6, int str, int disappearOnDeath){
Waitframes(4);
if(HealthBar_GetHPTotal(npcid1, npcid2, npcid3, npcid4, npcid5, npcid6)==0) //If none of the enemies are found, quit out
Quit();
int hp = HealthBar_GetHPTotal(npcid1, npcid2, npcid3, npcid4, npcid5, npcid6);
int maxHP = hp;
int lastHP = hp;
int drainHP = hp;
int lastDrainHP = hp;
int damage;
int damageCounter;
int nameString[256];
if(str>0){ //Get nameString from the string editor
Game->GetMessage(str, nameString);
}
HealthBar_CapString(nameString);
//Begin main loop that runs until HP drains to 0
while(drainHP>0){
hp = HealthBar_GetHPTotal(npcid1, npcid2, npcid3, npcid4, npcid5, npcid6);
//There's really no clean way I can think of to handle enemies that summon more of themselves
//so in this case we'll count this as healing and increase maxHP when necessary
if(maxHP<hp)
maxHP = hp;
if(damageCounter>0)
damageCounter--;
//Keep track of when the enemy takes damage
if(hp!=lastHP){
if(hp<lastHP){
if(damageCounter>0)
damage += lastHP-hp;
else
damage = lastHP-hp;
damageCounter = HEALTHBAR_DAMAGE_COUNT;
}
lastDrainHP = lastHP;
lastHP = hp;
}
//Decrease drainHP towards the current HP
if(drainHP!=hp){
if(drainHP>hp){
drainHP = Max(drainHP-(Abs(lastDrainHP-hp)/HEALTHBAR_CHIP_RATE), hp);
}
else
drainHP = hp;
}
HealthBar_Draw(nameString, hp, maxHP, drainHP, damage, damageCounter, 0);
Waitframe();
}
//If the enemy isn't set to disappear, keep running the empty health bar forever
if(!disappearOnDeath){
while(true){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, 0);
Waitframe();
}
}
//Wait extra frames before quitting out so it doesn't look as abrupt
for(int i=0; i<HEALTHBAR_ENDDELAY; i++){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, 0);
Waitframe();
}
}
//Returns the combined HP of up to 6 types of NPCs on the screen for all instances
int HealthBar_GetHPTotal(int npcid1, int npcid2, int npcid3, int npcid4, int npcid5, int npcid6){
int total;
for(int i=Screen->NumNPCs(); i>=1; i--){
npc n = Screen->LoadNPC(i);
if(n->ID==npcid1)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid2)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid3)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid4)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid5)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid6)
total += Max(HealthBar_GetHP(n), 0);
}
return total;
}
//Draws a string with an outline
void HealthBar_DrawString(int layer, int x, int y, int font, int c1, int c2, int format, int str){
Screen->DrawString(layer, x, y-1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y+1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x-1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x+1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y, font, c1, -1, format, str, 128);
}
//Draws damage numbers as a string
void HealthBar_DrawDamage(int layer, int x, int y, int font, int c1, int c2, int number){
int istr[8];
int i;
//Add every digit to the string from largest to smallest
if(number>=10000){
istr[i] = '0'+(Floor(number/10000)%10);
i++;
}
if(number>=1000){
istr[i] = '0'+(Floor(number/1000)%10);
i++;
}
if(number>=100){
istr[i] = '0'+(Floor(number/100)%10);
i++;
}
if(number>=10){
istr[i] = '0'+(Floor(number/10)%10);
i++;
}
istr[i] = '0'+(number%10);
i++;
istr[i] = 0;
HealthBar_DrawString(layer, x, y, font, c1, c2, TF_RIGHT, istr);
}
//Draws the entire health bar
void HealthBar_Draw(int str, int HP, int maxHP, int drainHP, int damage, int damageCounter, int offset){
int x1 = HEALTHBAR_X;
int y1 = HEALTHBAR_Y+offset;
int x2 = HEALTHBAR_X+HEALTHBAR_WIDTH-1;
int y2 = HEALTHBAR_Y+HEALTHBAR_HEIGHT-1+offset;
int hpLength = (x2-x1-2)*(HP/maxHP);
int drainLength = (x2-x1-2)*(drainHP/maxHP);
//Draws the main body of the health bar
Screen->Rectangle(6, x1, y1, x2, y2, C_HEALTHBAR_BG, 1, 0, 0, 0, true, 128);
if(drainHP>0&&HEALTHBAR_DRAW_CHIP)
Screen->Rectangle(6, x1+1, y1+1, x1+1+Clamp(drainLength, 0, x2-x1-2), y2-1, C_HEALTHBAR_DRAIN, 1, 0, 0, 0, true, 128);
if(HP>0)
Screen->Rectangle(6, x1+1, y1+1, x1+1+Clamp(hpLength, 0, x2-x1-2), y2-1, C_HEALTHBAR_BAR, 1, 0, 0, 0, true, 128);
Screen->Rectangle(6, x1, y1, x2, y2, C_HEALTHBAR_OUTLINE, 1, 0, 0, 0, false, 128);
//Draw the string
if(str>0){
HealthBar_DrawString(6, x1+HEALTHBAR_FONT_X_OFFSET, y1+HEALTHBAR_FONT_Y_OFFSET, FONT_HEALTHBAR_TITLE, C_HEALTHBAR_FONT, C_HEALTHBAR_FONTBG, TF_NORMAL, str);
}
//Draw the damage
if(HEALTHBAR_DRAW_DAMAGE){
if(damageCounter>0){
HealthBar_DrawDamage(6, x2-HEALTHBAR_FONT_X_OFFSET, y1+HEALTHBAR_FONT_Y_OFFSET, FONT_HEALTHBAR_TITLE, C_HEALTHBAR_FONT, C_HEALTHBAR_FONTBG, damage);
}
}
}
//Remove trailing spaces from a string
void HealthBar_CapString(int str){
for(int i=SizeOfArray(str)-1; i>=0; i--){
if(str[i]>32){
str[i+1] = 0;
return;
}
}
}
}
const int HEALTHBAR_TILED_UNIQUEFIRSTLAST = 0; //Set to 1 if you want to use unique first and last blocks
const int HEALTHBAR_TILED_DRAWCAPS = 1; //Set to 1 if you want to use cap tiles (two states, drawn on either end)
const int HEALTHBAR_TILED_VERTICAL = 0; //Set to 1 if you want the health bar to be vertical
//X and Y position of the tiled health bar
const int HEALTHBAR_TILED_X = 16;
const int HEALTHBAR_TILED_Y = 16;
const int HEALTHBAR_TILED_NUM_BLOCKS = 14; //How many tiles make up the health bar
const int HEALTHBAR_TILED_SPACING = 16; //How many pixels the tiles are spaced apart
const int HEALTHBAR_TILED_STATES = 17; //How many states health bar tiles have, from full to empty
//Offsets for the font of the NPC's name
const int HEALTHBAR_TILED_FONT_X_OFFSET = 0;
const int HEALTHBAR_TILED_FONT_Y_OFFSET = -6;
//Y offset for duplicate health bars
const int HEALTHBAR_TILED_DUPE_OFFSET = 20;
const int FONT_HEALTHBAR_TILED_TITLE = 14; //Font for the title and damage numbers. See FONT_ in std_constants.zh
const int C_HEALTHBAR_TILED_FONT = 0x01; //Color of the health bar's font
const int C_HEALTHBAR_TILED_FONTBG = 0x0F; //Color of the health bar's font background
const int CS_HEALTHBAR_TILED = 5; //CSet of the tiled health bar
//Tiles for the main health bar
const int TIL_HEALTHBAR_TILED_MAIN = 52040; //First of the tiles for the main blocks of the health bar
const int TIL_HEALTHBAR_TILED_FIRST = 52020; //First of the tiles for the first block
const int TIL_HEALTHBAR_TILED_LAST = 52060; //First of the tiles for the last block
const int TIL_HEALTHBAR_TILED_FIRSTCAP = 52000; //First of two tiles for the starting cap
const int TIL_HEALTHBAR_TILED_LASTCAP = 52002; //First of two tiles for the end cap
//Tiles for the draining health bar
const int TIL_HEALTHBAR_TILED_DRAIN_MAIN = 52100; //First of the tiles for the main blocks of the health bar
const int TIL_HEALTHBAR_TILED_DRAIN_FIRST = 52080; //First of the tiles for the first block
const int TIL_HEALTHBAR_TILED_DRAIN_LAST = 52120; //First of the tiles for the last block
const int TIL_HEALTHBAR_TILED_DRAIN_FIRSTCAP = 52004; //First of two tiles for the starting cap
const int TIL_HEALTHBAR_TILED_DRAIN_LASTCAP = 52006; //First of two tiles for the end cap
ffc script HealthBar_Tiled_Single{
void run(int npcid, int str, int npcNumber, int disappearOnDeath){
Waitframes(4);
npc n = HealthBar_GetNPC(npcid, npcNumber);
if(!n->isValid()) //If an enemy isn't found, quit out
Quit();
int hp = HealthBar_GetHP(n);
int maxHP = HealthBar_GetHP(n);
int lastHP = HealthBar_GetHP(n);
int drainHP = HealthBar_GetHP(n);
int lastDrainHP = HealthBar_GetHP(n);
int damage;
int damageCounter;
//If there's more than one enemy with health bars on the screen, offset them by the
int ffcCount;
for(int i=1; i<=32; i++){
ffc f = Screen->LoadFFC(i);
if(f->Script==this->Script){
if(f==this)
break;
else
ffcCount++;
}
}
int nameString[256];
if(str>0){ //Get nameString from the string editor
Game->GetMessage(str, nameString);
}
else{ //Get nameString from the enemy editor
n->GetName(nameString);
}
HealthBar_CapString(nameString);
this->InitD[0] = 0; //Mark the FFC as "Alive"
//Begin main loop that runs until HP drains to 0
while(drainHP>0){
if(n->isValid()) //If the enemy isn't there, assume it's dead
hp = Max(HealthBar_GetHP(n), 0);
else
hp = 0;
if(damageCounter>0)
damageCounter--;
//Keep track of when the enemy takes damage
if(hp!=lastHP){
if(hp<lastHP){
if(damageCounter>0)
damage += lastHP-hp;
else
damage = lastHP-hp;
damageCounter = HEALTHBAR_DAMAGE_COUNT;
}
lastDrainHP = lastHP;
lastHP = hp;
}
//Decrease drainHP towards the current HP
if(drainHP!=hp){
if(drainHP>hp){
drainHP = Max(drainHP-(Abs(lastDrainHP-hp)/HEALTHBAR_CHIP_RATE), hp);
}
else
drainHP = hp;
}
HealthBar_Draw(nameString, hp, maxHP, drainHP, damage, damageCounter, HEALTHBAR_TILED_DUPE_OFFSET*ffcCount);
Waitframe();
}
this->InitD[0] = 1; //Mark the FFC as "Dead"
while(true){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, HEALTHBAR_TILED_DUPE_OFFSET*ffcCount);
//If marked to disappear, wait until all health bars are "Dead" before removing
if(disappearOnDeath){
if(HealthBar_CheckDone(this)){
break;
}
}
Waitframe();
}
//Wait extra frames before quitting out so it doesn't look as abrupt
for(int i=0; i<HEALTHBAR_ENDDELAY; i++){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, HEALTHBAR_TILED_DUPE_OFFSET*ffcCount);
Waitframe();
}
}
//Returns true if all FFCs with this script are "Dead"
bool HealthBar_CheckDone(ffc this){
for(int i=1; i<=32; i++){
ffc f = Screen->LoadFFC(i);
if(f->Script==this->Script){
if(f->InitD[0]==0)
return false;
}
}
return true;
}
//Returns the nth enemy with a certain ID on the screen
npc HealthBar_GetNPC(int id, int extra){
for(int i=Screen->NumNPCs(); i>=1; i--){
npc n = Screen->LoadNPC(i);
if(n->ID==id){
if(extra<=1)
return n;
else
extra--;
}
}
npc nullnpc;
return nullnpc;
}
//Draws a string with an outline
void HealthBar_DrawString(int layer, int x, int y, int font, int c1, int c2, int format, int str){
Screen->DrawString(layer, x, y-1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y+1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x-1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x+1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y, font, c1, -1, format, str, 128);
}
//Draws damage numbers as a string
void HealthBar_DrawDamage(int layer, int x, int y, int font, int c1, int c2, int number){
int istr[8];
int i;
//Add every digit to the string from largest to smallest
if(number>=10000){
istr[i] = '0'+(Floor(number/10000)%10);
i++;
}
if(number>=1000){
istr[i] = '0'+(Floor(number/1000)%10);
i++;
}
if(number>=100){
istr[i] = '0'+(Floor(number/100)%10);
i++;
}
if(number>=10){
istr[i] = '0'+(Floor(number/10)%10);
i++;
}
istr[i] = '0'+(number%10);
i++;
istr[i] = 0;
HealthBar_DrawString(layer, x, y, font, c1, c2, TF_RIGHT, istr);
}
//Draws a tiled health bar
void HealthBar_DrawTiledHealthBar(int layer, int startX, int startY, int cset, int tilStart, int tilMain, int tilEnd, int tilStartCap, int tilEndCap, int HP, int maxHP){
int x; int y; int til;
int blockMaxHP = maxHP/HEALTHBAR_TILED_NUM_BLOCKS; //The total HP per tile in the health bar
int currentBlock = Clamp(Floor(HP/blockMaxHP), 0, HEALTHBAR_TILED_NUM_BLOCKS); //Which tile of the health bar the enemy's HP falls under
int blockHP = HP%blockMaxHP; //The HP of the current block
int blockTil = Clamp(((blockMaxHP-blockHP)/blockMaxHP)*HEALTHBAR_TILED_STATES, 0, HEALTHBAR_TILED_STATES-1); //The tile offset for the current block based on its HP
//Prevent the last tile from appearing as "empty" if the enemy is close to dead
if(blockTil==HEALTHBAR_TILED_STATES-1&&HP>0&& currentBlock==0)
blockTil = HEALTHBAR_TILED_STATES-2;
//Cycle through all the blocks
for(int i=0; i<HEALTHBAR_TILED_NUM_BLOCKS; i++){
x = startX;
y = startY;
if(HEALTHBAR_TILED_VERTICAL)
y += HEALTHBAR_TILED_SPACING*i;
else
x += HEALTHBAR_TILED_SPACING*i;
til = tilMain;
//Change the base tile if unique start/end tiles are being used
if(HEALTHBAR_TILED_UNIQUEFIRSTLAST){
if(i==0)
til = tilStart;
else if(i==HEALTHBAR_TILED_NUM_BLOCKS-1)
til = tilEnd;
}
//Draw different states based on relation to the "current HP" block
if(i<currentBlock)
Screen->FastTile(layer, x, y, til, cset, 128);
else if(i==currentBlock)
Screen->FastTile(layer, x, y, til+blockTil, cset, 128);
else
Screen->FastTile(layer, x, y, til+HEALTHBAR_TILED_STATES-1, cset, 128);
}
//If caps are enabled, draw those
if(HEALTHBAR_TILED_DRAWCAPS){
if(HEALTHBAR_TILED_VERTICAL){
if(HP>0) //Caps have two states, depending on whether HP is empty/full and which side of the bar the cap is on
Screen->FastTile(layer, startX, startY-HEALTHBAR_TILED_SPACING, tilStartCap, cset, 128);
else
Screen->FastTile(layer, startX, startY-HEALTHBAR_TILED_SPACING, tilStartCap+1, cset, 128);
if(HP<maxHP)
Screen->FastTile(layer, startX, startY+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, tilEndCap+1, cset, 128);
else
Screen->FastTile(layer, startX, startY+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, tilEndCap, cset, 128);
}
else{
if(HP>0)
Screen->FastTile(layer, startX-HEALTHBAR_TILED_SPACING, startY, tilStartCap, cset, 128);
else
Screen->FastTile(layer, startX-HEALTHBAR_TILED_SPACING, startY, tilStartCap+1, cset, 128);
if(HP<maxHP)
Screen->FastTile(layer, startX+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, startY, tilEndCap+1, cset, 128);
else
Screen->FastTile(layer, startX+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, startY, tilEndCap, cset, 128);
}
}
}
//Draws the entire health bar
void HealthBar_Draw(int str, int HP, int maxHP, int drainHP, int damage, int damageCounter, int offset){
int x1 = HEALTHBAR_TILED_X;
int y1 = HEALTHBAR_TILED_Y;
int x2 = x1+HEALTHBAR_TILED_SPACING*HEALTHBAR_TILED_NUM_BLOCKS-1;
//Draws the main body of the health bar
if(HEALTHBAR_DRAW_CHIP)
HealthBar_DrawTiledHealthBar(6, x1, y1+offset, CS_HEALTHBAR_TILED, TIL_HEALTHBAR_TILED_DRAIN_FIRST, TIL_HEALTHBAR_TILED_DRAIN_MAIN, TIL_HEALTHBAR_TILED_DRAIN_LAST, TIL_HEALTHBAR_TILED_DRAIN_FIRSTCAP, TIL_HEALTHBAR_TILED_DRAIN_LASTCAP, drainHP, maxHP);
HealthBar_DrawTiledHealthBar(6, x1, y1+offset, CS_HEALTHBAR_TILED, TIL_HEALTHBAR_TILED_FIRST, TIL_HEALTHBAR_TILED_MAIN, TIL_HEALTHBAR_TILED_LAST, TIL_HEALTHBAR_TILED_FIRSTCAP, TIL_HEALTHBAR_TILED_LASTCAP, HP, maxHP);
if(!HEALTHBAR_TILED_VERTICAL){
//Draw the string
if(str>0){
HealthBar_DrawString(6, x1+HEALTHBAR_TILED_FONT_X_OFFSET, y1+HEALTHBAR_TILED_FONT_Y_OFFSET+offset, FONT_HEALTHBAR_TILED_TITLE, C_HEALTHBAR_TILED_FONT, C_HEALTHBAR_TILED_FONTBG, TF_NORMAL, str);
}
//Draw the damage
if(HEALTHBAR_DRAW_DAMAGE){
if(damageCounter>0){
HealthBar_DrawDamage(6, x2-HEALTHBAR_TILED_FONT_X_OFFSET, y1+HEALTHBAR_TILED_FONT_Y_OFFSET+offset, FONT_HEALTHBAR_TILED_TITLE, C_HEALTHBAR_TILED_FONT, C_HEALTHBAR_TILED_FONTBG, damage);
}
}
}
}
//Remove trailing spaces from a string
void HealthBar_CapString(int str){
for(int i=SizeOfArray(str)-1; i>=0; i--){
if(str[i]>32){
str[i+1] = 0;
return;
}
}
}
}
ffc script HealthBar_Tiled_Group{
void run(int npcid1, int npcid2, int npcid3, int npcid4, int npcid5, int npcid6, int str, int disappearOnDeath){
Waitframes(4);
if(HealthBar_GetHPTotal(npcid1, npcid2, npcid3, npcid4, npcid5, npcid6)==0) //If none of the enemies are found, quit out
Quit();
int hp = HealthBar_GetHPTotal(npcid1, npcid2, npcid3, npcid4, npcid5, npcid6);
int maxHP = hp;
int lastHP = hp;
int drainHP = hp;
int lastDrainHP = hp;
int damage;
int damageCounter;
int nameString[256];
if(str>0){ //Get nameString from the string editor
Game->GetMessage(str, nameString);
}
HealthBar_CapString(nameString);
//Begin main loop that runs until HP drains to 0
while(drainHP>0){
hp = HealthBar_GetHPTotal(npcid1, npcid2, npcid3, npcid4, npcid5, npcid6);
//There's really no clean way I can think of to handle enemies that summon more of themselves
//so in this case we'll count this as healing and increase maxHP when necessary
if(maxHP<hp)
maxHP = hp;
if(damageCounter>0)
damageCounter--;
//Keep track of when the enemy takes damage
if(hp!=lastHP){
if(hp<lastHP){
if(damageCounter>0)
damage += lastHP-hp;
else
damage = lastHP-hp;
damageCounter = HEALTHBAR_DAMAGE_COUNT;
}
lastDrainHP = lastHP;
lastHP = hp;
}
//Decrease drainHP towards the current HP
if(drainHP!=hp){
if(drainHP>hp){
drainHP = Max(drainHP-(Abs(lastDrainHP-hp)/HEALTHBAR_CHIP_RATE), hp);
}
else
drainHP = hp;
}
HealthBar_Draw(nameString, hp, maxHP, drainHP, damage, damageCounter, 0);
Waitframe();
}
if(!disappearOnDeath){
while(true){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, 0);
Waitframe();
}
}
//Wait extra frames before quitting out so it doesn't look as abrupt
for(int i=0; i<HEALTHBAR_ENDDELAY; i++){
if(damageCounter>0)
damageCounter--;
HealthBar_Draw(nameString, 0, maxHP, 0, damage, damageCounter, 0);
Waitframe();
}
}
//Returns the combined HP of up to 6 types of NPCs on the screen for all instances
int HealthBar_GetHPTotal(int npcid1, int npcid2, int npcid3, int npcid4, int npcid5, int npcid6){
int total;
for(int i=Screen->NumNPCs(); i>=1; i--){
npc n = Screen->LoadNPC(i);
if(n->ID==npcid1)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid2)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid3)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid4)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid5)
total += Max(HealthBar_GetHP(n), 0);
if(n->ID==npcid6)
total += Max(HealthBar_GetHP(n), 0);
}
return total;
}
//Draws a string with an outline
void HealthBar_DrawString(int layer, int x, int y, int font, int c1, int c2, int format, int str){
Screen->DrawString(layer, x, y-1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y+1, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x-1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x+1, y, font, c2, -1, format, str, 128);
Screen->DrawString(layer, x, y, font, c1, -1, format, str, 128);
}
//Draws damage numbers as a string
void HealthBar_DrawDamage(int layer, int x, int y, int font, int c1, int c2, int number){
int istr[8];
int i;
//Add every digit to the string from largest to smallest
if(number>=10000){
istr[i] = '0'+(Floor(number/10000)%10);
i++;
}
if(number>=1000){
istr[i] = '0'+(Floor(number/1000)%10);
i++;
}
if(number>=100){
istr[i] = '0'+(Floor(number/100)%10);
i++;
}
if(number>=10){
istr[i] = '0'+(Floor(number/10)%10);
i++;
}
istr[i] = '0'+(number%10);
i++;
istr[i] = 0;
HealthBar_DrawString(layer, x, y, font, c1, c2, TF_RIGHT, istr);
}
//Draws a tiled health bar
void HealthBar_DrawTiledHealthBar(int layer, int startX, int startY, int cset, int tilStart, int tilMain, int tilEnd, int tilStartCap, int tilEndCap, int HP, int maxHP){
int x; int y; int til;
int blockMaxHP = maxHP/HEALTHBAR_TILED_NUM_BLOCKS; //The total HP per tile in the health bar
int currentBlock = Clamp(Floor(HP/blockMaxHP), 0, HEALTHBAR_TILED_NUM_BLOCKS); //Which tile of the health bar the enemy's HP falls under
int blockHP = HP%blockMaxHP; //The HP of the current block
int blockTil = Clamp(((blockMaxHP-blockHP)/blockMaxHP)*HEALTHBAR_TILED_STATES, 0, HEALTHBAR_TILED_STATES-1); //The tile offset for the current block based on its HP
//Prevent the last tile from appearing as "empty" if the enemy is close to dead
if(blockTil==HEALTHBAR_TILED_STATES-1&&HP>0&& currentBlock==0)
blockTil = HEALTHBAR_TILED_STATES-2;
//Cycle through all the blocks
for(int i=0; i<HEALTHBAR_TILED_NUM_BLOCKS; i++){
x = startX;
y = startY;
if(HEALTHBAR_TILED_VERTICAL)
y += HEALTHBAR_TILED_SPACING*i;
else
x += HEALTHBAR_TILED_SPACING*i;
til = tilMain;
//Change the base tile if unique start/end tiles are being used
if(HEALTHBAR_TILED_UNIQUEFIRSTLAST){
if(i==0)
til = tilStart;
else if(i==HEALTHBAR_TILED_NUM_BLOCKS-1)
til = tilEnd;
}
//Draw different states based on relation to the "current HP" block
if(i<currentBlock)
Screen->FastTile(layer, x, y, til, cset, 128);
else if(i==currentBlock)
Screen->FastTile(layer, x, y, til+blockTil, cset, 128);
else
Screen->FastTile(layer, x, y, til+HEALTHBAR_TILED_STATES-1, cset, 128);
}
//If caps are enabled, draw those
if(HEALTHBAR_TILED_DRAWCAPS){
if(HEALTHBAR_TILED_VERTICAL){
if(HP>0) //Caps have two states, depending on whether HP is empty/full and which side of the bar the cap is on
Screen->FastTile(layer, startX, startY-HEALTHBAR_TILED_SPACING, tilStartCap, cset, 128);
else
Screen->FastTile(layer, startX, startY-HEALTHBAR_TILED_SPACING, tilStartCap+1, cset, 128);
if(HP<maxHP)
Screen->FastTile(layer, startX, startY+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, tilEndCap+1, cset, 128);
else
Screen->FastTile(layer, startX, startY+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, tilEndCap, cset, 128);
}
else{
if(HP>0)
Screen->FastTile(layer, startX-HEALTHBAR_TILED_SPACING, startY, tilStartCap, cset, 128);
else
Screen->FastTile(layer, startX-HEALTHBAR_TILED_SPACING, startY, tilStartCap+1, cset, 128);
if(HP<maxHP)
Screen->FastTile(layer, startX+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, startY, tilEndCap+1, cset, 128);
else
Screen->FastTile(layer, startX+HEALTHBAR_TILED_NUM_BLOCKS*HEALTHBAR_TILED_SPACING, startY, tilEndCap, cset, 128);
}
}
}
//Draws the entire health bar
void HealthBar_Draw(int str, int HP, int maxHP, int drainHP, int damage, int damageCounter, int offset){
int x1 = HEALTHBAR_TILED_X;
int y1 = HEALTHBAR_TILED_Y;
int x2 = x1+HEALTHBAR_TILED_SPACING*HEALTHBAR_TILED_NUM_BLOCKS-1;
//Draws the main body of the health bar
if(HEALTHBAR_DRAW_CHIP)
HealthBar_DrawTiledHealthBar(6, x1, y1+offset, CS_HEALTHBAR_TILED, TIL_HEALTHBAR_TILED_DRAIN_FIRST, TIL_HEALTHBAR_TILED_DRAIN_MAIN, TIL_HEALTHBAR_TILED_DRAIN_LAST, TIL_HEALTHBAR_TILED_DRAIN_FIRSTCAP, TIL_HEALTHBAR_TILED_DRAIN_LASTCAP, drainHP, maxHP);
HealthBar_DrawTiledHealthBar(6, x1, y1+offset, CS_HEALTHBAR_TILED, TIL_HEALTHBAR_TILED_FIRST, TIL_HEALTHBAR_TILED_MAIN, TIL_HEALTHBAR_TILED_LAST, TIL_HEALTHBAR_TILED_FIRSTCAP, TIL_HEALTHBAR_TILED_LASTCAP, HP, maxHP);
if(!HEALTHBAR_TILED_VERTICAL){
//Draw the string
if(str>0){
HealthBar_DrawString(6, x1+HEALTHBAR_TILED_FONT_X_OFFSET, y1+HEALTHBAR_TILED_FONT_Y_OFFSET+offset, FONT_HEALTHBAR_TILED_TITLE, C_HEALTHBAR_TILED_FONT, C_HEALTHBAR_TILED_FONTBG, TF_NORMAL, str);
}
//Draw the damage
if(HEALTHBAR_DRAW_DAMAGE){
if(damageCounter>0){
HealthBar_DrawDamage(6, x2-HEALTHBAR_TILED_FONT_X_OFFSET, y1+HEALTHBAR_TILED_FONT_Y_OFFSET+offset, FONT_HEALTHBAR_TILED_TITLE, C_HEALTHBAR_TILED_FONT, C_HEALTHBAR_TILED_FONTBG, damage);
}
}
}
}
//Remove trailing spaces from a string
void HealthBar_CapString(int str){
for(int i=SizeOfArray(str)-1; i>=0; i--){
if(str[i]>32){
str[i+1] = 0;
return;
}
}
}
}