Big Wizzrobe Trio (Autoghost) Code

//Fake enemy
//Set to not be a beatable enemy
//Set to not return on death
//Will die along with main boss
//Must be placed on screen in enemy editor and set up like other enemies

ffc script Fake_Agahnim{
	void run(int enemyid){
		int i; 
		npc ghost = Ghost_InitAutoGhost(this, enemyid);
		int SFX = ghost->Attributes[0];
		int SPR = ghost->Attributes[1];
		int combo = ghost->Attributes[10];
		int attackCooldown = ghost->Haltrate*10;
		float angle = Rand(360);
		bool attackCond = false;
		eweapon fireball;
		int counter=-1;
		int attack;
		int X;
		int Y;
		Ghost_SetSize(this,ghost,2,2);
		while(true){
			if(Ghost_Dir==DIR_UP)
				Ghost_Data = combo;
			else if(Ghost_Dir==DIR_DOWN)
				Ghost_Data = combo+1;
			else if(Ghost_Dir==DIR_LEFT)
				Ghost_Data = combo+2;
			else if(Ghost_Dir==DIR_RIGHT)
				Ghost_Data = combo+3;		
			if(attackCooldown>0){
				counter = Ghost_ConstantWalk4(counter, ghost->Step, ghost->Rate, ghost->Homing, ghost->Hunger);
				attackCooldown--;
			}
			else if(attackCooldown<=0)
				attackCond=true;
			if(attackCond){
				Gen_Explode_Waitframes(this,ghost, Choose(32, 48, 96));
				TeleportNPC(this, ghost);	
				angle = Angle(ghost->X+16,ghost->Y+16,CenterLinkX(),CenterLinkY());
				if(Between(angle,0,45))
					angle=0;
				else if(Between(angle,46,135))
					angle=90;
				else if(Between(angle,136,225))
					angle= 180;
				else if(Between(angle,226,315))
					angle= 270;
				else
					angle= 0;
				if(angle==90){
					Ghost_Dir=DIR_DOWN;
					Ghost_Data = combo+1;
					X= 12;
					Y= 32;
				}
				else if(angle==180){
					Ghost_Dir=DIR_LEFT;
					Ghost_Data = combo+2;
					X= -16;
					Y= 12;
				}
				else if(angle==270){
					Ghost_Dir=DIR_UP;
					Ghost_Data = combo;
					X= 12;
					Y= -16;
				}
				else{
					Ghost_Dir= DIR_RIGHT;
					Ghost_Data = combo+3;
					X= 32;
					Y= 12;
				}
				Ghost_Waitframes(this,ghost,30);
				fireball = FireEWeapon(EW_MAGIC,ghost->X+X,ghost->Y+Y,DegtoRad(angle), 200, 
											ghost->WeaponDamage, SPR, SFX,0);
				attackCooldown = ghost->Haltrate*10;
				attackCond = false;
			}
			Gen_Explode_Waitframe(this,ghost);
		}
	}
}

