Copy to Clipboard Test

Scripted Damage Combos Code

const int SCRIPTEDDAMAGECOMBO_ONLYFACING = 1; //If 1, scripted damage combos will only do damage when Link is facing them
const int SCRIPTEDDAMAGECOMBO_DAMAGEONHEADBONK = 1; //If 1, Link takes damage instantly when jumping into a spiked ceiling

const int DELAY_SCRIPTEDDAMAGECOMBO = 8; //Default frame delay before taking damage
const int DELAY_SCRIPTEDDAMAGECOMBO_SIDEVIEW = 8; //Frame delay for sideview screens
const int EW_SCRIPTEDDAMAGECOMBO = 40; //EWeapon used for dealing damage. Script 10 by default

//Collision points for spike hitboxes
//This tells the script which points to check offset from 
//Link's hitbox for various collision checks. Unless you're using 
//large Link these shouldn't need to be changed
const int SDC_SOLID_HITBOX_UD_LEFT = 7;
const int SDC_SOLID_HITBOX_UD_RIGHT = 8;
const int SDC_SOLID_HITBOX_UD_TOP = 8;
const int SDC_SOLID_HITBOX_LR_TOP = 11;
const int SDC_SOLID_HITBOX_LR_BOTTOM = 12;

const int SDC_FALLING_HITBOX_LEFT = 4;
const int SDC_FALLING_HITBOX_RIGHT = 11;

const int SDC_CEILING_HITBOX_LEFT = 4;
const int SDC_CEILING_HITBOX_RIGHT = 11;

const int SDC_NONSOLID_HITBOX_LEFT = 4;
const int SDC_NONSOLID_HITBOX_RIGHT = 11;
const int SDC_NONSOLID_HITBOX_TOP = 11;
const int SDC_NONSOLID_HITBOX_BOTTOM = 12;

const int SDC_NONSOLID_HITBOX_SIDEVIEW_LEFT = 4;
const int SDC_NONSOLID_HITBOX_SIDEVIEW_RIGHT = 11;
const int SDC_NONSOLID_HITBOX_SIDEVIEW_TOP = 4;
const int SDC_NONSOLID_HITBOX_SIDEVIEW_BOTTOM = 11;

//Change this function is you have custom rings with different damage divisors in your quest
int ScriptedDamageCombo_GetTunicMultipliers(){
	//Return the divisor of the highest level ring
	if(Link->Item[I_RING3])
		return 8;
	else if(Link->Item[I_RING2])
		return 4;
	else if(Link->Item[I_RING1])
		return 2;
	//Otherwise no ring and damage is normal
	return 1;
}

