Copy to Clipboard Test

Boss Health Bar Code

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;
			}
		}
	}
}