Copy to Clipboard Test

Link & Enemy Damage and Healing Numbers Code

int DamageNumbers[65536];

const int NPCM_DAMAGENUMBERSLASTHP = 12; //npc->Misc[] index tracking the enemy's last HP

//Tile and CSet for the first of 8 tiles for enemy damage numbers
const int GFX_DAMAGENUMBERS_ENEMY = 4680;
const int CS_DAMAGENUMBERS_ENEMY = 7;

//Tile and CSet for the first of 8 tiles for enemy healing numbers
const int GFX_DAMAGENUMBERS_ENEMYHEAL = 4720;
const int CS_DAMAGENUMBERS_ENEMYHEAL = 5;

//Tile and CSet for the first of 8 tiles for Link damage numbers
const int GFX_DAMAGENUMBERS_LINK = 4700;
const int CS_DAMAGENUMBERS_LINK = 8;

//Tile and CSet for the first of 8 tiles for Link healing numbers
const int GFX_DAMAGENUMBERS_LINKHEAL = 4720;
const int CS_DAMAGENUMBERS_LINKHEAL = 5;

//Width and height of damage number tiles in pixels
const int DAMAGENUMBERS_WIDTH = 4;
const int DAMAGENUMBERS_HEIGHT = 7;

//Multiplier and number of decimal places shown for enemies
const int DAMAGENUMBERS_ENEMY_MULTIPLIER = 1;
const int DAMAGENUMBERS_ENEMY_DECIMAL_PLACES = -1;

//Multiplier and number of decimal places shown for Link
const int DAMAGENUMBERS_LINK_MULTIPLIER = 1;
const int DAMAGENUMBERS_LINK_DECIMAL_PLACES = -1;

const int DAMAGENUMBERS_MAX = 32; //The max number of damage numbers drawn to the screen at once
const int DAMAGENUMBERS_FRAMES = 40; //How many frames the damage numbers last for
const int DAMAGENUMBERS_SPAWNFRAMES = 24; //How many frames the damage numbers to appear (0 for instant)
const int DAMAGENUMBERS_XOFF = 0; //X offset for the spawning point of the numbers
const int DAMAGENUMBERS_YOFF = -8; //Y offset for the spawning point of the numbers
const int DAMAGENUMBERS_YSPEED = -0.1; //The speed the number moves on the Y-axis (negative = upwards)
const int DAMAGENUMBERS_BOUNCEHEIGHT = 6; //How high in pixels the numbers bounce on the Y axis
const int DAMAGENUMBERS_LINKDAMAGECOOLDOWN = 16; //How often in frames the script can detect Link taking damage
const int DAMAGENUMBERS_LINKHEALCOOLDOWN = 16; //How often in frames the script can detect Link healing HP
const int DAMAGENUMBERS_ALLOW_LARGE_NEGATIVE = 0; //If 1, enemies with <-1000 HP can still draw damage numbers. This is usually used by scripts to kill enemies without making a death sound

//Internal constants used by the script, don't change
const int _DNUM_STARTINDEX = 16;
const int _DNUM_INDICES = 5;

const int _DNUM_LASTLINKHP = 0;
const int _DNUM_LINKDAMAGECOOLDOWN = 1;
const int _DNUM_LINKHEALCOOLDOWN = 2;
const int _DNUM_LASTDMAP = 3;
const int _DNUM_LASTSCREEN = 4;

const int _DNUMI_X = 0;
const int _DNUMI_Y = 1;
const int _DNUMI_TYPE = 2;
const int _DNUMI_DAMAGE = 3;
const int _DNUMI_TIMER = 4;

const int _DNUM_TYPE_ENEMYDAMAGE = 0;
const int _DNUM_TYPE_ENEMYHEAL = 1;
const int _DNUM_TYPE_LINKDAMAGE = 2;
const int _DNUM_TYPE_LINKHEAL = 3;

//Init function, clears the DamageNumbers[] array on every load
void DamageNumbers_Init(){
	int size = SizeOfArray(DamageNumbers);
	for(int i=0; i<size; i++){
		DamageNumbers[i] = 0;
	}
	
	DamageNumbers[_DNUM_LASTLINKHP] = Link->HP;
	DamageNumbers[_DNUM_LASTDMAP] = Game->GetCurDMap();
	DamageNumbers[_DNUM_LASTSCREEN] = Game->GetCurScreen();
}

//Update function for enemies. This can be combined with your own if you have one
void DamageNumbers_UpdateEnemies(){
	for(int i=Screen->NumNPCs(); i>0; i--){
		npc n = Screen->LoadNPC(i);
		DamageNumbers_UpdateEnemyDamage(n);
	}
}

