Copy to Clipboard Test

ALttP Beamos Code

const int LAYER_BEAMOS1 = 2; //Beamos use this layer to draw the bottom half
const int LAYER_BEAMOS2 = 3; //Beamos use this layer to draw the top half and eye
const int BEAMOS_IMPRECISION = 6; //How close the beamos must be aiming at Link to shoot
const int BEAMOS_FLASHFRAMES = 10; //How many frames the beamos flashes for before firing
const int BEAMOS_LASER_SPEED = 6; //How fast the laser shoots out
const int BEAMOS_LASER_LENGTH = 80; //How long the laser is
const int BEAMOS_LASER_MINDIST = 16; //How far the beamos can see/shoot through solid objects
const int COLOR_BEAMOS_LASER1 = 0x84; //The color of the inner laser
const int COLOR_BEAMOS_LASER2 = 0x85; //The color of the outer laser
const int CMB_BEAMOS_LASER_ENDPOINT = 1010; //The combo used for the ends of the laser
const int CS_BEAMOS_LASER_ENDPOINT = 8; //The cset used for the ends of the laser
const int SFX_BEAMOS_SIGHT = 69; //The sound that plays when the beamos sees Link
const int SFX_BEAMOS_LASER = 70; //The sound that plays when the beamos fires its laser

//LttP Beamos Script
//Attribute 1: The starting angle of the eye in degrees.
//Attribute 2: How fast the eye turns each frame in degrees. Make negative for a counterclockwise turn.
//Attribute 3: How long the enemy waits before it can fire another laser in frames. (60ths of a second)
//Attribute 4: Whether or not the enemy has collision. 0 = Yes, 1 = No. No collision is mostly for placing over a solid combo.
//Attribute 11: The first of 10 combos. Statue top, statue bottom, eye up, eye down, eye left, eye right, eye left-up, eye right-up, eye left-down, eye right-down
//Attribute 12: The slot this script is loaded into

