agrip/nav.mqc

Go to the documentation of this file.
00001 /*  Copyright 2004 Matthew Tylee Atkinson
00002 
00003     This program is free software; you can redistribute it and/or modify
00004     it under the terms of the GNU General Public License as published by
00005     the Free Software Foundation; either version 2 of the License, or
00006     (at your option) any later version.
00007 
00008     This program is distributed in the hope that it will be useful,
00009     but WITHOUT ANY WARRANTY; without even the implied warranty of
00010     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011     GNU General Public License for more details.
00012 
00013     You should have received a copy of the GNU General Public License
00014     along with this program; if not, write to the Free Software
00015     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00016 
00017     See file, 'COPYING', for details.
00018 */
00019 
00020 /* $AGRIP-START */
00021 /* AGRIP Navigation Assistant Object */
00022 
00023 
00024 // CONSTANTS
00025 
00026 // <defined in agrip-misc.qc>
00027 
00028 
00029 // PROTOTYPES
00030 
00031 void snap_nav_constructor();
00032 void snap_nav_main();
00033 
00034 void snap_nav_struct_main();
00035 float snap_nav_struct_detect(float dir, vector v_start, vector v_out, vector v_out_up);
00036 void snap_nav_struct_soundout(float dir, float objtype);
00037 
00038 void snap_nav_haz_main();
00039 float snap_nav_haz_detect(float fwd, vector v_start, vector v_dir, float reverse, float snd);
00040 
00041 float snap_nav_z_main();
00042 
00043 void snap_nav_corners_main();
00044 float snap_nav_corners_check(vector v_cmid, vector v_coff);
00045 
00046 
00047 // IMPLEMENTATIONS
00048 
00049 // OBJECT MANAGEMENT FUNCTIONS
00050 
00058 void snap_nav_constructor()
00059 {
00060     local entity new_agnav, child_drop;
00061 
00062     // Let there be light!        
00063     new_agnav = spawn();
00064         
00065     // Set up new nav object...
00066     new_agnav.movetype = MOVETYPE_NONE;
00067     new_agnav.solid = SOLID_NOT;
00068         
00069     /*  Give it the preferences the user specified...
00070         To save memory, we're using existing (and empty for this entity) fields... */
00071     
00072     // Interval (seconds) between uses of the nav object:
00073     new_agnav.health = 0.5;
00074     
00075     // Detection range:
00076     new_agnav.frags = stof(infokey(self, "agv_nav_detrange"));
00077     // AG_FIXME: error check on nav detrange?
00078     
00079     // Volume throttle for the wall sounds...
00080     new_agnav.ammo_rockets = stof(infokey(self, "agv_nav_wall_volume_throttle"));
00081     if( new_agnav.ammo_rockets > 1 || new_agnav.ammo_rockets < 0 )
00082         new_agnav.ammo_rockets = 1;
00083 
00084     // Volume throttle for the wall-touch sounds...
00085     new_agnav.ammo_shells = stof(infokey(self, "agv_nav_wall_touch_volume_throttle"));
00086     if( new_agnav.ammo_shells > 1 || new_agnav.ammo_shells < 0 )
00087         new_agnav.ammo_shells = 1;
00088 
00089     // Volume throttle for the ledge/drop sounds...
00090     new_agnav.skin = stof(infokey(self, "agv_nav_haz_volume_throttle"));
00091     if( new_agnav.skin > 1 || new_agnav.skin < 0 )
00092         new_agnav.skin = 1;
00093 
00094     // Volume throttle for the corner sounds...
00095     new_agnav.lip = stof(infokey(self, "agv_nav_corner_volume_throttle"));
00096     if( new_agnav.lip > 1 || new_agnav.lip < 0 )
00097         new_agnav.lip = 1;
00098 
00099     // Connect the nav object and player...
00100     new_agnav.owner = self;
00101     self.agrip_nav = new_agnav;
00102 
00103     // Set up the child entity that is the drop marker...
00104     child_drop = spawn();
00105     child_drop.movetype = MOVETYPE_NONE;
00106     child_drop.solid = SOLID_NOT;
00107     setmodel(child_drop, "progs/s_bubble.spr");
00108     child_drop.classname = "agrip_hazard";
00109     child_drop.owner = self.agrip_nav;
00110     self.agrip_nav.aiment = child_drop;
00111     self.agrip_nav.view_ofs = '0 0 0';
00112 
00113     // Set previous run time for CAOSD...
00114     new_agnav.wait = time;
00115 
00116     // Tell it there weren't any corners yet...
00117     self.count = 0;
00118     self.state = 0;
00119 
00120     // Give it life!
00121     new_agnav.think = snap_nav_main;
00122     new_agnav.nextthink = time + 0.1;
00123 };
00124 
00128 void snap_nav_main()
00129 {
00130     local float z_changed;
00131     
00132     // Find walls, slopes and doors...
00133     snap_nav_struct_main();
00134     
00135     // Tell the player if they're moving up/down...
00136     z_changed = snap_nav_z_main();
00137 
00138     // Find drops, water, slime, lava and jumps...
00139     // DO NOT detect ledges if we're moving up/down, though!
00140     if( z_changed != true )
00141     if( infokey(self.owner, "agv_t_nav_haz_warnings") == "1" )
00142         snap_nav_haz_main();
00143 
00144     // Find corners, etc...
00145     snap_nav_corners_main();
00146 
00147     // Finished this scan... when do we do it all again?
00148     self.nextthink = time + self.health;
00149 };
00150 
00151 
00152 // WALLS, SLOPES and DOORS DETECTION FUNCTIONS
00153 
00159 void snap_nav_struct_main()
00160 {
00161     // Declare the vectors to store the various points we are going to test for obstacles 
00162     local vector    v_start,
00163                     v_nav_fwd, v_nav_fwd_up,
00164                     v_nav_lft, v_nav_lft_up,
00165                     v_nav_rgt, v_nav_rgt_up,
00166                     v_nav_bck, v_nav_bck_up;
00167 
00168     // Store the type of object that was detected in a given direction...
00169     local float     objtype;
00170         
00171     // Get the correct starting point for the vectors...
00172     // (owner origin not appropriate as it is too high and thus confuses the detection)
00173     v_start = self.owner.origin - '0 0 20';
00174 
00175     // Calculate the vectors along which we need to `look'...
00176     // (We need two so that we can try to find SLOPE/slopes.)
00177     makevectors(self.owner.angles);
00178     // Forward vectors
00179     v_nav_fwd = v_start + self.frags * v_forward;
00180     v_nav_fwd_up = v_nav_fwd + ( 50 * v_up );
00181     // Left vectors
00182     v_nav_lft = v_start - self.frags * v_right;
00183     v_nav_lft_up = v_nav_lft + ( 50 * v_up );
00184     // Right vectors
00185     v_nav_rgt = v_start + self.frags * v_right;
00186     v_nav_rgt_up = v_nav_rgt + ( 50 * v_up );
00187     // Back vectors
00188     v_nav_bck = v_start - self.frags * v_forward;
00189     v_nav_bck_up = v_nav_bck + ( 50 * v_up );
00190 
00191     // Now detect and sound stuff...
00192    
00193     // Forward and Back...
00194     objtype = snap_nav_struct_detect(SNAP_NAV_DIR_FWD, v_start, v_nav_fwd, v_nav_fwd_up);
00195     snap_nav_struct_soundout(SNAP_NAV_DIR_FWD, objtype);
00196     //dprint("\nNAV: f = ");dprint(ftos(objtype));
00197     objtype = snap_nav_struct_detect(SNAP_NAV_DIR_BCK, v_start, v_nav_bck, v_nav_bck_up);
00198     snap_nav_struct_soundout(SNAP_NAV_DIR_BCK, objtype);
00199     //dprint("  b = ");dprint(ftos(objtype));
00200 
00201     // Left and Right...    
00202     objtype = snap_nav_struct_detect(SNAP_NAV_DIR_LFT, v_start, v_nav_lft, v_nav_lft_up);
00203     snap_nav_struct_soundout(SNAP_NAV_DIR_LFT, objtype);
00204     //dprint("  l = ");dprint(ftos(objtype));
00205     objtype = snap_nav_struct_detect(SNAP_NAV_DIR_RGT, v_start, v_nav_rgt, v_nav_rgt_up);
00206     snap_nav_struct_soundout(SNAP_NAV_DIR_RGT, objtype);
00207     //dprint("  r = ");dprint(ftos(objtype));dprint("\n");
00208 };
00209 
00219 float snap_nav_struct_detect(float dir, vector v_start, vector v_out, vector v_out_up)
00220 {
00221     local float Xout, Xoutup, Yout, Youtup;
00222     local float tracedown_half_out, tracedown_quarter_out, tracedown_nearplr_out;
00223     local vector v_test; // used to test if we're on a slope so we don't get stuff wrong.
00224     
00225     // calc v_out_up's endpos...
00226     // Do this trace from higher up than the next one, so that we can detect
00227     // SLOPE and ramps when they are very close to the player...
00228     traceline(v_start + '0 0 15', v_out_up, true, self.owner);
00229     //snap_misc_showpoint(trace_endpos, "progs/s_bubble.spr", 0.5);
00230     Xoutup = trace_endpos_x;
00231     Youtup = trace_endpos_y;
00232 
00233     // Did we find a door?
00234     if( trace_fraction != 1 )
00235     {
00236         if( trace_ent.classname == "door" )
00237         {
00238             self.volume = 1 - trace_fraction;
00239             return SNAP_NAV_STRUCT_DOOR;
00240         }
00241     }
00242 
00243     // If we already know we're touching a wall, don't do anything...
00244     // (The reason we waited to do this was so that if it was a door, we'd
00245     // keep saying it was.)
00246     if( self.owner.agrip_aux.ammo_nails & dir )
00247     {
00248         self.volume = 1 - trace_fraction;
00249         return SNAP_NAV_STRUCT_WALL;
00250     }
00251 
00252     // Calculate v_out_'s endpos...
00253     traceline(v_start, v_out, true, self.owner);
00254     //snap_misc_showpoint(trace_endpos, "progs/s_bubble.spr", 0.5);
00255     Xout = trace_endpos_x;
00256     Yout = trace_endpos_y;
00257 
00258     // Prepare the test for us being on a slope...
00259     /*v_test = v_out - v_start;
00260     v_test = normalize(v_test);
00261     v_test = v_start + 20*v_test - '0 0 5';*/
00262     // (We do 20*v_test to account for the player being on steps but over a gap.)
00263     /*local float test;
00264     test = pointcontents(v_test);
00265     dprint("pc: ");dprint(ftos(test));
00266     dprint("  so: ");dprint(vtos(self.owner.origin));
00267     dprint("  po: ");dprint(vtos(v_test));
00268     dprint("\n");*/
00269     
00270     // Set sound volume (this will be used for slopes and walls, but
00271     // overriden if the object is a downward slope)...
00272     self.volume = 1 - trace_fraction;
00273     
00274     // Two situations for an up slope:
00275     // the two x's are not the same OR the two y's are not the same...
00276     if(     (Xout != Xoutup && Yout == Youtup )
00277         ||  (Yout != Youtup && Xout == Xoutup ) )
00278     {
00279         return SNAP_NAV_STRUCT_SLOPE;   // one endpoint further back.
00280     }
00281     else if(    trace_fraction < 0.5
00282              && Xout == Xoutup && Yout == Youtup )
00283     {
00284         // It's a wall and it's within the wall sounding distance...
00285         return SNAP_NAV_STRUCT_WALL;
00286     }
00287     else
00288     // See if there is a downward slope...
00289     // We will do this by seeing if the trace_fraction is greater the further
00290     // away from the player we trace...
00291     {
00292         // Do a trace vertically down from (1/2)t_out...
00293         v_out = v_out - v_start;
00294         v_out = v_out * 0.5;
00295         v_out = v_start + v_out;
00296         traceline(v_out, v_out - '0 0 100', true, self);
00297         //snap_misc_showpoint(trace_endpos, "progs/s_bubble.spr", 0.5);
00298         tracedown_half_out = trace_fraction;
00299 
00300         // Do a trace vertically down from (1/4)t_out...
00301         v_out = v_out - v_start;
00302         v_out = v_out * 0.5;
00303         v_out = v_start + v_out;
00304         traceline(v_out, v_out - '0 0 100', true, self);
00305         //snap_misc_showpoint(trace_endpos, "progs/s_bubble.spr", 0.5);
00306         tracedown_quarter_out = trace_fraction;
00307 
00308         // It could be a drop...
00309         if( trace_fraction == 1 )
00310             return;
00311 
00312         // Do a trace vertically down from right near the player...
00313         v_out = normalize(v_out - v_start);
00314         v_out = v_start + 20*v_out;
00315         traceline(v_out, v_out - '0 0 100', true, self);
00316         //snap_misc_showpoint(trace_endpos, "progs/s_bubble.spr", 0.5);
00317         tracedown_nearplr_out = trace_fraction;
00318 
00319         // It could be a drop...
00320         if( trace_fraction == 1 )
00321             return;
00322 
00323         // If there is a downward slope,
00324         // tracedown_quarter_out will be < tracedown_half_out...
00325         // (We need some tolerance in there, though.)
00326         
00327         // Tolerance...
00328         // (the tolerance is 5 units)
00329         if( fabs(tracedown_half_out - tracedown_quarter_out) < 0.05 )
00330             tracedown_half_out = tracedown_quarter_out;
00331         if( fabs(tracedown_nearplr_out - tracedown_quarter_out) < 0.05 )
00332             tracedown_nearplr_out = tracedown_quarter_out;
00333 
00334         // Debug info
00335         /*if( dir = SNAP_NAV_DIR_FWD )
00336         {
00337             dprint("\nSLP: ");
00338             dprint(ftos(tracedown_nearplr_out));dprint(" ");
00339             dprint(ftos(tracedown_quarter_out));dprint(" ");
00340             dprint(ftos(tracedown_half_out));dprint(" ");
00341             dprint("\n");
00342         }*/
00343         
00344         // Work out if there is a slope...
00345         if(     (       (tracedown_nearplr_out < tracedown_quarter_out)
00346                     &&  (tracedown_quarter_out < tracedown_half_out) )
00347             ||  (       (tracedown_nearplr_out == tracedown_quarter_out)
00348                     &&  (tracedown_quarter_out < tracedown_half_out) )
00349             ||  (       (tracedown_nearplr_out < tracedown_quarter_out) 
00350                     &&  (tracedown_quarter_out == tracedown_half_out) ) )
00351         {
00352             self.volume = 1 - ( vlen(trace_endpos - v_start) / self.frags );
00353             return SNAP_NAV_STRUCT_SLOPE_DOWN;
00354         }
00355     }
00356 };
00357 
00368 void snap_nav_struct_soundout(float direction, float objtype)
00369 {
00370     local float actual_sound_level;
00371 
00372     // Set myself up in the right place to make the sound...
00373     setorigin(self, trace_endpos);
00374 
00375     // Debug info - front object...
00376     /*if( direction == SNAP_NAV_DIR_FWD )
00377     {
00378         dprint("Object in front is a: ");dprint(ftos(objtype));dprint("\n");
00379     }*/
00380     
00381     // Work out what sound to make...
00382     if( objtype == SNAP_NAV_STRUCT_WALL )
00383     {
00384         // It's a WALL...
00385         // Do we sound it out?
00386         
00387         // Have we just hit it?
00388         if( self.owner.agrip_aux.ammo_shells & direction )
00389         {
00390             // Yes -- make the wall-hit sound and unset the ``just hit this'' flag...
00391             actual_sound_level = 1;
00392             safe_soundtoclient(self.owner, self, CHAN_AUTO, "player/land.wav", actual_sound_level, ATTN_NORM);
00393             self.owner.agrip_aux.ammo_shells = self.owner.agrip_aux.ammo_shells - (self.owner.agrip_aux.ammo_shells & direction);
00394         }
00395         // Perhaps we're still touching it after hitting it a while back...
00396         else if( self.owner.agrip_aux.ammo_nails & direction )
00397         {
00398             // Make the wall touch sound?
00399             if( infokey(self.owner, "agv_t_nav_wall_touch_warnings") == "1" )
00400             {
00401                 // Apply throttle for wall-touch sounds...
00402                 actual_sound_level = 1 * self.ammo_shells;
00403                     
00404                 // If the wall is NOT in front us, make it a bit quieter...
00405                 if( direction != SNAP_NAV_DIR_FWD )
00406                     actual_sound_level = actual_sound_level * 0.5;
00407             
00408                 // Make the wall-touch sound...
00409                 // AG_FIXME: wall-touch sound
00410                 safe_soundtoclient(self.owner, self, CHAN_AUTO, "misc/menu3.wav", actual_sound_level, ATTN_NORM);
00411             }
00412         }
00413         // OK, well maybe the wall is within our wall detection range...
00414         else if( trace_fraction < 0.5 )
00415         {
00416             // Sound volume should fall off as if the wall detection range was half
00417             // of what the other nav objects' detection range is...
00418             actual_sound_level = self.volume * self.ammo_rockets;
00419       
00420             // Sound out the wall?
00421             if(     infokey(self.owner, "agv_t_nav_wall_warnings") == "1"
00422                 &&  direction != SNAP_NAV_DIR_BCK
00423                 &&  (
00424                           infokey(self.owner, "agv_t_nav_side_wall_warnings") == "1"
00425                       ||  (     
00426                                 direction != SNAP_NAV_DIR_LFT
00427                             &&  direction != SNAP_NAV_DIR_RGT
00428                           )
00429                     )
00430               )
00431                 safe_soundtoclient(self.owner, self, CHAN_AUTO, "nav/wall.wav", actual_sound_level, ATTN_NORM);
00432         }
00433         // END of WALL sounding stuff.
00434     }
00435     else
00436     {
00437         // It's a SLOPE or DOOR...
00438         // Sound volume should fall off in direct proportion to the trace farction...
00439         // But:     Don't make a sound if we're only detecting what is behind us
00440         // Note:    ALWAYS detect slopes/doors at our sides, even if side wall
00441         //          warnings is off!
00442         if( direction != SNAP_NAV_DIR_BCK )
00443         {
00444             // Get sound volume...
00445             actual_sound_level = self.volume;
00446             // (The throttle is only for walls.)
00447             
00448             // Sound out ramps, SLOPE and doors...
00449             if( objtype == SNAP_NAV_STRUCT_SLOPE )
00450             {
00451                 safe_soundtoclient(self.owner, self, CHAN_AUTO, "nav/slope.wav", actual_sound_level, ATTN_NORM);
00452             }
00453             else if( objtype == SNAP_NAV_STRUCT_SLOPE_DOWN )
00454             {
00455                 safe_soundtoclient(self.owner, self, CHAN_AUTO, "nav/slope.wav", actual_sound_level, ATTN_NORM);
00456             }
00457             else if( objtype == SNAP_NAV_STRUCT_DOOR )
00458             {
00459                 safe_soundtoclient(self.owner, self, CHAN_AUTO, "nav/door.wav", actual_sound_level, ATTN_NORM);
00460             }
00461         }
00462     }
00463 };
00464 
00465 
00466 // DROPS, WATER, SLIME, LAVA and JUMPS DETECTION FUNCTIONS
00467 
00480 void snap_nav_haz_main()
00481 {
00482     local vector    v_start;
00483     local float     haztype;
00484 
00485     // Get the correct starting point for the vectors...
00486     // (owner origin not appropriate as it is too high and thus confuses the detection)
00487     v_start = self.owner.origin - '0 0 25';
00488 
00489     // Now detect and sound stuff...
00490     // (For forward, left, right and back directions.)
00491     
00492     // Forward
00493     // Should the ESR be making the sound?
00494     if( infokey(self.owner, "agv_t_esr") == "2" )
00495         haztype = snap_nav_haz_detect(true, v_start, v_forward, false, false);
00496     else
00497         haztype = snap_nav_haz_detect(true, v_start, v_forward, false, true);
00498 
00499     // NB: self.aiment (the hazard marker) is only set up if we are detecting
00500     //     in front of the player.  If there was a hazard, it's origin will now
00501     //     be set and it will have it's state set to 1.
00502     //     The following calls do not interfere with this.
00503     
00504     // Do the other dirs if we've been told to...
00505     if( infokey(self.owner, "agv_t_nav_side_haz_warnings") == "1" )
00506     {
00507         // Left
00508         snap_nav_haz_detect(false, v_start, v_right, true, true);
00509         // Right
00510         snap_nav_haz_detect(false, v_start, v_right, false, true);
00511     }
00512 
00519     self.weapon = haztype;
00520     
00525     if( self.aiment.state == 1 )
00526         self.takedamage = snap_misc_jumptest(self.aiment.origin, self.aiment.movedir);
00527     else
00528         self.takedamage = 0;
00529 };
00530      
00562 float snap_nav_haz_detect(float fwd, vector v_start, vector v_dir, float reverse, float snd)
00563 {
00564     local vector v_end;
00565     local float ledge_dist, drop_length, soundout_volume;
00566     local float retval;
00567 
00574     // Get owner's pointy vectors...
00575     makevectors(self.owner.angles);
00576    
00577     // Start off looking 10 units further away from the player in whatever
00578     // direction we are scanning for ledges in...
00579     v_end = v_start;
00580     ledge_dist = vlen(v_end - v_start);
00581 
00582     // Iteratively work out if there is a drop in front of our intrepid hero...
00583     while( ledge_dist < ((self.frags / 2) - 10) && pointcontents(v_end) == CONTENT_SOLID )
00584     {
00585         ledge_dist = vlen(v_end - v_start);
00586 
00587         // We need this hack becuase QC can't deal with passing in negative
00588         // numbers as parameters...
00589         if( ! reverse )
00590             v_end = v_end + 10 * v_dir;
00591         else
00592             v_end = v_end - 10 * v_dir;
00593     }
00594 
00595     
00605     traceline(self.owner.origin, (v_end + '0 0 40'), true, self.owner);
00606     if(    trace_fraction == 1
00607         // Check we're not under a raised floor such as a bridge, for example...
00608         && pointcontents(v_end + '0 0 10') != CONTENT_SOLID
00609       )
00610     {
00612        
00613         // Make sure it's not just a small gap (at a plat, for example)...
00614         traceline(v_end, v_end + 10 * v_dir, true, self.owner);
00615         /*dprint("test -- tf: ");dprint(ftos(trace_fraction));
00616         dprint("  as:");dprint(ftos(trace_allsolid));dprint("\n");*/
00617         if(     ! trace_allsolid
00618             ||  trace_fraction != 1 )
00619         {
00642             // Prepare for Descent...
00643             traceline(v_end, (v_end - '0 0 800') , true, self.owner);
00644     
00645             // How big is the drop?
00646             drop_length = vlen(trace_endpos - v_end);
00647    
00648             
00651             
00652             if( snd )
00653             {
00654                 // Get into the right place...
00655                 setorigin(self, v_end);
00656                 
00659                 
00660                 // Calculate ledge_dist as a percentage of the detection range...
00661                 soundout_volume = ledge_dist / (self.frags / 2);
00662                 // Invert and throttle down soundout_volume...
00663                 soundout_volume = ( 1 - soundout_volume ) * self.skin;
00664                 //dprint("soundout volume: ");dprint(ftos(soundout_volume));dprint("\n");
00665 
00666                 // FIXME: Make it do a different version of each drop height sound if the player is on the edge!
00667                 
00668                 // Classify as SMALL, BIG or HUGE...
00669                 if( drop_length > 275 )
00670                 {
00671                     safe_soundtoclient(self.owner, self, CHAN_AUTO, "haz/drop-huge.wav", soundout_volume, ATTN_NORM);
00672                 }
00673                 else if( drop_length > 39 )
00674                 {
00675                     safe_soundtoclient(self.owner, self, CHAN_AUTO, "haz/drop-big.wav", soundout_volume, ATTN_NORM);
00676                 }
00677                 else if( drop_length > 16 )
00678                 {
00679                     safe_soundtoclient(self.owner, self, CHAN_AUTO, "haz/drop-small.wav", soundout_volume, ATTN_NORM);
00680                 }
00681             }
00682 
00690             if( drop_length > 16 )
00691             {
00692                 if( ledge_dist < 150 )
00693                 {
00694                     // Set to return drop contents...
00695                     retval = pointcontents(trace_endpos);
00696                 }
00697                 else
00698                 {
00699                     // Too far away to ``see'' contents...
00700                     retval = 0;
00701                 }
00702             }
00703             else
00704             {
00705                 // Drop not big enough...
00706                 retval = 10;
00707             }
00708         }
00709         else
00710         {
00711             //dprint("HAZ: drop not valid\n");
00712             retval = 255;
00713         }
00714     }
00715     else
00716     {
00717         //dprint("HAZ: not visible\n");
00718         retval = 255;
00719     }
00720 
00723     if( fwd)
00724     if( retval < 20 )
00725     {
00726         setorigin( self.aiment, (v_end + '0 0 40') );
00727         self.aiment.state = 1;
00728     }
00729     else
00730     {
00731         self.aiment.state = 0;
00732     }
00733 
00734     return retval;
00735 };
00736 
00737 
00738 // Z DETECTION FUNCTION
00739 
00746 float snap_nav_z_main()
00747 {
00748     local float z_changed;
00749 
00750     // Assume we have moved since the last time this was run...
00751     z_changed = true;
00752     
00760     if( infokey(self.owner, "agv_t_nav_z_warnings") == "1" )
00761     {
00762         // Compare previous Z location to current one and make an appropriate
00763         // sound...
00764         if( self.owner.origin_z > self.frame + 5 )
00765         {
00766             safe_soundtoclient(self.owner, self.owner, CHAN_AUTO, "nav/up.wav", 1, ATTN_NORM);
00767         }
00768         else if( self.owner.origin_z < self.frame - 5 )
00769         {
00770             safe_soundtoclient(self.owner, self.owner, CHAN_AUTO, "nav/down.wav", 1, ATTN_NORM);
00771         }
00772         else
00773             z_changed = false;
00774 
00775         // Set our owner's current Z location as the ``previous'' one...
00776         self.frame = self.owner.origin_z;
00777     }
00778     else
00779     {
00780         // Store our current Z location as ``prev Z location''...
00781         // (This avoids un-necessary blips at the on/off transitions)
00782         self.frame = self.owner.origin_z;
00783     }
00784 
00785     return z_changed;
00786 };
00787 
00788 
00789 // CORNER FINDING STUFF
00790 
00796 void snap_nav_corners_main()
00797 {
00798     // Should we be here?
00799     if(     ! time > self.wait
00800         ||  ! infokey(self.owner, "agv_t_nav_corner_warnings") == "1" )
00801         return;
00802 
00803     local float corner_dist, found_left_corner, found_right_corner;
00804     local float corner_vol, midpoint_vol;
00805     local vector v_start, v_corner_fwd, v_corner_left, v_corner_right, v_iL, v_iR;
00806     local float passed_left_corner;
00807     passed_left_corner = false;
00808 
00817     // It really is as simple as that, people (what am I on?)!
00818 
00819     makevectors(self.owner.angles);
00820     v_start = self.owner.origin;
00821     v_corner_fwd = v_start;
00822     corner_dist = 0;
00823     found_left_corner = false;
00824     found_right_corner = false;
00825     
00826     // Iteratively work out if there is a gap for a corner in front of Quake guy...
00827     while(      corner_dist < ( self.frags - 10 )
00828            &&   (    pointcontents(v_corner_fwd) == CONTENT_EMPTY
00829                   || pointcontents(v_corner_fwd) == CONTENT_WATER )
00830          )
00831     {
00832         // Find a corner to the left...
00833         if( ! found_left_corner )
00834         {
00835             v_corner_left = v_corner_fwd - (self.frags/2) * v_right;
00836             found_left_corner = snap_nav_corners_check(v_corner_fwd, v_corner_left);
00837             v_iL = v_corner_fwd;
00838         }
00839 
00840         // Find a corner to the right...
00841         if( ! found_right_corner )
00842         {
00843             v_corner_right = v_corner_fwd + (self.frags/2) * v_right;
00844             found_right_corner = snap_nav_corners_check(v_corner_fwd, v_corner_right);
00845             v_iR = v_corner_fwd;
00846         }
00847     
00848         // Done, go to the next point...
00849         v_corner_fwd = v_corner_fwd + 10 * v_forward;
00850         corner_dist = vlen(v_corner_fwd - v_start);
00851     }
00852 
00867     if( found_left_corner )
00868     {
00869         // Debug info -- show how far it was...
00870         //snap_misc_showpoint(v_iL, "progs/s_explod.spr", 1.5);
00871         //snap_misc_showpoint(v_corner_left, "progs/s_light.spr", 10);
00872         //dprint("found left corner!  sv = ");
00873 
00874         // Sound it out...
00875 
00876         // Work out corner volume...
00877         corner_vol = 1 - (vlen(v_iL - self.owner.origin) / self.frags)*self.lip;
00878         if( corner_vol > 1 )
00879             corner_vol = 1;
00880         else if( corner_vol < 0 )
00881             corner_vol = 0;
00882         //dprint("left: ");dprint(ftos(corner_vol));dprint("\n");
00883        
00884         // Kick off the sounder...
00885         local entity sounderL;
00886         sounderL = spawn();
00887         sounderL.message = "weapons/tink1.wav";
00888         sounderL.dest1 = v_corner_fwd;
00889         sounderL.frags = corner_vol;
00890         sounderL.dest2 = v_corner_left;
00891         sounderL.health = corner_vol;
00892         sounderL.owner = self.owner;
00893         sounderL.think = snap_se_cornersound;
00894         sounderL.nextthink = time + 0.01;
00895 
00896         // Flag we found the corner...
00897         // NOT if we have immediately found a new one after losing the old,
00898         // one, though!
00899         if( self.count == true && self.pos1 != v_corner_left )
00900         {
00901             // Make sure that the distance between the two is big
00902             // enough to be significant...
00903             if( vlen(v_corner_left - self.pos1) > 20 )
00904                 self.count = false;
00905         }
00906         else
00907         {
00908             self.count = true;
00909             self.pos1 = v_corner_left;
00910             self.finaldest = v_iL;
00911             self.dest1 = v_forward;
00912         }
00913     }
00914 
00915     if( found_right_corner )
00916     {
00917         // Debug info -- show how far it was...
00918         //snap_misc_showpoint(v_iR, "progs/s_explod.spr", 1.5);
00919         //snap_misc_showpoint(v_corner_right, "progs/s_light.spr", 10);
00920         //dprint("found right corner!  sv = ");
00921 
00922         // Sound it out...
00923 
00924         // Work out corner volume...
00925         corner_vol = 1 - (vlen(v_iR - self.owner.origin) / self.frags)*self.lip;
00926         if( corner_vol > 1 )
00927             corner_vol = 1;
00928         else if( corner_vol < 0 )
00929             corner_vol = 0;
00930         //dprint("right: ");dprint(ftos(corner_vol));dprint("\n");
00931        
00932         // Kick off the sounder...
00933         local entity sounderR;
00934         sounderR = spawn();
00935         sounderR.message = "weapons/tink1.wav";
00936         sounderR.dest1 = v_corner_fwd;
00937         sounderR.frags = corner_vol;
00938         sounderR.dest2 = v_corner_right;
00939         sounderR.health = corner_vol;
00940         sounderR.owner = self.owner;
00941         sounderR.think = snap_se_cornersound;
00942         if( found_left_corner )
00943             sounderR.nextthink = time + 0.5;
00944         else
00945             sounderR.nextthink = time + 0.01;
00946 
00947         // Flag we found the corner...
00948         // NOT if we have immediately found a new one after losing the old,
00949         // one, though!
00950         if( self.state == true && self.pos2 != v_corner_right )
00951         {
00952             // Make sure that the distance between the two is big
00953             // enough to be significant...
00954             if( vlen(v_corner_right - self.pos2) > 20 )
00955                 self.state = false;
00956         }
00957         else
00958         {
00959             self.state = true;
00960             self.pos2 = v_corner_right;
00961             self.finalangle = v_iR;
00962             self.dest2 = v_forward;
00963         }
00964     }
00965     
00968 
00969     // Left corners...
00970     if( self.count != found_left_corner )
00971     if( self.dest1 == v_forward )
00972     {
00973         // Indicate we've just passed a corner on whichever side it was...
00974         local entity passed_corner_snder;
00975         local float sound_level;
00976         sound_level = 1 - (vlen(self.finaldest - self.owner.origin) / self.frags)*self.lip;
00977         if( sound_level > 0 )
00978         if( sound_level <= 1 )
00979         {
00980             passed_corner_snder = spawn();
00981             passed_corner_snder.message = "weapons/ric2.wav";
00982             passed_corner_snder.dest1 = self.finaldest;
00983             passed_corner_snder.frags = sound_level;
00984             passed_corner_snder.dest2 = self.pos1;
00985             passed_corner_snder.health = sound_level;
00986             passed_corner_snder.owner = self.owner;
00987             passed_corner_snder.think = snap_se_cornersound;
00988             passed_corner_snder.nextthink = time + 0.01;
00989         }
00990         self.count = false;
00991 
00992         // If we need to tell the player they've gone past a right corner too,
00993         // we set this flag to make sure the sounds don't occur at the same
00994         // time as each other (just like the corner sounding above)...
00995         passed_left_corner = true;
00996     }
00997 
00998     // Right corners...
00999     if( self.state != found_right_corner )
01000     if( self.dest2 == v_forward )
01001     {
01002         // Indicate we've just passed a corner on whichever side it was...
01003         local entity passed_corner_snder;
01004         local float sound_level;
01005         sound_level = 1 - (vlen(self.finalangle - self.owner.origin) / self.frags)*self.lip;
01006         if( sound_level > 0 )
01007         if( sound_level <= 1 )
01008         {
01009             passed_corner_snder = spawn();
01010             passed_corner_snder.message = "weapons/ric2.wav";
01011             passed_corner_snder.dest1 = self.finalangle;
01012             passed_corner_snder.frags = sound_level;
01013             passed_corner_snder.dest2 = self.pos2;
01014             passed_corner_snder.health = sound_level;
01015             passed_corner_snder.owner = self.owner;
01016             passed_corner_snder.think = snap_se_cornersound;
01017             if( passed_left_corner )
01018                 passed_corner_snder.nextthink = time + 0.5;
01019             else
01020                 passed_corner_snder.nextthink = time + 0.01;
01021         }
01022         self.state = false;
01023     }
01024 
01025     // Set prevoius run time...
01026     if( found_left_corner )
01027     if( found_right_corner )
01028         self.wait = time + 1;
01029     else
01030         self.wait = time + 0.5;
01031 };
01032 
01047 float snap_nav_corners_check(vector v_cmid, vector v_coff)
01048 {
01049     local float retval, length;
01050     local vector v_cornerpoint, v_m2c_dir;
01051 
01052     // Check the mid-point can see the corner point...
01053     // NOTE:    We specify 1/2 for the trace fraction required so that sharper
01054     //          turns can be detected.
01055     traceline(v_cmid, v_coff, true, self);
01056     length = vlen(trace_endpos - v_cmid);
01057     v_cornerpoint = trace_endpos;
01058     if( length > 160 )  // Was (self.frags/4).  Changed to stop scaling affecting it badly.
01059     {
01060         // We need to ``move'' this point as it is probably up against a wall.
01061         // This should give it a _chance_ of passing the test for ``can you fit
01062         // the player in there?''...
01063 
01064         // Find out the direction the trace was in...
01065         v_m2c_dir = normalize(v_coff - v_cmid);
01066         // Subtract this a bit from the trace_endpos...
01067         v_cornerpoint = v_cornerpoint - 30 * v_m2c_dir;
01068 
01069         // Test if we can see the point from where we are
01070         // and if the player could fit there.
01071         traceline(self.owner.origin, v_cornerpoint, true, self);
01072         if(     trace_fraction != 1
01073             &&  (    pointcontents(v_cornerpoint + self.owner.mins) == CONTENT_EMPTY
01074                   || pointcontents(v_cornerpoint + self.owner.mins) == CONTENT_WATER )
01075             &&  (    pointcontents(v_cornerpoint + self.owner.maxs) == CONTENT_EMPTY 
01076                   || pointcontents(v_cornerpoint + self.owner.maxs) == CONTENT_WATER )
01077           )
01078             //&&  trace_ent.classname != "door" )
01079         {
01080             // Make sure the player would be on the ground at the corner, or
01081             // that the drop would be small...
01082             traceline(v_cornerpoint, v_cornerpoint - '0 0 50', true, self);
01083             if( trace_fraction != 1 )
01084             {
01085                 // Also test that the player could physically get there...
01086                 traceline(v_cmid, v_cornerpoint + '0 0 20', true, self);
01087                 if( trace_fraction == 1 )
01088                 {
01089                     retval = true;
01090                     //snap_misc_showpoint(v_cornerpoint, "progs/s_explod.spr", 4);
01091                 }
01092             }
01093         }
01094     }
01095     else
01096         retval = false;
01097 
01098     return retval;
01099 };
01100 
01101 /* $AGRIP-END */

Generated on Tue Jan 1 17:55:54 2008 for AudioQuake QuakeC by  doxygen 1.5.4