//Update function for individual enemies
void DamageNumbers_UpdateEnemyDamage(npc n){
	//Detect when the enemy's HP has changed
	if(n->HP!=n->Misc[NPCM_DAMAGENUMBERSLASTHP]){
		//Ignore enemies that don't have conventional HP
		if(DamageNumbers_IgnoreEnemyType(n))
			return;
		
		//NPCs with -1000 HP have usually been killed by scripts
		if(n->HP>-1000||DAMAGENUMBERS_ALLOW_LARGE_NEGATIVE){
			//If it's less make a damage number
			if(n->HP<n->Misc[NPCM_DAMAGENUMBERSLASTHP]){
				if(GFX_DAMAGENUMBERS_ENEMY!=0){
					DamageNumbers_AddNumberGFX(CenterX(n), CenterY(n)-DAMAGENUMBERS_HEIGHT/2, _DNUM_TYPE_ENEMYDAMAGE, Abs(n->HP-n->Misc[NPCM_DAMAGENUMBERSLASTHP]));
				}
			}
			//else if it wasn't 0 (just spawned in) make a healing number
			else if(n->Misc[NPCM_DAMAGENUMBERSLASTHP]){
				if(GFX_DAMAGENUMBERS_ENEMYHEAL!=0){
					DamageNumbers_AddNumberGFX(CenterX(n), CenterY(n)-DAMAGENUMBERS_HEIGHT/2, _DNUM_TYPE_ENEMYHEAL, Abs(n->HP-n->Misc[NPCM_DAMAGENUMBERSLASTHP]));
				}
			}
		}
			
		//Update the change in HP
		n->Misc[NPCM_DAMAGENUMBERSLASTHP] = n->HP;
	}
}

//Function for finding enemies that should never show damage numbers
bool DamageNumbers_IgnoreEnemyType(npc n){
	bool invalidTypes[256];
	
	invalidTypes[NPCT_GUY] = true;
	invalidTypes[NPCT_ROCK] = true;
	invalidTypes[NPCT_TRAP] = true;
	invalidTypes[NPCT_PROJECTILE] = true;
	invalidTypes[NPCT_NONE] = true;
	invalidTypes[NPCT_FAIRY] = true;
	
	return invalidTypes[n->Type];
}

//Update function for Link
void DamageNumbers_UpdateLink(){
	//Cooldown timer for Link taking damage (for things that damage him every frame)
	if(DamageNumbers[_DNUM_LINKDAMAGECOOLDOWN]>0)
		DamageNumbers[_DNUM_LINKDAMAGECOOLDOWN]--;
	//Cooldown timer for Link healing (for things that heal him every frame)
	if(DamageNumbers[_DNUM_LINKHEALCOOLDOWN]>0)
		DamageNumbers[_DNUM_LINKHEALCOOLDOWN]--;
	
	//Whenever Link's HP changes
	if(Link->HP!=DamageNumbers[_DNUM_LASTLINKHP]){
		//If he took damage and isn't on cooldown
		if(Link->HP<DamageNumbers[_DNUM_LASTLINKHP]){
			if(DamageNumbers[_DNUM_LINKDAMAGECOOLDOWN]<=0){
				if(GFX_DAMAGENUMBERS_LINK!=0)
					DamageNumbers_AddNumberGFX(CenterLinkX(), CenterLinkY()-DAMAGENUMBERS_HEIGHT/2, _DNUM_TYPE_LINKDAMAGE, Abs(Link->HP-DamageNumbers[_DNUM_LASTLINKHP]));
				
				DamageNumbers[_DNUM_LINKDAMAGECOOLDOWN] = DAMAGENUMBERS_LINKDAMAGECOOLDOWN;
			
				DamageNumbers[_DNUM_LASTLINKHP] = Link->HP;
			}
		}
		//otherwise if he healed and isn't on cooldown
		else{
			if(DamageNumbers[_DNUM_LINKHEALCOOLDOWN]<=0){
				if(GFX_DAMAGENUMBERS_LINKHEAL!=0)
					DamageNumbers_AddNumberGFX(CenterLinkX(), CenterLinkY()-DAMAGENUMBERS_HEIGHT/2, _DNUM_TYPE_LINKHEAL, Abs(Link->HP-DamageNumbers[_DNUM_LASTLINKHP]));
				
				DamageNumbers[_DNUM_LINKHEALCOOLDOWN] = DAMAGENUMBERS_LINKHEALCOOLDOWN;
			
				DamageNumbers[_DNUM_LASTLINKHP] = Link->HP;
			}
		}
	}
}

