#include maps\_utility; #include common_scripts\utility; #using_animtree( "generic_human" ); init_mgTurretsettings() { level.mgTurretSettings[ "easy" ][ "convergenceTime" ] = 2.5; level.mgTurretSettings[ "easy" ][ "suppressionTime" ] = 3.0; level.mgTurretSettings[ "easy" ][ "accuracy" ] = 0.38; level.mgTurretSettings[ "easy" ][ "aiSpread" ] = 2; level.mgTurretSettings[ "easy" ][ "playerSpread" ] = 0.5; level.mgTurretSettings[ "medium" ][ "convergenceTime" ] = 1.5; level.mgTurretSettings[ "medium" ][ "suppressionTime" ] = 3.0; level.mgTurretSettings[ "medium" ][ "accuracy" ] = 0.38; level.mgTurretSettings[ "medium" ][ "aiSpread" ] = 2; level.mgTurretSettings[ "medium" ][ "playerSpread" ] = 0.5; level.mgTurretSettings[ "hard" ][ "convergenceTime" ] = .8; level.mgTurretSettings[ "hard" ][ "suppressionTime" ] = 3.0; level.mgTurretSettings[ "hard" ][ "accuracy" ] = 0.38; level.mgTurretSettings[ "hard" ][ "aiSpread" ] = 2; level.mgTurretSettings[ "hard" ][ "playerSpread" ] = 0.5; level.mgTurretSettings[ "fu" ][ "convergenceTime" ] = .4; level.mgTurretSettings[ "fu" ][ "suppressionTime" ] = 3.0; level.mgTurretSettings[ "fu" ][ "accuracy" ] = 0.38; level.mgTurretSettings[ "fu" ][ "aiSpread" ] = 2; level.mgTurretSettings[ "fu" ][ "playerSpread" ] = 0.5; } main() { if ( getDvar( "mg42" ) == "" ) setDvar( "mgTurret", "off" ); level.magic_distance = 24; turretInfos = getEntArray( "turretInfo", "targetname" ); for ( index = 0; index < turretInfos.size; index++ ) turretInfos[ index ] delete(); } portable_mg_behavior() { // turret guys have the turret attached to precache it. This'll have to // support allied guns or different guns as we add more, but right now // it can be generic self detach( "weapon_mg42_carry", "tag_origin" ); self endon( "death" ); // first set his goalpos and goalradius self.goalradius = level.default_goalradius; if ( isdefined( self.target ) ) { node = getnode( self.target, "targetname" ); if ( isdefined( node ) ) { if ( isdefined( node.radius ) ) self.goalradius = node.radius; self setgoalnode( node ); } } while ( !isdefined( self.node ) ) { // wait until the AI picks a node to run to wait( 0.05 ); } // try to find a turret to run to // first try the node we're targetted to turret_node = undefined; if ( isdefined( self.target ) ) { node = getnode( self.target, "targetname" ); turret_node = node; } // next try the node we're going to, maybe its a turret node if ( !isdefined( turret_node ) ) { turret_node = self.node; } // just be a normal guy if we're not going to a turret node if ( !isdefined( turret_node ) ) { return; } if ( turret_node.type != "Turret" ) return; taken_nodes = getTakenNodes(); taken_nodes[ self.node.origin + "" ] = undefined;// clear our own node since its taken by us // some random AI has this node already, probably doing cover there if ( isdefined( taken_nodes[ turret_node.origin + "" ] ) ) return; turret = turret_node.turret; if ( isdefined( turret.reserved ) ) { assert( turret.reserved != self ); return; } reserve_turret( turret ); // we're not at the turret position so we have to run to it if ( turret.isSetup ) { // its already setup so just go there and use it leave_gun_and_run_to_new_spot( turret ); } else { // its not setup yet so go there and set it up then use it run_to_new_spot_and_setup_gun( turret ); } maps\_mg_penetration::gunner_think( turret_node.turret ); } mg42_trigger() { self waittill( "trigger" ); level notify( self.targetname ); level.mg42_trigger[ self.targetname ] = true; // println ("trigger at ", self getorigin()," was triggered"); self delete(); } mgTurret_auto( trigger ) { trigger waittill( "trigger" ); ai = getaiarray( "bad_guys" ); for ( i = 0;i < ai.size;i++ ) { if ( ( isdefined( ai[ i ].script_mg42auto ) ) && ( trigger.script_mg42auto == ai[ i ].script_mg42auto ) ) { ai[ i ] notify( "auto_ai" ); println( "^a ai auto on!" ); } } spawners = getspawnerarray(); for ( i = 0;i < spawners.size;i++ ) if ( ( isdefined( spawners[ i ].script_mg42auto ) ) && ( trigger.script_mg42auto == spawners[ i ].script_mg42auto ) ) { spawners[ i ].ai_mode = "auto_ai"; println( "^aspawner ", i, " set to auto" ); } maps\_spawner::kill_trigger( trigger ); } mg42_suppressionFire( targets ) { self endon( "death" ); self endon( "stop_suppressionFire" ); if ( !isdefined( self.suppresionFire ) ) self.suppresionFire = true; for ( ;; ) { while ( self.suppresionFire ) { self settargetentity( targets[ randomint( targets.size ) ] ); wait( 2 + randomfloat( 2 ) ); } self cleartargetentity(); while ( !self.suppresionFire ) wait 1; } } manual_think( mg42 )// For regular spawned guys that are told to use mg42s manually vs static target { org = self.origin; self waittill( "auto_ai" ); mg42 notify( "stopfiring" ); mg42 setmode( "auto_ai" );// auto, auto_ai, manual mg42 settargetentity( level.player ); /* mg42 setmode("auto_ai"); // auto, auto_ai, manual self useturret(mg42); // dude should be near the mg42 self.favoriteEnemy = level.player; // self doDamage ( 25, self.origin ); */ } burst_fire_settings( setting ) { if ( setting == "delay" ) return 0.2; else if ( setting == "delay_range" ) return 0.5; else if ( setting == "burst" ) return 0.5; else // if (setting == "burst_range") return 1.5; } burst_fire_unmanned() { self endon( "death" ); self endon( "stop_burst_fire_unmanned" ); if ( isdefined( self.script_delay_min ) ) mg42_delay = self.script_delay_min; else mg42_delay = burst_fire_settings( "delay" ); if ( isdefined( self.script_delay_max ) ) mg42_delay_range = self.script_delay_max - mg42_delay; else mg42_delay_range = burst_fire_settings( "delay_range" ); if ( isdefined( self.script_burst_min ) ) mg42_burst = self.script_burst_min; else mg42_burst = burst_fire_settings( "burst" ); if ( isdefined( self.script_burst_max ) ) mg42_burst_range = self.script_burst_max - mg42_burst; else mg42_burst_range = burst_fire_settings( "burst_range" ); pauseUntilTime = gettime(); turretState = "start"; for ( ;; ) { duration = ( pauseUntilTime - gettime() ) * 0.001; if ( self isFiringTurret() && ( duration <= 0 ) ) { if ( turretState != "fire" ) { turretState = "fire"; // self setAnimKnobRestart(%standMG42gun_fire_foward); thread DoShoot(); } duration = mg42_burst + randomfloat( mg42_burst_range ); //println("fire duration: ", duration); thread TurretTimer( duration ); self waittill( "turretstatechange" );// code or script duration = mg42_delay + randomfloat( mg42_delay_range ); //println("stop fire duration: ", duration); pauseUntilTime = gettime() + int( duration * 1000 ); } else { if ( turretState != "aim" ) { turretState = "aim"; // self setAnimKnobRestart(%standMG42gun_aim_foward); } //println("aim duration: ", duration); thread TurretTimer( duration ); self waittill( "turretstatechange" );// code or script } } } DoShoot() { self endon( "death" ); self endon( "turretstatechange" );// code or script for ( ;; ) { self ShootTurret(); wait 0.1; } } TurretTimer( duration ) { if ( duration <= 0 ) return; self endon( "turretstatechange" );// code //println("start turret timer"); wait duration; if ( isdefined( self ) ) self notify( "turretstatechange" ); //println("end turret timer"); } random_spread( ent ) { self endon( "death" ); self notify( "stop random_spread" ); self endon( "stop random_spread" ); self endon( "stopfiring" ); self settargetentity( ent ); while ( 1 ) { if ( isplayer( ent ) ) ent.origin = self.manual_target getorigin(); else ent.origin = self.manual_target.origin; ent.origin += ( 20 - randomfloat( 40 ), 20 - randomfloat( 40 ), 20 - randomfloat( 60 ) ); wait( 0.2 ); } } mg42_firing( mg42 ) { self notify( "stop_using_built_in_burst_fire" ); self endon( "stop_using_built_in_burst_fire" ); mg42 stopfiring(); while ( 1 ) { mg42 waittill( "startfiring" ); self thread burst_fire( mg42 ); mg42 startfiring(); mg42 waittill( "stopfiring" ); mg42 stopfiring(); } } burst_fire( mg42, manual_target ) { mg42 endon( "stopfiring" ); self endon( "stop_using_built_in_burst_fire" ); if ( isdefined( mg42.script_delay_min ) ) mg42_delay = mg42.script_delay_min; else mg42_delay = maps\_mgturret::burst_fire_settings( "delay" ); if ( isdefined( mg42.script_delay_max ) ) mg42_delay_range = mg42.script_delay_max - mg42_delay; else mg42_delay_range = maps\_mgturret::burst_fire_settings( "delay_range" ); if ( isdefined( mg42.script_burst_min ) ) mg42_burst = mg42.script_burst_min; else mg42_burst = maps\_mgturret::burst_fire_settings( "burst" ); if ( isdefined( mg42.script_burst_max ) ) mg42_burst_range = mg42.script_burst_max - mg42_burst; else mg42_burst_range = maps\_mgturret::burst_fire_settings( "burst_range" ); while ( 1 ) { mg42 startfiring(); if ( isdefined( manual_target ) ) mg42 thread random_spread( manual_target ); wait( mg42_burst + randomfloat( mg42_burst_range ) ); mg42 stopfiring(); wait( mg42_delay + randomfloat( mg42_delay_range ) ); } } _spawner_mg42_think() { if ( !isdefined( self.flagged_for_use ) ) self.flagged_for_use = false; if ( !isdefined( self.targetname ) ) return; node = getnode( self.targetname, "target" ); if ( !isdefined( node ) ) return; if ( !isdefined( node.script_mg42 ) ) return; if ( !isdefined( node.mg42_enabled ) ) node.mg42_enabled = true; self.script_mg42 = node.script_mg42; first_run = true; while ( 1 ) { if ( first_run ) { first_run = false; if ( ( isdefined( node.targetname ) ) || ( self.flagged_for_use ) ) self waittill( "get new user" ); } if ( !node.mg42_enabled ) { node waittill( "enable mg42" ); node.mg42_enabled = true; } excluders = []; ai = getaiarray(); for ( i = 0;i < ai.size;i++ ) { excluded = true; if ( ( isdefined( ai[ i ].script_mg42 ) ) && ( ai[ i ].script_mg42 == self.script_mg42 ) ) excluded = false; if ( isdefined( ai[ i ].used_an_mg42 ) ) excluded = true; if ( excluded ) excluders[ excluders.size ] = ai[ i ]; } if ( excluders.size ) ai = maps\_utility::get_closest_ai_exclude( node.origin, undefined, excluders ); else ai = maps\_utility::get_closest_ai( node.origin, undefined ); excluders = undefined; if ( isdefined( ai ) ) { ai notify( "stop_going_to_node" ); ai thread maps\_spawner::go_to_node( node ); ai waittill( "death" ); } else self waittill( "get new user" ); } } mg42_think() { if ( !isdefined( self.ai_mode ) ) self.ai_mode = "manual_ai"; node = getnode( self.target, "targetname" ); if ( !isdefined( node ) ) { println( "Guy at org ", self.origin, " had no node" ); return; } mg42 = getent( node.target, "targetname" ); mg42.org = node.origin; if ( isdefined( mg42.target ) ) { if ( ( !isdefined( level.mg42_trigger ) ) || ( !isdefined( level.mg42_trigger[ mg42.target ] ) ) ) { level.mg42_trigger[ mg42.target ] = false; getent( mg42.target, "targetname" ) thread mg42_trigger(); } trigger = true; } else trigger = false; while ( 1 ) { if ( self.count == 0 ) return; mg42_gunner = undefined; while ( !isdefined( mg42_gunner ) ) { mg42_gunner = self dospawn(); wait( 1 ); } // println ("gunner thinking"); mg42_gunner thread mg42_gunner_think( mg42, trigger, self.ai_mode ); mg42_gunner thread mg42_firing( mg42 ); mg42_gunner waittill( "death" ); // println ("gunner thought"); if ( isdefined( self.script_delay ) ) wait( self.script_delay ); else if ( ( isdefined( self.script_delay_min ) ) && ( isdefined( self.script_delay_max ) ) ) wait( self.script_delay_min + randomfloat( self.script_delay_max - self.script_delay_min ) ); else wait( 1 ); } } kill_objects( owner, msg, temp1, temp2 ) { owner waittill( msg ); if ( isdefined( temp1 ) ) temp1 delete(); if ( isdefined( temp2 ) ) temp2 delete(); } mg42_gunner_think( mg42, trigger, ai_mode ) { self endon( "death" ); if ( ai_mode == "manual_ai" ) { while ( 1 ) { self thread mg42_gunner_manual_think( mg42, trigger ); self waittill( "auto_ai" ); self move_use_turret( mg42, "auto_ai" );// was setmode( "auto_ai" ) and cleartargetentity() self waittill( "manual_ai" ); } } else { while ( 1 ) { self move_use_turret( mg42, "auto_ai", level.player );// was setmode( "auto_ai" ) and settargetentity( level.player ) self waittill( "manual_ai" ); self thread mg42_gunner_manual_think( mg42, trigger ); self waittill( "auto_ai" ); } } } player_safe() { if ( !isdefined( level.player_covertrigger ) ) return false; if ( level.player getstance() == "prone" ) return true; if ( ( level.player_covertype == "cow" ) && ( level.player getstance() == "crouch" ) ) return true; return false; } stance_num() { if ( level.player getstance() == "prone" ) return( 0, 0, 5 ); else if ( level.player getstance() == "crouch" ) return( 0, 0, 25 ); return( 0, 0, 50 ); } mg42_gunner_manual_think( mg42, trigger ) { self endon( "death" ); self endon( "auto_ai" ); // maps\_utility::debug_message ("MANUAL", self.origin); self.pacifist = true; self setgoalpos( mg42.org ); self.goalradius = level.magic_distance; self waittill( "goal" ); if ( trigger ) { if ( !level.mg42_trigger[ mg42.target ] ) level waittill( mg42.target ); } self.pacifist = false; // mg42 setmode("manual_ai"); // auto, auto_ai, manual mg42 setmode( "auto_ai" );// auto, auto_ai, manual mg42 cleartargetentity(); targ_org = spawn( "script_origin", ( 0, 0, 0 ) ); tempmodel = spawn( "script_model", ( 0, 0, 0 ) ); tempmodel.scale = 3; if ( getdvar( "mg42" ) != "off" ) tempmodel setmodel( "temp" ); tempmodel thread temp_think( mg42, targ_org ); level thread kill_objects( self, "death", targ_org, tempmodel ); level thread kill_objects( self, "auto_ai", targ_org, tempmodel ); mg42.player_target = false; mg42timer = 0; targets = getentarray( "mg42_target", "targetname" ); if ( targets.size > 0 ) { script_targets = true; current_org = targets[ randomint( targets.size ) ].origin; self thread shoot_mg42_script_targets( targets ); self move_use_turret( mg42 ); self.target_entity = targ_org; mg42 setmode( "manual_ai" );// auto, auto_ai, manual mg42 settargetentity( targ_org ); mg42 notify( "startfiring" ); mindist = 15; wait_time = 0.08;// 2.8 / 20; dif = 0.05;// 1 / 20; // player_safe_time = gettime() + 5500 + randomfloat (5000); targ_org.origin = targets[ randomint( targets.size ) ].origin; shoot_timer = 0; // while (gettime() < player_safe_time) while ( !isdefined( level.player_covertrigger ) ) { current_org = targ_org.origin; if ( distance( current_org, targets[ self.gun_targ ].origin ) > mindist ) { temp_vec = vectornormalize( targets[ self.gun_targ ].origin - current_org ); temp_vec = vector_multiply( temp_vec, mindist ); current_org += temp_vec; } else self notify( "next_target" ); targ_org.origin = current_org; wait( 0.1 ); } while ( 1 ) { for ( i = 0;i < 1;i += dif ) { targ_org.origin = vector_multiply( current_org, 1.0 - i ) + vector_multiply( level.player getorigin() + stance_num(), i ); if ( player_safe() ) i = 2; wait( wait_time ); } old_org = level.player getorigin(); while ( !player_safe() ) { targ_org.origin = level.player getorigin(); vec_dif = targ_org.origin - old_org; targ_org.origin = targ_org.origin + vec_dif + stance_num(); old_org = level.player getorigin(); wait( 0.1 ); } if ( player_safe() ) { shoot_timer = gettime() + 1500 + randomfloat( 4000 ); while ( ( player_safe() ) && ( isdefined( level.player_covertrigger.target ) ) && ( gettime() < shoot_timer ) ) { target = getentarray( level.player_covertrigger.target, "targetname" ); target = target[ randomint( target.size ) ]; targ_org.origin = target.origin + ( randomfloat( 30 ) - 15, randomfloat( 30 ) - 15, randomfloat( 40 ) - 60 ); wait( 0.1 ); } } self notify( "next_target" ); while ( player_safe() ) { current_org = targ_org.origin; if ( distance( current_org, targets[ self.gun_targ ].origin ) > mindist ) { temp_vec = vectornormalize( targets[ self.gun_targ ].origin - current_org ); temp_vec = vector_multiply( temp_vec, mindist ); current_org += temp_vec; } else self notify( "next_target" ); targ_org.origin = current_org; wait( 0.1 ); } } } else { while ( 1 ) { // Play is not safe, shoot player. self move_use_turret( mg42 ); while ( !isdefined( level.player_covertrigger ) ) { if ( !mg42.player_target ) { mg42 settargetentity( level.player ); mg42.player_target = true; // mg42 settargetentity(tempmodel); tempmodel.targent = level.player; } wait( 0.2 ); } // Player is safe so shoot in front of him. mg42 setmode( "manual_ai" );// auto, auto_ai, manual self move_use_turret( mg42 ); mg42 notify( "startfiring" ); shoot_timer = gettime() + 1500 + randomfloat( 4000 ); while ( shoot_timer > gettime() ) { if ( isdefined( level.player_covertrigger ) ) { target = getentarray( level.player_covertrigger.target, "targetname" ); target = target[ randomint( target.size ) ]; targ_org.origin = target.origin + ( randomfloat( 30 ) - 15, randomfloat( 30 ) - 15, randomfloat( 40 ) - 60 ); mg42 settargetentity( targ_org ); tempmodel.targent = targ_org; wait( randomfloat( 1 ) ); } else break; } mg42 notify( "stopfiring" ); // Play is still safe, shoot friendlies. self move_use_turret( mg42 ); if ( mg42.player_target ) { mg42 setmode( "auto_ai" );// auto, auto_ai, manual mg42 cleartargetentity(); mg42.player_target = false; tempmodel.targent = tempmodel; tempmodel.origin = ( 0, 0, 0 ); } while ( isdefined( level.player_covertrigger ) ) wait( 0.2 ); wait( .750 + randomfloat( .200 ) ); } } } shoot_mg42_script_targets( targets ) { self endon( "death" ); while ( 1 ) { targ_filled = []; for ( i = 0;i < targets.size;i++ ) targ_filled[ i ] = false; for ( i = 0;i < targets.size;i++ ) { self.gun_targ = randomint( targets.size ); self waittill( "next_target" ); while ( targ_filled[ self.gun_targ ] ) { self.gun_targ++ ; if ( self.gun_targ >= targets.size ) self.gun_targ = 0; } targ_filled[ self.gun_targ ] = true; } } } move_use_turret( mg42, aitype, target ) { self setgoalpos( mg42.org ); self.goalradius = level.magic_distance; self waittill( "goal" ); if ( isdefined( aitype ) && aitype == "auto_ai" ) { mg42 setmode( "auto_ai" ); if ( isdefined( target ) ) mg42 settargetentity( target ); else mg42 cleartargetentity(); } self useturret( mg42 );// dude should be near the mg42 } temp_think( mg42, targ ) { if ( getdvar( "mg42" ) == "off" ) return; self.targent = self; while ( 1 ) { self.origin = targ.origin; line( self.origin, mg42.origin, ( 0.2, 0.5, 0.8 ), 0.5 ); wait( 0.1 ); } } // This is a thread that runs for each turret managing AI users of the turret turret_think( node ) { turret = getent( node.auto_mg42_target, "targetname" ); mintime = 0.5; if ( isdefined( turret.script_turret_reuse_min ) ) mintime = turret.script_turret_reuse_min; maxtime = 2; if ( isdefined( turret.script_turret_reuse_max ) ) mintime = turret.script_turret_reuse_max; assert( maxtime >= mintime ); for ( ;; ) { turret waittill( "turret_deactivate" ); wait( mintime + randomfloat( maxtime - mintime ) );// Wait for a bit before calling the next AI over. while ( !( isturretactive( turret ) ) ) { turret_find_user( node, turret ); wait 1.0; } } } turret_find_user( node, turret ) { ai = getaiarray(); for ( i = 0;i < ai.size;i++ ) { if ( ai[ i ] isingoal( node.origin ) && ai[ i ] canuseturret( turret ) ) { savekeepclaimed = ai[ i ].keepClaimedNodeIfValid; ai[ i ].keepClaimedNodeIfValid = false; if ( !( ai[ i ] usecovernode( node ) ) ) { ai[ i ].keepClaimedNodeIfValid = savekeepclaimed; } } } } setDifficulty() { init_mgTurretsettings(); mg42s = getEntArray( "misc_turret", "code_classname" ); difficulty = getdifficulty(); for ( index = 0; index < mg42s.size; index++ ) { if ( isdefined( mg42s[ index ].script_skilloverride ) ) { switch( mg42s[ index ].script_skilloverride ) { case "easy": difficulty = "easy"; break; case "medium": difficulty = "medium"; break; case "hard": difficulty = "hard"; break; case "fu": difficulty = "fu"; break; default: continue; } } mg42_setdifficulty( mg42s[ index ], difficulty ); } } mg42_setdifficulty( mg42, difficulty ) { mg42.convergenceTime = level.mgTurretSettings[ difficulty ][ "convergenceTime" ]; mg42.suppressionTime = level.mgTurretSettings[ difficulty ][ "suppressionTime" ]; mg42.accuracy = level.mgTurretSettings[ difficulty ][ "accuracy" ]; mg42.aiSpread = level.mgTurretSettings[ difficulty ][ "aiSpread" ]; mg42.playerSpread = level.mgTurretSettings[ difficulty ][ "playerSpread" ]; } mg42_target_drones( nonai, team, fakeowner ) { if ( !isdefined( fakeowner ) ) fakeowner = false; self endon( "death" ); self.dronefailed = false; if ( !isdefined( self.script_fireondrones ) ) self.script_fireondrones = false; if ( !isdefined( nonai ) ) nonai = false; self setmode( "manual_ai" ); difficulty = getdifficulty(); if ( !isdefined( level.drones ) ) waitfornewdrone = true; else waitfornewdrone = false; while ( 1 ) { if ( fakeowner && !isdefined( self.fakeowner ) ) { self setmode( "manual" ); while ( !isdefined( self.fakeowner ) ) wait .2; } else if ( nonai ) self setmode( "auto_nonai" ); else self setmode( "auto_ai" ); if ( waitfornewdrone ) level waittill( "new_drone" ); if ( !isdefined( self.oldconvergencetime ) ) self.oldconvergencetime = self.convergencetime; self.convergencetime = 2; if ( !nonai ) { turretowner = self getturretowner(); if ( !isalive( turretowner ) || isplayer( turretowner ) ) { wait .05; continue; } else team = turretowner.team; } else { if ( fakeowner && !isdefined( self.fakeowner ) ) { wait .05; continue; } assert( isdefined( team ) ); turretowner = undefined; } if ( team == "allies" ) targetteam = "axis"; else targetteam = "allies"; while ( level.drones[ targetteam ].lastindex ) { //self gettagangles ("tag_flash") target = get_bestdrone( targetteam ); if ( !isdefined( self.script_fireondrones ) || !self.script_fireondrones ) { wait .2; break; } if ( !isdefined( target ) ) { wait .2; break; } if ( nonai ) self setmode( "manual" ); else self setmode( "manual_ai" ); thread drone_fail( target, 3 ); if ( !self.dronefailed ) { self settargetentity( target.turrettarget ); self shootturret(); self startfiring(); } else { self.dronefailed = false; wait .05; continue; } target waittill_any( "death", "drone_mg42_fail" ); waittillframeend; if ( !nonai && !( isdefined( self getturretowner() ) && self getturretowner() == turretowner ) ) break; } self.convergencetime = self.oldconvergencetime; self.oldconvergencetime = undefined; self cleartargetentity(); self stopfiring(); if ( level.drones[ targetteam ].lastindex ) waitfornewdrone = false; else waitfornewdrone = true; } } drone_fail( drone, time ) { self endon( "death" ); drone endon( "death" ); timer = gettime() + ( time * 1000 ); while ( timer > gettime() ) { turrettarget = self getturrettarget( true ); // bullettracepassed(, , , ) if ( !sighttracepassed( self gettagorigin( "tag_flash" ), drone.origin + ( 0, 0, 40 ), 0, drone ) ) { self.dronefailed = true; wait .2; break; } else if ( isdefined( turrettarget ) && distance( turrettarget.origin, self.origin ) < distance( self.origin, drone.origin ) ) { self.dronefailed = true; wait .1; break; } wait .1; } // maps\_utility::structarray_swaptolast(level.drones[drone.team],drone); maps\_utility::structarray_shuffle( level.drones[ drone.team ], 1 ); drone notify( "drone_mg42_fail" ); } get_bestdrone( team ) { //prof_begin("drone_mg42"); // dotvalue = .88; // dist = 9999999; if ( level.drones[ team ].lastindex < 1 ) return; ent = undefined; dotforward = anglestoforward( self.angles ); for ( i = 0;i < level.drones[ team ].lastindex;i++ ) { angles = vectortoangles( level.drones[ team ].array[ i ].origin - self.origin ); forward = anglestoforward( angles ); if ( vectordot( dotforward, forward ) < .88 ) continue; // if(!sighttracepassed(level.drones[team].array[i].origin,self.origin+(0,0,10),0,level.drones[team].array[i])) // continue; // newdist = distance(level.drones[team].array[i].origin, self.origin); // if (newdist >= dist) // continue; // dist = newdist; ent = level.drones[ team ].array[ i ]; break; } aitarget = self getturrettarget( true ); if ( isdefined( ent ) && isdefined( aitarget ) && distance( self.origin, aitarget.origin ) < distance( self.origin, ent.origin ) ) ent = undefined; // shoot at ai if they are closer //prof_end("drone_mg42"); return ent; } saw_mgTurretLink( nodes ) { possible_turrets = getEntArray( "misc_turret", "code_classname" ); turrets = []; for ( i = 0; i < possible_turrets.size; i++ ) { if ( isDefined( possible_turrets[ i ].targetname ) ) continue; if ( isdefined( possible_turrets[ i ].isvehicleattached ) ) { assertEx( possible_turrets[ i ].isvehicleattached != 0, "Setting must be either true or undefined" ); continue; } turrets[ possible_turrets[ i ].origin + "" ] = possible_turrets[ i ]; } // did we find any turrets? if ( !turrets.size ) return; for ( nodeIndex = 0; nodeIndex < nodes.size; nodeIndex++ ) { node = nodes[ nodeIndex ]; if ( node.type == "Path" ) continue; if ( node.type == "Begin" ) continue; if ( node.type == "End" ) continue; nodeForward = anglesToForward( ( 0, node.angles[ 1 ], 0 ) ); foreach ( index, turret in turrets ) { if ( distance( node.origin, turret.origin ) > 50 ) continue; turretForward = anglesToForward( ( 0, turret.angles[ 1 ], 0 ) ); dot = vectorDot( nodeForward, turretForward ); if ( dot < 0.9 ) continue; node.turretInfo = spawn( "script_origin", turret.origin ); node.turretInfo.angles = turret.angles; node.turretInfo.node = node; node.turretInfo.leftArc = 45; node.turretInfo.rightArc = 45; node.turretInfo.topArc = 15; node.turretInfo.bottomArc = 15; if ( isDefined( turret.leftArc ) ) node.turretInfo.leftArc = min( turret.leftArc, 45 ); if ( isDefined( turret.rightArc ) ) node.turretInfo.rightArc = min( turret.rightArc, 45 ); if ( isDefined( turret.topArc ) ) node.turretInfo.topArc = min( turret.topArc, 15 ); if ( isDefined( turret.bottomArc ) ) node.turretInfo.bottomArc = min( turret.bottomArc, 15 ); turrets[ index ] = undefined; turret delete(); } } /# foreach ( turret in turrets ) { assertMsg( "ERROR: turret at " + turret.origin + " could not link to any node!" ); } #/ } auto_mgTurretLink( nodes ) { // Attaches MG turrets with targetname auto_mgTurret to cover crouch and stand nodes. possible_turrets = getEntArray( "misc_turret", "code_classname" ); turrets = []; for ( i = 0; i < possible_turrets.size; i++ ) { if ( !isDefined( possible_turrets[ i ].targetname ) || tolower( possible_turrets[ i ].targetname ) != "auto_mgturret" ) continue; // if the turret is legit, create a unique string reference to it if ( !isdefined( possible_turrets[ i ].export ) ) continue; if ( !isdefined( possible_turrets[ i ].script_dont_link_turret ) ) turrets[ possible_turrets[ i ].origin + "" ] = possible_turrets[ i ]; } // did we find any turrets? if ( !turrets.size ) return; for ( nodeIndex = 0; nodeIndex < nodes.size; nodeIndex++ ) { node = nodes[ nodeIndex ]; if ( node.type == "Path" ) continue; if ( node.type == "Begin" ) continue; if ( node.type == "End" ) continue; // if ( node.type != "Turret" ) // continue; nodeForward = anglesToForward( ( 0, node.angles[ 1 ], 0 ) ); keys = getArrayKeys( turrets ); for ( i = 0; i < keys.size; i++ ) { turret = turrets[ keys[ i ] ]; if ( distance( node.origin, turret.origin ) > 70 ) continue; turretForward = anglesToForward( ( 0, turret.angles[ 1 ], 0 ) ); dot = vectorDot( nodeForward, turretForward ); if ( dot < 0.9 ) continue; node.turret = turret; turret.node = node; turret.isSetup = true; assertEx( isdefined( turret.export ), "Turret at " + turret.origin + " does not have a .export value but is near a cover node. If you do not want them to link, use .script_dont_link_turret." ); // remove the reference for it so that the other nodes dont // scan for this turret turrets[ keys[ i ] ] = undefined; } // assertEx( isdefined( node.turret ), "Cover node at " + node.origin + " has no turret!" ); } /# // err the unclaimed turrets keys = getArrayKeys( turrets ); if ( keys.size ) { println( "The turrets at the following origins were not auto-bound to a node_turret." ); println( "Set .script_dont_link_turret if you do not want a turret to use a node_turret." ); for ( i = 0; i < keys.size; i++ ) { println( keys[ i ] ); } assertEx( 0, "Turrets failed to be linked to node_turrets, see list above." ); } #/ /* // logic that makes the node "call" for ai if ( isDefined( node.auto_mgTurret_target ) ) { thread maps\_mgturret::turret_think( node ); } */ nodes = undefined; } save_turret_sharing_info() { // shares turrets so a guy at a turret knows where to run if he decides to move the turret self.shared_turrets = []; self.shared_turrets[ "connected" ] = []; self.shared_turrets[ "ambush" ] = []; if ( !isdefined( self.export ) ) { assertEx( !isdefined( self.script_turret_share ), "Turret at " + self.origin + " has script_turret_share but has no .export value, so script_turret_share won't have any effect." ); assertEx( !isdefined( self.script_turret_ambush ), "Turret at " + self.origin + " has script_turret_ambush but has no .export value, so script_turret_ambush won't have any effect." ); return; } level.shared_portable_turrets[ self.export ] = self; if ( isdefined( self.script_turret_share ) ) { // turn the origin into a unique string // record which turrets share with this one strings = strtok( self.script_turret_share, " " ); for ( i = 0; i < strings.size; i++ ) { self.shared_turrets[ "connected" ][ strings[ i ] ] = true; } } if ( isdefined( self.script_turret_ambush ) ) { // turn the origin into a unique string // record which turrets share with this one strings = strtok( self.script_turret_ambush, " " ); for ( i = 0; i < strings.size; i++ ) { self.shared_turrets[ "ambush" ][ strings[ i ] ] = true; } } } /* mg42setup_gun() { assertEX (isdefined(self.target), "Portable MG42 guy at origin " + self.origin + " has no target"); mg42node = getnode (self.target,"targetname"); mg42 = getent (self.target,"targetname"); if ( !isdefined( mg42.shared_turrets ) ) mg42 save_turret_sharing_info(); // If the portable gunner targets a node then he's going to do a chain of nodes to the destination, which should // be an mg42. Otherwise he's directly targetting an mg42. if (isdefined (mg42node)) { // Set this so later we can run along it as a chain. self.mg42node = mg42node; assert (!isdefined (mg42), "guy at " + self.origin + " targets an ent and a node"); for (;;) { newnode = getnode (mg42node.target,"targetname"); if (!isdefined (newnode)) { mg42 = getent (mg42node.target,"targetname"); break; } mg42node = newnode; } } assertEX (isdefined(mg42), "Portable MG42 guy at origin " + self.origin + " doesn't target an mg42"); assertEX (mg42.classname == "misc_turret", "Portable MG42 guy at origin " + self.origin + " doesn't target an mg42"); if (!isdefined(mg42.isSetup)) { mg42 hide_turret(); } return mg42; } */ restoreDefaultPitch() { self notify( "gun_placed_again" ); self endon( "gun_placed_again" ); self waittill( "restore_default_drop_pitch" ); wait( 1 ); self restoredefaultdroppitch(); } dropTurret() { thread dropTurretProc(); } dropTurretProc() { turret = spawn( "script_model", ( 0, 0, 0 ) ); turret.origin = self gettagorigin( level.portable_mg_gun_tag ); turret.angles = self gettagangles( level.portable_mg_gun_tag ); turret setmodel( self.turretModel ); forward = anglestoforward( self.angles ); forward = vector_multiply( forward, 100 ); turret moveGravity( forward, 0.5 ); self detach( self.turretModel, level.portable_mg_gun_tag ); self.turretmodel = undefined; wait( 0.7 ); turret delete(); } turretDeathDetacher() { self endon( "kill_turret_detach_thread" ); self endon( "dropped_gun" ); self waittill( "death" ); if ( !isdefined( self ) ) return;// in case many guys die at once and we are removed dropTurret(); } turretDetacher() { self endon( "death" ); self endon( "kill_turret_detach_thread" ); // in case the enemy gets close to a portable turret guy self waittill( "dropped_gun" ); self detach( self.turretModel, level.portable_mg_gun_tag ); } restoreDefaults() { // self.goalradius = 350; self.run_overrideanim = undefined; self set_all_exceptions( animscripts\init::empty ); } restorePitch() { self waittill( "turret_deactivate" ); self restoredefaultdroppitch(); } update_enemy_target_pos_while_running( ent ) { self endon( "death" ); self endon( "end_mg_behavior" ); self endon( "stop_updating_enemy_target_pos" ); for ( ;; ) { self waittill( "saw_enemy" ); ent.origin = self.last_enemy_sighting_position; } } move_target_pos_to_new_turrets_visibility( ent, new_spot ) { // moves the target position to a point where the new turret // can see it. If the position gets updated by seeing an enemy // then that position also gets pushed towards the last turret to // the point of visibility on the assumption that towards the old // turret will bring it into visibility without causing it to // go to a weird point. // in any case the whole system probably needs a failsafe in case // the target position gets way outside the gun's allowed angles self endon( "death" ); self endon( "end_mg_behavior" ); self endon( "stop_updating_enemy_target_pos" ); old_turret_pos = self.turret.origin + ( 0, 0, 16 );// turrets are on geo so it could abstruct; dest_pos = new_spot.origin + ( 0, 0, 16 ); for ( ;; ) { wait( 0.05 );// plenty of time while he runs, doesn't have to happen often if ( sighttracepassed( ent.origin, dest_pos, 0, undefined ) ) { // line( ent.origin, dest_pos, ( 0,1,0 ) ); continue; } // line( ent.origin, dest_pos, ( 1,0,0 ) ); // move the target pos towards the last turret position angles = vectortoangles( old_turret_pos - ent.origin ); forward = anglestoforward( angles ); forward = vector_multiply( forward, 8 ); ent.origin = ent.origin + forward; } } record_bread_crumbs_for_ambush( ent ) { self endon( "death" ); self endon( "end_mg_behavior" ); self endon( "stop_updating_enemy_target_pos" ); ent.bread_crumbs = []; for ( ;; ) { // print3d( self.origin + (0,0,50), "*", (1,1,0), 1, 1.5, 10*20 ); ent.bread_crumbs[ ent.bread_crumbs.size ] = self.origin + ( 0, 0, 50 ); wait( 0.35 ); } } aim_turret_at_ambush_point_or_visible_enemy( turret, ent ) { if ( !isalive( self.current_enemy ) && self canSee( self.current_enemy ) ) { // if we can see our enemy then just aim at him ent.origin = self.last_enemy_sighting_position; return; } forward = anglestoforward( turret.angles ); // find the best bread crumb to aim at // start a few from the back because the crumbs from while we were walking at the gun // arent going to be good for ( i = ent.bread_crumbs.size - 3; i >= 0; i -- ) { // dot check it so we're not aiming at the breadcrumbs we walked over crumb = ent.bread_crumbs[ i ]; normal = vectorNormalize( crumb - turret.origin ); dot = vectorDot( forward, normal ); if ( dot < 0.75 ) continue; ent.origin = crumb; // find the first one we cant see and aim there if ( sighttracepassed( turret.origin, crumb, 0, undefined ) ) { // linetime( turret.origin, crumb, ( 0, 1, 0 ), 10 ); continue; } // linetime( turret.origin, crumb, ( 1, 0, 0 ), 10 ); break; } } find_a_new_turret_spot( ent ) { // find a new spot to go to array = get_portable_mg_spot( ent ); new_spot = array[ "spot" ]; connection_type = array[ "type" ]; if ( !isdefined( new_spot ) ) return; reserve_turret( new_spot ); // if we see the enemy while we run, update the target position thread update_enemy_target_pos_while_running( ent ); thread move_target_pos_to_new_turrets_visibility( ent, new_spot ); if ( connection_type == "ambush" ) { thread record_bread_crumbs_for_ambush( ent ); } if ( new_spot.isSetup ) { leave_gun_and_run_to_new_spot( new_spot ); } else { pickup_gun( new_spot ); run_to_new_spot_and_setup_gun( new_spot ); } self notify( "stop_updating_enemy_target_pos" ); if ( connection_type == "ambush" ) { aim_turret_at_ambush_point_or_visible_enemy( new_spot, ent ); } // thread snap_lock_turret_onto_target( new_spot ); new_spot setTargetEntity( ent ); } snap_lock_turret_onto_target( turret ) { // turn manual on for a moment so he aims quickly to the spot he wants to aim at. turret setmode( "manual" ); wait( 0.5 ); turret setmode( "manual_ai" ); } leave_gun_and_run_to_new_spot( spot ) { assert( spot.reserved == self ); // spot is a bit of a misnomer as its actually a hidden gun we // rematerialize when he runs to it self stopuseturret(); self animscripts\shared::placeWeaponOn( self.primaryweapon, "none" ); // run to the spot where the gun is setup setup_anim = get_turret_setup_anim( spot ); org = getStartOrigin( spot.origin, spot.angles, setup_anim ); self setruntopos( org ); assertEx( distance( org, self.goalpos ) < self.goalradius, "Tried to set the run pos outside the goalradius" ); self waittill( "runto_arrived" ); use_the_turret( spot ); } pickup_gun( spot ) { // spot is a bit of a misnomer as its actually a hidden gun we // rematerialize when he runs to it self stopuseturret(); self.turret hide_turret(); } get_turret_setup_anim( turret ) { spot_types = []; spot_types[ "saw_bipod_stand" ] = level.mg_animmg[ "bipod_stand_setup" ]; spot_types[ "saw_bipod_crouch" ] = level.mg_animmg[ "bipod_crouch_setup" ]; spot_types[ "saw_bipod_prone" ] = level.mg_animmg[ "bipod_prone_setup" ]; return spot_types[ turret.weaponinfo ]; } run_to_new_spot_and_setup_gun( spot ) { assert( spot.reserved == self ); oldhealth = self.health; spot endon( "turret_deactivate" ); self.mg42 = spot;// used in the animscript exceptions self endon( "death" ); self endon( "dropped_gun" ); setup_anim = get_turret_setup_anim( spot ); self.turretModel = "weapon_mg42_carry"; // guys are meant to get their gun back automatically when they ditch an mg self notify( "kill_get_gun_back_on_killanimscript_thread" ); self animscripts\shared::placeWeaponOn( self.weapon, "none" ); if ( self isBadGuy() ) self.health = 1; // set the run anim self.run_overrideanim = %saw_gunner_run_fast; self.crouchrun_combatanim = %saw_gunner_run_fast; // attach the carry version of the gun self attach( self.turretModel, level.portable_mg_gun_tag ); thread turretDeathDetacher(); // run to the spot where the gun is going to be setup org = getStartOrigin( spot.origin, spot.angles, setup_anim ); self setruntopos( org ); assertEx( distance( org, self.goalpos ) < self.goalradius, "Tried to set the run pos outside the goalradius" ); wait( 0.05 );// must figure out what this wait is needed for self set_all_exceptions( animscripts\combat::exception_exposed_mg42_portable ); clear_exception( "move" ); set_exception( "cover_crouch", ::hold_indefintely ); while ( distance( self.origin, org ) > 16 ) { self setruntopos( org ); wait( 0.05 ); } // self waittill( "runto_arrived" ); self notify( "kill_turret_detach_thread" ); if ( self isBadGuy() ) self.health = oldhealth; if ( soundexists( "weapon_setup" ) ) thread play_sound_in_space( "weapon_setup" ); self animscripted( "setup_done", spot.origin, spot.angles, setup_anim ); restoreDefaults();// reset the run anims self waittillmatch( "setup_done", "end" ); spot notify( "restore_default_drop_pitch" ); spot show_turret(); self animscripts\shared::placeWeaponOn( self.primaryweapon, "right" ); use_the_turret( spot ); self detach( self.turretModel, level.portable_mg_gun_tag ); self set_all_exceptions( animscripts\init::empty ); self notify( "bcs_portable_turret_setup" ); } move_to_run_pos() { self setruntopos( self.runpos ); } hold_indefintely() { self endon( "killanimscript" ); self waittill( "death" ); } using_a_turret() { if ( !isdefined( self.turret ) ) return false; return self.turret.owner == self; } turret_user_moves() { // we've been forced to move by goalradius change or becoming exposed if ( !using_a_turret() ) { clear_exception( "move" ); return; } array = find_connected_turrets( "connected" ); new_spots = array[ "spots" ]; if ( !new_spots.size ) { // none of the turrets in the area we're moving to are connected to the // one we were at so we turn back to normal guy now clear_exception( "move" ); return; } // lets see if we have a new node, and if we do, if its a compatible turret node turret_node = self.node; // if its not, then lets use a random node from the connected nodes that are // within our goal radius if ( !isdefined( turret_node ) || !is_in_array( new_spots, turret_node ) ) { taken_nodes = getTakenNodes(); for ( i = 0; i < new_spots.size; i++ ) { turret_node = random( new_spots ); // some random AI has this node already, probably doing cover there // if we get the ability to push AI off their node then we'll do that here later if ( isdefined( taken_nodes[ turret_node.origin + "" ] ) ) return; } } turret = turret_node.turret; if ( isdefined( turret.reserved ) ) { assert( turret.reserved != self ); return; } reserve_turret( turret ); // we're not at the turret position so we have to run to it if ( turret.isSetup ) { // its already setup so just go there and use it leave_gun_and_run_to_new_spot( turret ); } else { // its not setup yet so go there and set it up then use it run_to_new_spot_and_setup_gun( turret ); } maps\_mg_penetration::gunner_think( turret_node.turret ); } use_the_turret( spot ) { turretWasUsed = self useturret( spot ); if ( turretWasUsed ) { set_exception( "move", ::turret_user_moves );// run this before running move so we might move the turret self.turret = spot; self thread mg42_firing( spot );// does the burst fire timings spot setmode( "manual_ai" ); spot thread restorePitch(); self.turret = spot; spot.owner = self; // self useturret( spot ); // dude should be near the mg42 // spot setmode( "manual_ai" ); // auto, auto_ai, manual // self.turret = spot; return true; } else { spot restoredefaultdroppitch(); return false; } } get_portable_mg_spot( ent ) { find_spot_funcs = []; find_spot_funcs[ find_spot_funcs.size ] = ::find_different_way_to_attack_last_seen_position; find_spot_funcs[ find_spot_funcs.size ] = ::find_good_ambush_spot; find_spot_funcs = array_randomize( find_spot_funcs ); for ( i = 0; i < find_spot_funcs.size; i++ ) { array = [[ find_spot_funcs[ i ] ]]( ent ); if ( !isdefined( array[ "spots" ] ) ) continue; array[ "spot" ] = random( array[ "spots" ] ); return array; } } getTakenNodes() { // returns an array of node origins owned by AI array = []; ai = getaiarray(); for ( i = 0; i < ai.size; i++ ) { if ( !isdefined( ai[ i ].node ) ) continue; array[ ai[ i ].node.origin + "" ] = true; } return array; } find_connected_turrets( connection_type ) { spots = level.shared_portable_turrets; // an array of shared turrets, using their origin as the index usable_spots = []; spot_exports = getArrayKeys( spots ); taken_nodes = getTakenNodes(); taken_nodes[ self.node.origin + "" ] = undefined; // find turrets that share the same keys for ( i = 0; i < spot_exports.size; i++ ) { export = spot_exports[ i ]; if ( spots[ export ] == self.turret ) continue; keys = getArrayKeys( self.turret.shared_turrets[ connection_type ] ); for ( p = 0; p < keys.size; p++ ) { // go through each key that the turret the guy is currently on has, // and see if any turrets share keys. // cast export as a string because arraykeys returns strings if ( spots[ export ].export + "" != keys[ p ] ) continue; // somebody else has this one or they're running to it if ( isdefined( spots[ export ].reserved ) ) continue; // some random AI has this node already, probably doing cover there if ( isdefined( taken_nodes[ spots[ export ].node.origin + "" ] ) ) continue; // don't pick one that is outside the goalradius if ( distance( self.goalpos, spots[ export ].origin ) > self.goalradius ) continue; // this spot is usable usable_spots[ usable_spots.size ] = spots[ export ]; } } array = []; // store it so we can figure out the best place for an ambusher to aim array[ "type" ] = connection_type; array[ "spots" ] = usable_spots; return array; } find_good_ambush_spot( ent ) { return find_connected_turrets( "ambush" ); } find_different_way_to_attack_last_seen_position( ent ) { array = find_connected_turrets( "connected" ); usable_spots = array[ "spots" ]; if ( !usable_spots.size ) return; good_spot = []; // find a spot that has a good fov and LOS on the target spot for ( i = 0; i < usable_spots.size; i++ ) { if ( !within_fov( usable_spots[ i ].origin, usable_spots[ i ].angles, ent.origin, 0.75 ) ) continue; /* normal = vectorNormalize( ent.origin - ( usable_spots[ i ].origin + offset ) ); forward = anglestoforward( usable_spots[ i ].angles ); dot = vectorDot( forward, normal ); thread linetime( usable_spots[ i ].origin + offset, usable_spots[ i ].origin + offset + vector_multiply( forward, 1000 ), ( 1, 0, 0 ), 10 ); thread linetime( ent.origin, usable_spots[ i ].origin + offset, ( 0, 0, 1 ), 10 ); */ if ( !sighttracepassed( ent.origin, usable_spots[ i ].origin + ( 0, 0, 16 ), 0, undefined ) ) continue; good_spot[ good_spot.size ] = usable_spots[ i ]; } array[ "spots" ] = good_spot; return array; } portable_mg_spot() { save_turret_sharing_info(); turret_preplaced = 1; self.isSetup = true; assert( !isdefined( self.reserved ) ); self.reserved = undefined; if ( isdefined( self.isvehicleattached ) ) return; // nate if ( self.spawnflags & turret_preplaced ) return; // a spot where a gun could be placed hide_turret(); } hide_turret() { assert( self.isSetup ); self notify( "stop_checking_for_flanking" ); self.isSetup = false; self hide(); self.solid = false; self makeUnusable(); self setdefaultdroppitch( 0 ); self thread restoreDefaultPitch(); } show_turret() { self show(); self.solid = true; self makeUsable(); assert( !self.isSetup ); self.isSetup = true; thread stop_mg_behavior_if_flanked(); } stop_mg_behavior_if_flanked() { self endon( "stop_checking_for_flanking" ); self waittill( "turret_deactivate" ); if ( isalive( self.owner ) ) self.owner notify( "end_mg_behavior" ); } turret_is_mine( turret ) { owner = turret getTurretOwner(); if ( !isdefined( owner ) ) return false; return owner == self; } end_turret_reservation( turret ) { waittill_turret_is_released( turret ); turret.reserved = undefined; } waittill_turret_is_released( turret ) { turret endon( "turret_deactivate" ); self endon( "death" ); self waittill( "end_mg_behavior" ); } reserve_turret( turret ) { turret.reserved = self; thread end_turret_reservation( turret ); } /*QUAKED misc_turret_50cal_turret_technical (1 0 0) (-16 -16 0) (16 16 56) pre-placed Spawn Flags: pre-placed - Means it already exists in map. Used by script only. Key Pairs: leftarc - horizonal left fire arc. rightarc - horizonal left fire arc. toparc - vertical top fire arc. bottomarc - vertical bottom fire arc. yawconvergencetime - time (in seconds) to converge horizontally to target. pitchconvergencetime - time (in seconds) to converge vertically to target. suppressionTime - time (in seconds) that the turret will suppress a target hidden behind cover maxrange - maximum firing/sight range. aiSpread - spread of the bullets out of the muzzle in degrees when used by the AI playerSpread - spread of the bullets out of the muzzle in degrees when used by the player defaultmdl="weapon_m2_50cal_center" default:"weaponinfo" "50cal_turret_technical" default:"targetname" "50cal_turret_technical" */