ffc script Agahnim{
	void run(int enemyid){
		int i; 
		npc ghost = Ghost_InitAutoGhost(this, enemyid);
		int SFX = ghost->Attributes[0];
		int SPR = ghost->Attributes[1];
		int combo = ghost->Attributes[10];
		int attackCooldown = ghost->Haltrate*10;
		float angle = Rand(360);
		bool attackCond = false;
		eweapon fireball;
		int counter=-1;
		int attack;
		int X;
		int Y;
		Ghost_SetSize(this,ghost,2,2);
		while(true){
			if(Ghost_Dir==DIR_UP)
				Ghost_Data = combo;
			else if(Ghost_Dir==DIR_DOWN)
				Ghost_Data = combo+1;
			else if(Ghost_Dir==DIR_LEFT)
				Ghost_Data = combo+2;
			else if(Ghost_Dir==DIR_RIGHT)
				Ghost_Data = combo+3;		
			if(attackCooldown>0){
				counter = Ghost_ConstantWalk4(counter, ghost->Step, ghost->Rate, ghost->Homing, ghost->Hunger);
				attackCooldown--;
			}
			else if(attackCooldown<=0)
				attackCond=true;
			if(attackCond){
				Gen_Leader_Waitframes(this,ghost, Choose(32, 48, 96));
				if(Rand(0,100)>51)
					attack= 1;
				else
					attack=0;
				if(attack==0){
					TeleportNPC(this, ghost);	
					angle = Angle(ghost->X+16,ghost->Y+16,CenterLinkX(),CenterLinkY());
					if(Between(angle,0,45))
						angle=0;
					else if(Between(angle,46,135))
						angle=90;
					else if(Between(angle,136,225))
						angle= 180;
					else if(Between(angle,226,315))
						angle= 270;
					else
						angle= 0;
					if(angle==90){
						Ghost_Dir=DIR_DOWN;
						Ghost_Data = combo+1;
						X= 12;
						Y= 32;
					}
					else if(angle==180){
						Ghost_Dir=DIR_LEFT;
						Ghost_Data = combo+2;
						X= -16;
						Y= 12;
					}
					else if(angle==270){
						Ghost_Dir=DIR_UP;
						Ghost_Data = combo;
						X= 12;
						Y= -16;
					}
					else{
						Ghost_Dir= DIR_RIGHT;
						Ghost_Data = combo+3;
						X= 32;
						Y= 12;
					}
					Gen_Leader_Waitframes(this,ghost,30);
					fireball = FireEWeapon(EW_MAGIC,ghost->X+X,ghost->Y+Y,DegtoRad(angle), 200, 
												ghost->WeaponDamage, SPR, SFX,0);
				}
				else{
					X= 120;
					Y= 32;
					angle = Angle(CenterX(ghost), CenterY(ghost), X,Y);
					Ghost_Data= combo+1;
					Gen_Leader_Waitframe(this,ghost);
					while(Ghost_CanMove(AngleDir8(angle), 16, 0)){
						Ghost_MoveAtAngle(angle, 5, 0);
						Phantom_Rush(this, ghost);
						Gen_Leader_Waitframe(this,ghost);
					}
					Lightning(this,ghost);
				}
				attackCooldown = ghost->Haltrate*10;
				attackCond = false;
			}
			Gen_Leader_Waitframe(this,ghost);
		}
	}
}

//Newbie boss functions by Moosh
const int GALE_WARP_SFX = 91;

void TeleportNPC(ffc this, npc ghost){	
	int w = ghost->TileWidth;
	int h = ghost->TileHeight;
	Game->PlaySound(GALE_WARP_SFX);
	int tc;
	ghost->CollDetection = false;
	for(int i=0; i<16; i++){
		if(i%2==0)
			ghost->DrawYOffset = -1000;
		else
			ghost->DrawYOffset = -2;
		Gen_Explode_Waitframe(this,ghost);
	}
	ghost->DrawYOffset = -1000;
	tc = Rand(176);
	for(int i=0; i<352&&(!CanPlaceNPC(ghost, ComboX(tc), 
			ComboY(tc))||Distance(ComboX(tc)+ghost->HitWidth/2, 
			ComboY(tc)+ghost->HitHeight/2, CenterLinkX(), CenterLinkY())<((w+h)/2)*8+32); i++){
		if(i>=176)
			tc = i-176;
		else
			tc = Rand(176);
	}
	Ghost_X = ComboX(tc);
	Ghost_Y = ComboY(tc);
	Gen_Explode_Waitframe(this,ghost);
	Ghost_Dir = AngleDir4(Angle(CenterX(ghost), CenterY(ghost), CenterLinkX(), CenterLinkY()));
	for(int i=0; i<16; i++){
		if(i%2==0)
			ghost->DrawYOffset = -1000;
		else
			ghost->DrawYOffset = -2;
		Gen_Explode_Waitframe(this,ghost);
	}
	ghost->DrawYOffset = -2;
	ghost->CollDetection = true;
}