//Update function for scripted damage number draws
void DamageNumbers_UpdateNumberGFX(){
	int j;
	
	//If the screen has changed, reset all damage numbers
	if(Game->GetCurDMap()!=DamageNumbers[_DNUM_LASTDMAP]||Game->GetCurScreen()!=DamageNumbers[_DNUM_LASTSCREEN]){
		DamageNumbers_Init();
	}
	
	//Cycle through all number indices
	for(int i=0; i<DAMAGENUMBERS_MAX; i++){
		j = _DNUM_STARTINDEX+_DNUM_INDICES*i;
		//If >0, is valid
		if(DamageNumbers[j+_DNUMI_TIMER]>0){
			int type = DamageNumbers[j+_DNUMI_TYPE];
			
			int damage = DamageNumbers[j+_DNUMI_DAMAGE];
			
			//Draw all numbers based on type
			if(type==_DNUM_TYPE_ENEMYDAMAGE){
				if(DAMAGENUMBERS_ENEMY_MULTIPLIER>0)
					damage *= DAMAGENUMBERS_ENEMY_MULTIPLIER;
				DamageNumbers_Draw(DamageNumbers[j+_DNUMI_X]+DAMAGENUMBERS_XOFF, DamageNumbers[j+_DNUMI_Y]+DAMAGENUMBERS_YOFF, GFX_DAMAGENUMBERS_ENEMY, CS_DAMAGENUMBERS_ENEMY, damage, DamageNumbers[j+_DNUMI_TIMER], DAMAGENUMBERS_ENEMY_DECIMAL_PLACES);
			}
			else if(type==_DNUM_TYPE_ENEMYHEAL){
				if(DAMAGENUMBERS_ENEMY_MULTIPLIER>0)
					damage *= DAMAGENUMBERS_ENEMY_MULTIPLIER;
				DamageNumbers_Draw(DamageNumbers[j+_DNUMI_X]+DAMAGENUMBERS_XOFF, DamageNumbers[j+_DNUMI_Y]+DAMAGENUMBERS_YOFF, GFX_DAMAGENUMBERS_ENEMYHEAL, CS_DAMAGENUMBERS_ENEMYHEAL, damage, DamageNumbers[j+_DNUMI_TIMER], DAMAGENUMBERS_ENEMY_DECIMAL_PLACES);
			}
			else if(type==_DNUM_TYPE_LINKDAMAGE){
				if(DAMAGENUMBERS_LINK_MULTIPLIER>0)
					damage *= DAMAGENUMBERS_LINK_MULTIPLIER;
				DamageNumbers_Draw(DamageNumbers[j+_DNUMI_X]+DAMAGENUMBERS_XOFF, DamageNumbers[j+_DNUMI_Y]+DAMAGENUMBERS_YOFF, GFX_DAMAGENUMBERS_LINK, CS_DAMAGENUMBERS_LINK, damage, DamageNumbers[j+_DNUMI_TIMER], DAMAGENUMBERS_LINK_DECIMAL_PLACES);
			}
			else if(type==_DNUM_TYPE_LINKHEAL){
				if(DAMAGENUMBERS_LINK_MULTIPLIER>0)
					damage *= DAMAGENUMBERS_LINK_MULTIPLIER;
				DamageNumbers_Draw(DamageNumbers[j+_DNUMI_X]+DAMAGENUMBERS_XOFF, DamageNumbers[j+_DNUMI_Y]+DAMAGENUMBERS_YOFF, GFX_DAMAGENUMBERS_LINKHEAL, CS_DAMAGENUMBERS_LINKHEAL, damage, DamageNumbers[j+_DNUMI_TIMER], DAMAGENUMBERS_LINK_DECIMAL_PLACES);
			}
			
			DamageNumbers[j+_DNUMI_Y] += DAMAGENUMBERS_YSPEED;
			DamageNumbers[j+_DNUMI_TIMER]--;
		}
	}
}

//Function to add scripted damage number draws
void DamageNumbers_AddNumberGFX(int x, int y, int type, int damage){
	int j;
	//Cycle through all number indices
	for(int i=0; i<DAMAGENUMBERS_MAX; i++){
		j = _DNUM_STARTINDEX+_DNUM_INDICES*i;
		//If <= 0, is invalid
		if(DamageNumbers[j+_DNUMI_TIMER]<=0){
			DamageNumbers[j+_DNUMI_X] = x;
			DamageNumbers[j+_DNUMI_Y] = y;
			DamageNumbers[j+_DNUMI_TYPE] = type;
			DamageNumbers[j+_DNUMI_DAMAGE] = damage;
			DamageNumbers[j+_DNUMI_TIMER] = DAMAGENUMBERS_FRAMES;
			return;
		}
	}
}