ffc script Beamos{
	//This function draws the parts of the beamos in the right order
	void Beamos_Draw(ffc this, npc ghost, int Combo, int EyeAngle){
		int EyeX = Ghost_X+VectorX(8, EyeAngle);
		int EyeY = Ghost_Y+4+VectorY(5, EyeAngle);
		int EyeCombo = Combo+2+AngleDir8(EyeAngle);
		if(Link->HP>0){
			if(EyeAngle<=0)
				Screen->FastCombo(LAYER_BEAMOS2, EyeX, EyeY, EyeCombo, this->CSet, 128);
			Screen->FastCombo(LAYER_BEAMOS2, Ghost_X, Ghost_Y, Combo, this->CSet, 128);
			if(EyeAngle>0)
				Screen->FastCombo(LAYER_BEAMOS2, EyeX, EyeY, EyeCombo, this->CSet, 128);
			Screen->FastCombo(LAYER_BEAMOS1, Ghost_X, Ghost_Y+16, Combo+1, this->CSet, 128);
		}
	}
	//This function checks the path of the beamos' laser before firing
	bool CheckPath(int X, int Y, int Angle, int Distance, int SafeDist, int Step){
		for(int i = 0; i<Distance-Step; i+=Step){
			X += VectorX(Step, Angle);
			Y += VectorY(Step, Angle);
			if(Screen->isSolid(X, Y)&&i>SafeDist)
				return false;
		}
		return true;
	}
	//This function checks if the laser is colliding with Link and deals damage
	void CheckBeamosLaser(npc ghost, int StartX, int StartY, int EndX, int EndY){
		if(lineBoxCollision(StartX, StartY, EndX, EndY, Link->X, Link->Y, Link->X+Link->HitWidth, Link->Y+Link->HitHeight, 0)){
			eweapon e = FireEWeapon(EW_SCRIPT10, Link->X+InFrontX(Link->Dir, 10), Link->Y+InFrontY(Link->Dir, 10), 0, 0, ghost->WeaponDamage, -1, -1, EWF_UNBLOCKABLE);
			SetEWeaponLifespan(e, EWL_TIMER, 1);
			SetEWeaponDeathEffect(e, EWD_VANISH, 0);
			e->DrawYOffset = -1000;
		}
	}
	void run(int enemyid){
		npc ghost = Ghost_InitAutoGhost(this, enemyid);
		int StartAngle = ghost->Attributes[0];
		int IncrementAngle = ghost->Attributes[1];
		int LaserCooldown = ghost->Attributes[2];
		int NoCollision = ghost->Attributes[3];
		int Combo = ghost->Attributes[10];
		Ghost_Transform(this, ghost, GH_INVISIBLE_COMBO, ghost->CSet, 1, 2);
		Ghost_Y-=8;
		if(NoCollision==1)
			ghost->CollDetection = false;
		Ghost_SetHitOffsets(ghost, 16, 0, 0, 0);
		int EyeAngle = WrapDegrees(StartAngle);
		this->Flags[FFCF_IGNOREHOLDUP] = true;
		while(true){
			EyeAngle = WrapDegrees(EyeAngle+IncrementAngle);
			int EyeX = Ghost_X+VectorX(8, EyeAngle);
			int EyeY = Ghost_Y+4+VectorY(5, EyeAngle);
			int AngleLink = Angle(EyeX+8, EyeY+8, CenterLinkX(), CenterLinkY());
			int DistLink = Distance(EyeX+8, EyeY+8, CenterLinkX(), CenterLinkY());
			//Check if the eye is aimed at Link and there's a clear path before firing
			if(Abs(angleDifference(EyeAngle, AngleLink))<BEAMOS_IMPRECISION&&CheckPath(EyeX+8, EyeY+8, AngleLink, DistLink, BEAMOS_LASER_MINDIST, 6)){
				int Angle = Angle(Ghost_X+8, Ghost_Y+8, CenterLinkX(), CenterLinkY());
				int LStartX = EyeX+8;
				int LStartY = EyeY+8;
				int LEndX = LStartX;
				int LEndY = LStartY;
				int LaserLength = 0;
				bool LaserEnded = false;
				//Play the line of sight sound and flash briefly
				Game->PlaySound(SFX_BEAMOS_SIGHT);
				Ghost_StartFlashing(BEAMOS_FLASHFRAMES);
				for(int i=0; i<BEAMOS_FLASHFRAMES; i++){
					Beamos_Draw(this, ghost, Combo, EyeAngle);
					Ghost_Waitframe(this, ghost, true, true);
				}
				//Fire the laser until it reaches full length or hits a wall
				Game->PlaySound(SFX_BEAMOS_LASER);
				while(Distance(LStartX, LStartY, LEndX, LEndY)<BEAMOS_LASER_LENGTH&&!LaserEnded){
					int LDist = Distance(LStartX, LStartY, LEndX, LEndY);
					LEndX += VectorX(BEAMOS_LASER_SPEED, Angle);
					LEndY += VectorY(BEAMOS_LASER_SPEED, Angle);
					LaserLength += BEAMOS_LASER_SPEED;
					if((Screen->isSolid(LEndX, LEndY)&&Distance(EyeX+8, EyeY+8, LEndX, LEndY)>=BEAMOS_LASER_MINDIST)||(LEndX<-16||LEndX>272||LEndY<-16||LEndY>192))
						LaserEnded = true;
					//Draw everything in order
					int EyeCombo = Combo+2+AngleDir8(EyeAngle);
					if(Link->HP>0){
						if(EyeAngle<=0){
							Screen->FastCombo(LAYER_BEAMOS2, EyeX, EyeY, EyeCombo, this->CSet, 128);
							Screen->Rectangle(LAYER_BEAMOS2, LStartX, LStartY-1, LStartX+LDist, LStartY+1, COLOR_BEAMOS_LASER2, 1, LStartX, LStartY, Angle, true, 128);
							Screen->Line(LAYER_BEAMOS2, LStartX, LStartY, LEndX, LEndY, COLOR_BEAMOS_LASER1, 1, 0, 0, 0, 128);
							Screen->FastCombo(LAYER_BEAMOS2, LStartX-8, LStartY-8, CMB_BEAMOS_LASER_ENDPOINT, CS_BEAMOS_LASER_ENDPOINT, 128);
						}
						Screen->FastCombo(LAYER_BEAMOS2, Ghost_X, Ghost_Y, Combo, this->CSet, 128);
						if(EyeAngle>0){
							Screen->FastCombo(LAYER_BEAMOS2, EyeX, EyeY, EyeCombo, this->CSet, 128);
							Screen->Rectangle(LAYER_BEAMOS2, LStartX, LStartY-1, LStartX+LDist, LStartY+1, COLOR_BEAMOS_LASER2, 1, LStartX, LStartY, Angle, true, 128);
							Screen->Line(LAYER_BEAMOS2, LStartX, LStartY, LEndX, LEndY, COLOR_BEAMOS_LASER1, 1, 0, 0, 0, 128);
							Screen->FastCombo(LAYER_BEAMOS2, LStartX-8, LStartY-8, CMB_BEAMOS_LASER_ENDPOINT, CS_BEAMOS_LASER_ENDPOINT, 128);
						}
						Screen->FastCombo(LAYER_BEAMOS1, Ghost_X, Ghost_Y+16, Combo+1, this->CSet, 128);
					}
					CheckBeamosLaser(ghost, LStartX, LStartY, LEndX, LEndY);
					Ghost_Waitframe(this, ghost, true, true);
				}
				//If it reaches full length
				if(!LaserEnded){
					while((!Screen->isSolid(LEndX, LEndY)&&!(LEndX<-16||LEndX>272||LEndY<-16||LEndY>192))||Distance(EyeX+8, EyeY+8, LEndX, LEndY)<BEAMOS_LASER_MINDIST){
						int LDist = Distance(LStartX, LStartY, LEndX, LEndY);
						LStartX += VectorX(BEAMOS_LASER_SPEED, Angle);
						LStartY += VectorY(BEAMOS_LASER_SPEED, Angle);
						LEndX += VectorX(BEAMOS_LASER_SPEED, Angle);
						LEndY += VectorY(BEAMOS_LASER_SPEED, Angle);
						if(Screen->isSolid(LEndX, LEndY))
							LaserEnded = true;
						if(EyeAngle<=0){
							Screen->Rectangle(LAYER_BEAMOS2, LStartX, LStartY-1, LStartX+LDist, LStartY+1, COLOR_BEAMOS_LASER2, 1, LStartX, LStartY, Angle, true, 128);
							Screen->Line(LAYER_BEAMOS2, LStartX, LStartY, LEndX, LEndY, COLOR_BEAMOS_LASER1, 1, 0, 0, 0, 128);
						}
						Beamos_Draw(this, ghost, Combo, EyeAngle);
						if(EyeAngle>0){
							Screen->Rectangle(LAYER_BEAMOS2, LStartX, LStartY-1, LStartX+LDist, LStartY+1, COLOR_BEAMOS_LASER2, 1, LStartX, LStartY, Angle, true, 128);
							Screen->Line(LAYER_BEAMOS2, LStartX, LStartY, LEndX, LEndY, COLOR_BEAMOS_LASER1, 1, 0, 0, 0, 128);
						}
						Screen->Rectangle(LAYER_BEAMOS2, LStartX, LStartY-1, LStartX+LDist, LStartY+1, COLOR_BEAMOS_LASER2, 1, LStartX, LStartY, Angle, true, 128);
						Screen->Line(LAYER_BEAMOS2, LStartX, LStartY, LEndX, LEndY, COLOR_BEAMOS_LASER1, 1, 0, 0, 0, 128);
						CheckBeamosLaser(ghost, LStartX, LStartY, LEndX, LEndY);
						Ghost_Waitframe(this, ghost, true, true);
					}
				}
				//Once the laser has hit a wall, make the ends meet again
				while(Distance(LStartX, LStartY, LEndX, LEndY)>BEAMOS_LASER_SPEED){
					int LDist = Distance(LStartX, LStartY, LEndX, LEndY);
					if(LaserLength>BEAMOS_LASER_LENGTH){
						LStartX += VectorX(BEAMOS_LASER_SPEED, Angle);
						LStartY += VectorY(BEAMOS_LASER_SPEED, Angle);
					}
					if(LaserLength<=BEAMOS_LASER_LENGTH)
						LaserLength += BEAMOS_LASER_SPEED;
					//Draw everything in order
					int EyeCombo = Combo+2+AngleDir8(EyeAngle);
					if(Link->HP>0){
						if(EyeAngle<=0){
							Screen->FastCombo(LAYER_BEAMOS2, EyeX, EyeY, EyeCombo, this->CSet, 128);
							Screen->Rectangle(LAYER_BEAMOS2, LStartX, LStartY-1, LStartX+LDist, LStartY+1, COLOR_BEAMOS_LASER2, 1, LStartX, LStartY, Angle, true, 128);
							Screen->Line(LAYER_BEAMOS2, LStartX, LStartY, LEndX, LEndY, COLOR_BEAMOS_LASER1, 1, 0, 0, 0, 128);
							if(LaserLength<=BEAMOS_LASER_LENGTH)
								Screen->FastCombo(LAYER_BEAMOS2, LStartX-8, LStartY-8, CMB_BEAMOS_LASER_ENDPOINT, CS_BEAMOS_LASER_ENDPOINT, 128);
							Screen->FastCombo(LAYER_BEAMOS2, LEndX-8, LEndY-8, CMB_BEAMOS_LASER_ENDPOINT, CS_BEAMOS_LASER_ENDPOINT, 128);
						}
						Screen->FastCombo(LAYER_BEAMOS2, Ghost_X, Ghost_Y, Combo, this->CSet, 128);
						if(EyeAngle>0){
							Screen->FastCombo(LAYER_BEAMOS2, EyeX, EyeY, EyeCombo, this->CSet, 128);
							Screen->Rectangle(LAYER_BEAMOS2, LStartX, LStartY-1, LStartX+LDist, LStartY+1, COLOR_BEAMOS_LASER2, 1, LStartX, LStartY, Angle, true, 128);
							Screen->Line(LAYER_BEAMOS2, LStartX, LStartY, LEndX, LEndY, COLOR_BEAMOS_LASER1, 1, 0, 0, 0, 128);
							if(LaserLength<=BEAMOS_LASER_LENGTH)
								Screen->FastCombo(LAYER_BEAMOS2, LStartX-8, LStartY-8, CMB_BEAMOS_LASER_ENDPOINT, CS_BEAMOS_LASER_ENDPOINT, 128);
							Screen->FastCombo(LAYER_BEAMOS2, LEndX-8, LEndY-8, CMB_BEAMOS_LASER_ENDPOINT, CS_BEAMOS_LASER_ENDPOINT, 128);
						}
						Screen->FastCombo(LAYER_BEAMOS1, Ghost_X, Ghost_Y+16, Combo+1, this->CSet, 128);
					}
					CheckBeamosLaser(ghost, LStartX, LStartY, LEndX, LEndY);
					Ghost_Waitframe(this, ghost, true, true);
				}
				for(int i=0; i<LaserCooldown; i++){
					EyeAngle = WrapDegrees(EyeAngle+IncrementAngle);
					Beamos_Draw(this, ghost, Combo, EyeAngle);
					Ghost_Waitframe(this, ghost, true, true);
				}
			}
			Beamos_Draw(this, ghost, Combo, EyeAngle);
			Ghost_Waitframe(this, ghost, true, true);
		}
	}
}

//These following two functions are taken from theRandomHeader.zh. If you're using that, you can delete them here.
// 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 lineBoxCollision

// Function to get the difference between two angles
float angleDifference(float angle1, float angle2)
{
	// Get the difference between the two angles
	float dif = angle2 - angle1;
	
	// Compensate for the difference being outside of normal bounds
	if(dif >= PI)
		dif -= 2 * PI;
	else if(dif <= -1 * PI)
		dif += 2 * PI;
		
	return dif;
}