bool CanPlaceNPC(npc ghost, int X, int Y){
	for(int x=ghost->HitXOffset; x<=ghost->HitXOffset+ghost->HitWidth-1; x=Min(x+8, ghost->HitXOffset+ghost->HitWidth-1)){
		for(int y=ghost->HitYOffset; y<=ghost->HitYOffset+ghost->HitHeight-1; y=Min(y+8, ghost->HitYOffset+ghost->HitHeight-1)){
			if(!Ghost_CanMovePixel(X+x, Y+y))
				return false;
			if(y==ghost->HitYOffset+ghost->HitHeight-1)
				break;
		}
		if(x==ghost->HitXOffset+ghost->HitWidth-1)
			break;
	}
	return true;
}

void Phantom_Rush(ffc this, npc ghost){
	int tile = Game->ComboTile(this->Data);
	lweapon trail = CreateLWeaponAt(LW_SCRIPT5, ghost->X+ghost->DrawXOffset, ghost->Y+ghost->DrawYOffset);
	trail->Extend = 3;
	trail->TileWidth = ghost->TileWidth;
	trail->TileHeight = ghost->TileHeight;
	trail->CSet = this->CSet;
	trail->Tile = tile;
	trail->OriginalTile = tile;
	trail->DrawStyle = DS_PHANTOM;
	trail->DeadState = 8;
}

//Lightning functions by Dimi
const int SFX_BOLT = 66;