//Function to draw scripted damage number draws for one frame
void DamageNumbers_Draw(int x, int y, int gfx, int cs, int num, int frame, int numDecimalPlaces){
	int i; int j; int k;
	
	//Frames normally count down, but this function calculates stuff as if they're counting up
	frame = DAMAGENUMBERS_FRAMES-frame;
	
	num = Clamp(num, 0, 99999.9999);
	int drawX = Floor(x-DAMAGENUMBERS_WIDTH/2);
	int drawY = Floor(y-DAMAGENUMBERS_HEIGHT/2);
	int digitsWh[5];
	
	//Get each digit of the number
	digitsWh[0] = Floor(num)%10;
	digitsWh[1] = Floor(num/10)%10;
	digitsWh[2] = Floor(num/100)%10;
	digitsWh[3] = Floor(num/1000)%10;
	digitsWh[4] = Floor(num/10000)%10;
	
	int digitsDc[5] = {-1, -1, -1, -1, -1};
	
	//If decimal places should be drawn
	if(numDecimalPlaces!=0){
		digitsDc[0] = 10; //Add the decimal point character
		
		//Get the digits of each decimal place
		i = (num-Floor(num))*10000;
		digitsDc[4] = Floor(i)%10;
		digitsDc[3] = Floor(i/10)%10;
		digitsDc[2] = Floor(i/100)%10;
		digitsDc[1] = Floor(i/1000)%10;
		
		//If there's a fixed number of decimal places, clear the unused ones
		if(numDecimalPlaces>0){
			for(i=4; i>numDecimalPlaces; i--)
				digitsDc[i] = -1;
		}
		//Else (-1) clear the unused ones
		else{
			for(i=1; digitsDc[i]==0&&i<=4; i++)
				digitsDc[i] = -1;
			//If all decimal places are empty, the number is whole. Remove the decimal point
			if(digitsDc[1]==-1)
				digitsDc[0] = -1;
		}
	}
	
	//Find how many whole number places should be visible
	int placesWh = 1;
	if(num>=10000)
		placesWh = 5;
	else if(num>=1000)
		placesWh = 4;
	else if(num>=100)
		placesWh = 3;
	else if(num>=10)
		placesWh = 2;
	
	int digits[10];
	int numDigits;
	
	//Put the whole and decimal digits into one array
	for(i=4; i>=0; i--){
		if(digitsDc[i]>0){
			digits[numDigits] = digitsDc[i];
			numDigits++;
		}
	}
	for(i=0; i<placesWh; i++){
		digits[numDigits] = digitsWh[i];
		numDigits++;
	}
	
	//Center the starting position of the number
	//(this gets added instead of subtracted because the numbers are drawn right to left)
	drawX += Floor(-DAMAGENUMBERS_WIDTH/2+(numDigits-1)*DAMAGENUMBERS_WIDTH/2);
	
	int tmpX; int tmpY;
	
	int spawnFreq;
	if(DAMAGENUMBERS_SPAWNFRAMES==0)
		spawnFreq = 0;
	else 
		spawnFreq = (360/DAMAGENUMBERS_SPAWNFRAMES);
	int spawnFreq2 = (180/numDigits);
	
	//Draw each digit in sequence
	for(i=0; i<numDigits; i++){
		k = frame*spawnFreq-i*spawnFreq2;
		j = Abs(Sin(Clamp(k, 0, 180)));
		tmpX = drawX-i*DAMAGENUMBERS_WIDTH;
		tmpY = drawY-DAMAGENUMBERS_BOUNCEHEIGHT*j;
		//Check if each digit should be drawn
		//When k>0, the Abs(Sin(k)) bounce animation has started. k is clamped so it only plays once.
		//Otherwise if spawnFreq is 0, all digits appear instantly
		if(k>=0||spawnFreq==0){
			if(gfx>0)
				Screen->FastTile(6, tmpX, tmpY, gfx+digits[i], cs, 128);
			else
				Screen->FastCombo(6, tmpX, tmpY, Abs(gfx)+digits[i], cs, 128);
		}
	}
}

global script DamageNumbers_ExampleGlobal{
	void run(){
		DamageNumbers_Init();
		while(true){
			DamageNumbers_UpdateEnemies();
			DamageNumbers_UpdateLink();
			DamageNumbers_UpdateNumberGFX();
			
			Waitdraw();
			Waitframe();
		}
	}
}