//D0: The combo flag to check for scripted damage combos. If 0, the FFC itself is a hitbox
//D1: How much base damage the spikes deal. 4 points = 1 heart
//D2: If 1, the damage pierces rings
//D3: If >0 set the delay before spikes deal damage when holding up against them
//D4: If 1, the combos are treated as non solid damage combos
//D5: If 1, Link takes no knockback when hit
ffc script ScriptedDamageCombo{
	void run(int flag, int damage, int pierce, int damageDelay, int notSolid, int noKnockback){
		if(damageDelay==0){
			damageDelay = DELAY_SCRIPTEDDAMAGECOMBO;
			if(IsSideview())
				damageDelay = DELAY_SCRIPTEDDAMAGECOMBO_SIDEVIEW;
		}
		
		int timer;
		
		int xyInput[2];
		while(true){
			//Combo placed on the screen
			if(flag>0){
				int collchk = ScriptedDamageCombo_DetectLinkCollision(flag, Link->X, Link->Y, Link->Dir, notSolid, xyInput);
				if(collchk){
					if(notSolid){
						ScriptedDamageCombo_DamageLink(damage, pierce, noKnockback);
					}
					else{
						if(timer<damageDelay&&collchk!=2)
							timer++;
						else{
							ScriptedDamageCombo_DamageLink(damage, pierce, noKnockback);
						}
					}
				}
				else
					timer = 0;
			}
			//Combo is the FFC
			else{
				eweapon e = FireEWeapon(EW_SCRIPTEDDAMAGECOMBO, this->X, this->Y, 0, 0, damage, 0, 0, EWF_UNBLOCKABLE);
				e->DrawYOffset = -1000;
				e->HitWidth = this->EffectWidth;
				e->HitHeight = this->EffectHeight;
				SetEWeaponLifespan(e, EWL_TIMER, 0);
				SetEWeaponDeathEffect(e, EWD_VANISH, 0);
			}
			Waitframe();
		}
	}
	void ScriptedDamageCombo_DamageLink(int damage, bool pierce, bool noKnockback){
		ScriptedDamageCombo_DamageLink(damage, pierce, noKnockback, Link->Dir);
	}
	void ScriptedDamageCombo_DamageLink(int damage, bool pierce, bool noKnockback, int dir){
		int x = Link->X;
		int y = Link->Y;
		if(dir==DIR_UP)
			y -= 4;
		else if(dir==DIR_DOWN)
			y += 4;
		else if(dir==DIR_LEFT)
			x -= 4;
		else if(dir==DIR_RIGHT)
			x += 4;
		
		if(pierce)
			damage *= ScriptedDamageCombo_GetTunicMultipliers();
		
		eweapon e = FireEWeapon(EW_SCRIPTEDDAMAGECOMBO, x, y, 0, 0, damage, 0, 0, EWF_UNBLOCKABLE);
		e->DrawYOffset = -1000;
		SetEWeaponLifespan(e, EWL_TIMER, 0);
		SetEWeaponDeathEffect(e, EWD_VANISH, 0);
		if(noKnockback)
			Link->HitDir = -1;
	}
	void ScriptedDamageCombo_UpdateInput(int xyInput){
		if(xyInput[1]==0){ //If no Y axis pressed
			if(Link->PressUp&&Link->PressDown) //Default to up when buttons pressed simultaneously
				xyInput[1] = -1;
			else if(Link->PressUp||Link->InputUp) //Set axis based on which button what pressed
				xyInput[1] = -1;
			else if(Link->PressDown||Link->InputDown)
				xyInput[1] = 1;
		}
		else{ //If Y axis pressed
			if(!Link->InputUp&&!Link->InputDown) //Release Y axis if neither button pressed
				xyInput[1] = 0;
			else if(xyInput[1]==-1&&!Link->InputUp) //Reverse Y axis if opposite direction held and button released
				xyInput[1] = 1;
			else if(xyInput[1]==1&&!Link->InputDown)
				xyInput[1] = -1;
		}
		
		if(xyInput[0]==0){ //If no X axis pressed
			if(Link->PressLeft&&Link->PressRight) //Default to left when buttons pressed simultaneously
				xyInput[0] = -1;
			else if(Link->PressLeft||Link->InputLeft) //Set axis based on which button what pressed
				xyInput[0] = -1;
			else if(Link->PressRight||Link->InputRight)
				xyInput[0] = 1;
		}
		else{ //If Y axis pressed
			if(!Link->InputLeft&&!Link->InputRight) //Release Y axis if neither button pressed
				xyInput[0] = 0;
			else if(xyInput[0]==-1&&!Link->InputLeft) //Reverse Y axis if opposite direction held and button released
				xyInput[0] = 1;
			else if(xyInput[0]==1&&!Link->InputRight)
				xyInput[0] = -1;
		}
	}
	int ScriptedDamageCombo_DetectLinkCollision(int flag, int x, int y, int dir, bool notSolid, int xyInput){
		ScriptedDamageCombo_UpdateInput(xyInput);
		
		int x2; int y2;
		//Check for collisions against solid combos
		if(!notSolid){
			//Check for sideview gravity
			if(IsSideview()){
				bool onSpikes;
				bool onSafeSolid;
				for(int i=SDC_FALLING_HITBOX_LEFT;i<=SDC_FALLING_HITBOX_RIGHT; i=Min(i+8, SDC_FALLING_HITBOX_RIGHT)){
					x2 = x+i;
					y2 = y+16;
					//Spikes mark Link as getting hurt
					if(ComboFI(x2, y2, flag)){
						if(Screen->isSolid(x2, y2)){
							onSpikes = true;
						}
					}
					//But standing on adjacent land overrides this
					else{
						if(Screen->isSolid(x2, y2)){
							onSafeSolid = true;
						}
					}
					if(i==SDC_FALLING_HITBOX_RIGHT)
						break;
				}
				if(onSpikes&&!onSafeSolid){
					return 2; //Special return value for sideview spikes, which overrides the damage cooldown
				}
				
				if(SCRIPTEDDAMAGECOMBO_DAMAGEONHEADBONK){
					if(Link->Jump>0){
						onSpikes = false;
						onSafeSolid  = false;
						for(int i=SDC_CEILING_HITBOX_LEFT;i<=SDC_CEILING_HITBOX_RIGHT; i=Min(i+8, SDC_CEILING_HITBOX_RIGHT)){
							x2 = x+i;
							y2 = y-Max(Ceiling(Link->Jump), 1);
							//Spikes mark Link as getting hurt
							if(ComboFI(x2, y2, flag)){
								if(Screen->isSolid(x2, y2)){
									onSpikes = true;
								}
							}
							//But standing on adjacent land overrides this
							else{
								if(Screen->isSolid(x2, y2)){
									onSafeSolid = true;
								}
							}
							if(i==SDC_CEILING_HITBOX_RIGHT)
								break;
						}
						if(onSpikes&&!onSafeSolid){
							return 2; //Special return value for sideview spikes, which overrides the damage cooldown
						}
					}
				}
			}
			
			//Check collisions walking up
			if(xyInput[1]==-1){
				for(int i=SDC_SOLID_HITBOX_UD_LEFT; i<=SDC_SOLID_HITBOX_UD_RIGHT; i=Min(i+8, SDC_SOLID_HITBOX_UD_RIGHT)){
					x2 = x+i;
					y2 = y+SDC_SOLID_HITBOX_UD_TOP-1;
					if(ComboFI(x2, y2, flag)){
						if(Screen->isSolid(x2, y2)){
							if(!SCRIPTEDDAMAGECOMBO_ONLYFACING||Link->Dir==DIR_UP)
								return 1;
						}
					}
					if(i==SDC_SOLID_HITBOX_UD_RIGHT)
						break;
				}
			}
			//Check collisions walking down
			else if(xyInput[1]==1){
				for(int i=SDC_SOLID_HITBOX_UD_LEFT; i<=SDC_SOLID_HITBOX_UD_RIGHT; i=Min(i+8, SDC_SOLID_HITBOX_UD_RIGHT)){
					x2 = x+i;
					y2 = y+16;
					if(ComboFI(x2, y2, flag)){
						if(Screen->isSolid(x2, y2)){
							if(!SCRIPTEDDAMAGECOMBO_ONLYFACING||Link->Dir==DIR_DOWN)
								return 1;
						}
					}
					if(i==SDC_SOLID_HITBOX_UD_RIGHT)
						break;
				}
			}
			//Check collisions walking left
			else if(xyInput[0]==-1){
				for(int i=SDC_SOLID_HITBOX_LR_TOP; i<=SDC_SOLID_HITBOX_LR_BOTTOM; i=Min(i+8, SDC_SOLID_HITBOX_LR_BOTTOM)){
					x2 = x-1;
					y2 = y+i;
					if(ComboFI(x2, y2, flag)){
						if(Screen->isSolid(x2, y2)){
							if(!SCRIPTEDDAMAGECOMBO_ONLYFACING||Link->Dir==DIR_LEFT)
								return 1;
						}
					}
					if(i==SDC_SOLID_HITBOX_LR_BOTTOM)
						break;
				}
			}
			//Check collisions walking right
			else if(xyInput[0]==1){
				for(int i=SDC_SOLID_HITBOX_LR_TOP; i<=SDC_SOLID_HITBOX_LR_BOTTOM; i=Min(i+8, SDC_SOLID_HITBOX_LR_BOTTOM)){
					x2 = x+16;
					y2 = y+i;
					if(ComboFI(x2, y2, flag)){
						if(Screen->isSolid(x2, y2)){
							if(!SCRIPTEDDAMAGECOMBO_ONLYFACING||Link->Dir==DIR_RIGHT)
								return 1;
						}
					}
					if(i==SDC_SOLID_HITBOX_LR_BOTTOM)
						break;
				}
			}
		}
		//Check for collisions against nonsolid combos
		else{
			int hit[4] = {SDC_NONSOLID_HITBOX_LEFT, SDC_NONSOLID_HITBOX_RIGHT, SDC_NONSOLID_HITBOX_TOP, SDC_NONSOLID_HITBOX_BOTTOM};
			if(IsSideview()){
				hit[0] = SDC_NONSOLID_HITBOX_SIDEVIEW_LEFT;
				hit[1] = SDC_NONSOLID_HITBOX_SIDEVIEW_RIGHT;
				hit[2] = SDC_NONSOLID_HITBOX_SIDEVIEW_TOP;
				hit[3] = SDC_NONSOLID_HITBOX_SIDEVIEW_BOTTOM;
			}
			
			for(int xi=hit[0]; xi<=hit[1]; xi=Min(xi+8, hit[1])){
				for(int yi=hit[2]; yi<=hit[3]; yi=Min(yi+8, hit[3])){
					x2 = x+xi;
					y2 = y+yi;
					if(ComboFI(x2, y2, flag)){
						return 1;
					}
					if(yi==hit[3])
						break;
				}
				if(xi==hit[1])
					break;
			}
		}
		return 0;
	}
}