void Lightning(ffc this, npc ghost){
	int Lightning1X[25];
	int Lightning1Y[25];
	int Lightning2X[25];
	int Lightning2Y[25];
	int Lightning3X[25];
	int Lightning3Y[25];
	int Lightning4X[25];
	int Lightning4Y[25];
	int HitCounter = 0;
	int i;
	int m= Rand(2,6);
	for (; m!= 0;){
		if (m > 0) m--;
		for (i = 24; i >= 0; i--){
			Lightning1X[i] = this->X+16;
			Lightning1Y[i] = this->Y+32;
			Lightning2X[i] = this->X+16;
			Lightning2Y[i] = this->Y+32;
			Lightning3X[i] = this->X+16;
			Lightning3Y[i] = this->Y+32;
			Lightning4X[i] = this->X+16;
			Lightning4Y[i] = this->Y+32;
		}
		Game->PlaySound(SFX_BOLT);
		while((Lightning1Y[24] < 176 
				|| Lightning2Y[24] < 176 
				|| Lightning3Y[24] < 176
				|| Lightning4Y[24] < 176)){
			bool Struck = false;
			for (int l = 0; l <= 2; l++){
				int Angle1 = Choose(210, 150, 225, 135);
				Angle1 -= 90;
				if(Link->X<=ghost->X-16)
					Angle1+=22;
				else if(Link->X>=ghost->X+48)
					Angle1-=22;
				ShiftArray(Lightning1X);
				ShiftArray(Lightning1Y);
				Lightning1X[0] += VectorX(10, Angle1);
				Lightning1Y[0] += VectorY(10, Angle1);
				int Angle2 = Choose(210, 150, 225, 135);
				Angle2 -= 90;
				if(Link->X<=ghost->X-16)
					Angle2+=22;
				else if(Link->X>=ghost->X+48)
					Angle2-=22;
				ShiftArray(Lightning2X);
				ShiftArray(Lightning2Y);
				Lightning2X[0] += VectorX(10, Angle2);
				Lightning2Y[0] += VectorY(10, Angle2);
				int Angle3 = Choose(210, 150, 225, 135);
				Angle3 -= 90;
				if(Link->X<=ghost->X-16)
					Angle3+=22;
				else if(Link->X>=ghost->X+48)
					Angle3-=22;
				ShiftArray(Lightning3X);
				ShiftArray(Lightning3Y);
				Lightning3X[0] += VectorX(10, Angle3);
				Lightning3Y[0] += VectorY(10, Angle3);
				int Angle4 = Choose(210, 150, 225, 135);
				Angle4 -= 90;
				if(Link->X<=ghost->X-16)
					Angle4+=22;
				else if(Link->X>=ghost->X+48)
					Angle4-=22;
				ShiftArray(Lightning4X);
				ShiftArray(Lightning4Y);
				Lightning4X[0] += VectorX(10, Angle4);
				Lightning4Y[0] += VectorY(10, Angle4);
				for (i = 24; i > 0; i--){
					Screen->Line(2, Lightning1X[i], Lightning1Y[i], Lightning1X[i-1], 
									Lightning1Y[i-1], 0x0B, 1, 0, 0, 0, 128);
					Screen->Line(2, Lightning2X[i], Lightning2Y[i], Lightning2X[i-1], 
									Lightning2Y[i-1], 0x0B, 1, 0, 0, 0, 128);
					Screen->Line(2, Lightning3X[i], Lightning3Y[i], Lightning3X[i-1], 
									Lightning3Y[i-1], 0x0B, 1, 0, 0, 0, 128);
					Screen->Line(2, Lightning4X[i], Lightning4Y[i], Lightning4X[i-1], 
									Lightning4Y[i-1], 0x0B, 1, 0, 0, 0, 128);
					Screen->Line(2, Lightning1X[i] - 1, Lightning1Y[i], Lightning1X[i-1] - 1, 
									Lightning1Y[i-1], 0x0B, 2, 0, 0, 0, 64);
					Screen->Line(2, Lightning2X[i] - 1, Lightning2Y[i], Lightning2X[i-1] - 1, 
									Lightning2Y[i-1], 0x0B, 2, 0, 0, 0, 64);
					Screen->Line(2, Lightning3X[i] - 1, Lightning3Y[i], Lightning3X[i-1] - 1, 
									Lightning3Y[i-1], 0x0B, 2, 0, 0, 0, 64);
					Screen->Line(2, Lightning4X[i] - 1, Lightning4Y[i], Lightning4X[i-1] - 1, 
									Lightning4Y[i-1], 0x0B, 2, 0, 0, 0, 64);
					if (!Struck){
						if (lineBoxCollision(Lightning1X[i], Lightning1Y[i], 
												Lightning1X[i-1], Lightning1Y[i-1], 
												Link->X, Link->Y, Link->X+Link->HitWidth, Link->Y+Link->HitHeight, 0))
						{
							Struck = true;
							if (HitCounter <= 0) HitCounter = 10;
							continue;
						}	
						if (lineBoxCollision(Lightning2X[i], Lightning2Y[i], 
												Lightning2X[i-1], Lightning2Y[i-1], 
												Link->X, Link->Y, Link->X+Link->HitWidth, Link->Y+Link->HitHeight, 0))
						{
							Struck = true;
							if (HitCounter <= 0) HitCounter = 10;
							continue;
						}
						if (lineBoxCollision(Lightning3X[i], Lightning3Y[i], 
												Lightning3X[i-1], Lightning3Y[i-1], 
												Link->X, Link->Y, Link->X+Link->HitWidth, Link->Y+Link->HitHeight, 0))
						{
							Struck = true;
							if (HitCounter <= 0) HitCounter = 10;
							continue;
						}
						if (lineBoxCollision(Lightning4X[i], Lightning4Y[i], 
												Lightning4X[i-1], Lightning4Y[i-1], 
												Link->X, Link->Y, Link->X+Link->HitWidth, Link->Y+Link->HitHeight, 0))
						{
							Struck = true;
							if (HitCounter <= 0) HitCounter = 10;
							continue;
						}
					}
				}
			}
			if (Struck){
				eweapon e = FireEWeapon(EW_SCRIPT10, Link->X+InFrontX(Link->Dir, 10), 
											Link->Y+InFrontY(Link->Dir, 10), 0, 0, 8, -1, -1, EWF_UNBLOCKABLE);
				SetEWeaponLifespan(e, EWL_TIMER, 1);
				SetEWeaponDeathEffect(e, EWD_VANISH, 0);
				e->DrawYOffset = -1000;
			}
			if (HitCounter > 0){
				if (Link->HP > 0)
					Screen->Rectangle(6, 0, 0, 256, 176, 1, 1, 0, 0, 0, true, OP_TRANS);
			}
			Gen_Explode_Waitframe(this,ghost);
			for (i = 24; i > 0; i--){
				Screen->Line(2, Lightning1X[i], Lightning1Y[i], 
								Lightning1X[i-1], Lightning1Y[i-1], 0x0B, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning2X[i], Lightning2Y[i], 
								Lightning2X[i-1], Lightning2Y[i-1], 0x0B, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning3X[i], Lightning3Y[i], 
								Lightning3X[i-1], Lightning3Y[i-1], 0x0B, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning4X[i], Lightning4Y[i], 
								Lightning4X[i-1], Lightning4Y[i-1], 0x0B, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning1X[i] - 1, Lightning1Y[i], 
								Lightning1X[i-1] - 1, Lightning1Y[i-1], 0x01, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning2X[i] - 1, Lightning2Y[i], 
								Lightning2X[i-1] - 1, Lightning2Y[i-1], 0x01, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning3X[i] - 1, Lightning3Y[i], 
								Lightning3X[i-1] - 1, Lightning3Y[i-1], 0x01, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning4X[i] - 1, Lightning4Y[i], 
								Lightning4X[i-1] - 1, Lightning4Y[i-1], 0x01, 2, 0, 0, 0, 64);
			}
			if (HitCounter > 0){
				if (Link->HP > 0)
					Screen->Rectangle(6, 0, 0, 256, 176, 1, 1, 0, 0, 0, true, OP_OPAQUE);
				HitCounter--;
			}
			Gen_Explode_Waitframe(this,ghost);
			for (i = 24; i > 0; i--){
				Screen->Line(2, Lightning1X[i], Lightning1Y[i], 
								Lightning1X[i-1], Lightning1Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning2X[i], Lightning2Y[i], 
								Lightning2X[i-1], Lightning2Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning3X[i], Lightning3Y[i],
								Lightning3X[i-1], Lightning3Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning4X[i], Lightning4Y[i], 
								Lightning4X[i-1], Lightning4Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning1X[i] - 1, Lightning1Y[i], 
								Lightning1X[i-1] - 1, Lightning1Y[i-1], 0x0B, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning2X[i] - 1, Lightning2Y[i], 
								Lightning2X[i-1] - 1, Lightning2Y[i-1], 0x0B, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning3X[i] - 1, Lightning3Y[i], 
								Lightning3X[i-1] - 1, Lightning3Y[i-1], 0x0B, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning4X[i] - 1, Lightning4Y[i], 
								Lightning4X[i-1] - 1, Lightning4Y[i-1], 0x0B, 2, 0, 0, 0, 64);
			}
			Gen_Explode_Waitframe(this,ghost);
			for (i = 24; i > 0; i--){
				Screen->Line(2, Lightning1X[i], Lightning1Y[i], 
								Lightning1X[i-1], Lightning1Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning2X[i], Lightning2Y[i], 
								Lightning2X[i-1], Lightning2Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning3X[i], Lightning3Y[i], 
								Lightning3X[i-1], Lightning3Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning4X[i], Lightning4Y[i], 
								Lightning4X[i-1], Lightning4Y[i-1], 0x01, 1, 0, 0, 0, 128);
				Screen->Line(2, Lightning1X[i] - 1, Lightning1Y[i], 
								Lightning1X[i-1] - 1, Lightning1Y[i-1], 0x01, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning2X[i] - 1, Lightning2Y[i], 
								Lightning2X[i-1] - 1, Lightning2Y[i-1], 0x01, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning3X[i] - 1, Lightning3Y[i], 
								Lightning3X[i-1] - 1, Lightning3Y[i-1], 0x01, 2, 0, 0, 0, 64);
				Screen->Line(2, Lightning4X[i] - 1, Lightning4Y[i], 
								Lightning4X[i-1] - 1, Lightning4Y[i-1], 0x01, 2, 0, 0, 0, 64);
			}
			Gen_Explode_Waitframe(this,ghost);
		}
	}
	while(HitCounter > 0){
		if (Link->HP > 0)
			Screen->Rectangle(6, 0, 0, 256, 176, 1, 1, 0, 0, 0, true, OP_OPAQUE);
		Gen_Explode_Waitframe(this,ghost);
		if (Link->HP > 0) 
			Screen->Rectangle(6, 0, 0, 256, 176, 1, 1, 0, 0, 0, true, OP_OPAQUE);
		HitCounter--;
		Gen_Explode_Waitframes(this,ghost,3);
	}
}

