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