/* ==================================================================== PROJBHVR.HC Projectile Behaviours By Michael Gummelt Various routines for projectile behaviours. =================================================================== */ void(float num_bubbles) DeathBubbles; /* ==================================================== void Skip() MG Returns True if the projectile hit a surface at a slight enough angle to deflect. False if not (meaning the missile should be destroyed in the touch function this was called from). The missile must be a MOVETYPE_BOUNCEMISSILE for this to work, and it's o_angle must match it's last known velocity. For now the angle of deflection is anything below the 0.2 (using the dot product of the negative value of the o_angle and the trace_plane_normal of the surface it hit.) But this could be easily customized to use a parameter or entity field. Values Used: .o_angle (.movetype must be MOVETYPE_BOUNCEMISSILE) ===================================================== */ float Skip (void) { vector dir1,dir2; float dot; dir1 = normalize(self.velocity); traceline(self.origin-dir1*4,self.origin+dir1*16,TRUE,self); dir2=trace_plane_normal; dir1*=-1; dot=dir1*dir2; if(dot<=0.15&&trace_fraction<1) return TRUE; else return FALSE; } /* ==================================================== void Veer(float amount) MG This function will make a projectile wander from it's course in a random manner. It does not actually directly use the .veer value, you must send the veer amount value to the function as a parameter. But this allows it to be used in other ways (call it once, etc.) So you can call it by using Veer(self.veer) or Veer(random()*300) or Veer([any number]), etc. ===================================================== */ void Veer(float amount) { //useful code for making projectiles wander randomly to a specified degree vector veerdir; veerdir_x=veerdir_y=veerdir_z=amount; self.velocity+=RandomVector(veerdir); self.angles=vectoangles(self.velocity); } void VeerThink () { Veer(self.veer); if(self.think==Veer) thinktime self : 0.1; } /* ========================================================= float ahead (entity loser, entity from) MG Checks to see if "loser" is within the forward cone (based on facing, NOT velocity!) of "from". Cone size defaults to 0.2 unless crossbow arrows (they do a one-time narrow-cone aquisition of 0.8) NOTE: "accept" can be a value between -1 and 1, the higher the value, the smaller the cone. Returns TRUE if so, FALSE if not. ========================================================= */ float ahead (entity loser, entity from) { vector proj_dir, spot1, spot2, vec; float accept, dot; proj_dir=normalize(from.velocity); spot1 = from.origin; spot2 = (loser.absmin+loser.absmax)*0.5; if(from.classname=="flaming arrow"||from.classname=="bolt") accept=0.875; else accept=0.2; vec = normalize (spot2 - spot1); dot = vec * proj_dir; if ( dot > accept) return TRUE; return FALSE; } /* ========================================================= float heading (entity loser, entity from, float accept) MG Checks to see if "loser" is within the forward cone (based on velocity, NOT facing!) of "from". "accept" is the size of the cone (see "ahead"), which, if none is sent, defaults to 0.8 (rather narrow). Returns TRUE if so, FALSE if not. ========================================================= */ float heading (entity loser, entity from, float accept) { vector proj_dir, spot1, spot2, vec; float dot; proj_dir=normalize(from.velocity); spot1 = from.origin; spot2 = (loser.absmin+loser.absmax)*0.5; if(!accept) accept=0.8; vec = normalize (spot2 - spot1); dot = vec * proj_dir; if ( dot > accept) return TRUE; return FALSE; } void()HomeThink; /* ====================================== entity HomeFindTarget() MG Simply looks for and returns a target to go after. ====================================== */ entity HomeFindTarget() { entity loser; float dist, bestdist; if(self.think!=HomeThink)//one-time only acquisition bestdist=5000; else bestdist=1000; loser=findradius(self.origin,bestdist); bestdist+=1; while (loser) { if(loser.health&&loser.takedamage&&(loser.flags2&FL_ALIVE)&&visible(loser)&&loser!=self&&loser!=world&&loser!=self.owner&&!loser.effects&EF_NODRAW)//&&!(loser.artifact_active&ARTFLAG_STONED) Why Not? { if((!self.aflag||self.ideal_yaw)&&!ahead(loser,self)) //looks for someone in front first time dprint("");//not infront\n"); else if(teamplay&&loser.classname=="player"&&((loser.team==self.owner.team&&self.owner.classname=="player")||(loser.team==self.controller.team&&self.owner.classname=="player"))) dprint("");//targeting teammate\n"); else if(coop&&loser.classname=="player"&&(self.owner.classname=="player"||self.controller.classname=="player")) dprint("");//target coop player\n"); else if((self.classname=="flame arrow"||self.classname=="bolt")&&deathmatch&&vlen(loser.velocity)>300) dprint("");//DM: player moving too fast\n"); else if(loser.controller==self.owner&&self.owner.classname=="player")//don't home in on owner's imps dprint("");//owner's thing\n"); else { //make it wait for closest (by vlen) or just go for first found? dist=vlen(self.origin-loser.origin); if(dist150) { 8; // dprint("");//DM: player moving too fast\n"); } else if((who.classname=="chainball")&&!ahead(loser,who))//only look ahead if yer the scarab staff { 8; // dprint("");//not infront\n"); } else if(loser.controller==who.owner&&who.owner.classname=="player")//don't home in on owner's imps { 8; // dprint("");//owner's thing\n"); } else { //make it wait for closest (by vlen) or just go for first found? dist=vlen(who.origin-loser.origin); if(dist