void ShiftArray(int Array){
	for (int i = SizeOfArray(Array)-1; i > 0; i--)
		Array[i] = Array[i-1];
}

// Function to see if a box has collided with a line
bool lineBoxCollision(int lineX1, int lineY1, int lineX2, 
						int lineY2, int boxX1, int boxY1, int boxX2, int boxY2, int boxBorder)
{
	// Shrink down the box for the border
	boxX1 += boxBorder; boxY1 += boxBorder;
	boxX2 -= boxBorder; boxY2 -= boxBorder;
	
	// If the line isn't vertical
	if(lineX2!=lineX1)
	{
		
		float i0 = (boxX1 - lineX1)/(lineX2-lineX1);
		float i1 = (boxX2 - lineX1)/(lineX2-lineX1);
		
		float yA = lineY1 + i0*(lineY2-lineY1);
		float yB = lineY1 + i1*(lineY2-lineY1);
		
		
		if(Max(boxX1, boxX2) >= Min(lineX1, lineX2) && Min(boxX1, boxX2) <= Max(lineX1, lineX2) &&
			Max(boxY1, boxY2) >= Min(lineY1, lineY2) && Min(boxY1, boxY2) <= Max(lineY1, lineY2))
		{
			if(Min(boxY1, boxY2) > Max(yA, yB) || Max(boxY1, boxY2) < Min(yA, yB))
				return false;
			else
				return true;
		}
		else
			return false;
	}
	// If the line is vertical
	else if(lineX1 >= boxX1 && lineX1 <= boxX2)
	{
		// Basically we need to find the top and bottom y values of the line to check for intersection
		float lineYMin = lineY1;
		float lineYMax = lineY2;
		
		if(lineYMin > lineYMax)
		{
			lineYMin = lineY2;
			lineYMax = lineY1;
		}
		
		// If either point intersects
		if((boxY1 >= lineYMin && boxY1 <= lineYMax) || (boxY2 >= lineYMin && boxY2 <= lineYMax))
			return true;
	}
	
	return false;
} //! End of lineBoxCollisionlineBoxCollision     

//Utility functions from lweapons.zh
//Uses to make a boss using the Gen_Explode_Waitframe wait for a certain number of frames before doing something.

void Gen_Explode_Waitframes(ffc this, npc ghost,int frames){
	for(;frames>0;frames--){
		Gen_Explode_Waitframe(this,ghost);
	}
}    
                   
//A general utility function to make a boss explode on death.

void Gen_Explode_Waitframe(ffc this, npc ghost){
     if(!Ghost_Waitframe(this, ghost, false, false)){
	   Ghost_DeathAnimation(this, ghost, 2);
	   Quit();
     }
}

//Kills all npcs on screen when this enemy dies.

void Gen_Leader_Waitframe(ffc this, npc ghost){
	 if(!Ghost_Waitframe(this, ghost, false, false)){
	   Ghost_DeathAnimation(this, ghost, 2);
	   for(int i =Screen->NumNPCs();i>0;i--){
			npc n = Screen->LoadNPC(i);
			n->HP = 0;
	   }
	   Quit();
     }
}

void Gen_Leader_Waitframes(ffc this, npc ghost, int frames){
	for(;frames>0;frames--)
		Gen_Leader_Waitframe(this,ghost);
}

//Test if one location is between two others.
//D0- Location to test
//D1- Lower bound
//D2- Higher bound

bool Between(int loc,int greaterthan, int lessthan){
	if(loc>=greaterthan && loc<=lessthan)return true;
	return false;
}