#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() { self.a.combatrunanim = %ai_mg_shoulder_run; self.run_noncombatanim = %ai_mg_shoulder_run; self.walk_combatanim = %ai_mg_shoulder_run; self.walk_noncombatanim = %ai_mg_shoulder_run; self.a.crouchRunAnim = %ai_mg_shoulder_run; self.crouchrun_combatanim = %ai_mg_shoulder_run; self.alwaysRunForward = true; self.disableExits = true; } mg42_trigger() { self waittill( "trigger" ); level notify( self.targetname ); level.mg42_trigger[self.targetname] = true; self Delete(); } mgTurret_auto( trigger ) { trigger waittill( "trigger" ); ai = GetAiArray( "axis" ); 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 ) { self waittill( "auto_ai" ); mg42 notify( "stopfiring" ); mg42 SetMode( "auto_ai" ); } 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 4; } } burst_fire_unmanned() { self notify( "stop_burst_fire_unmanned" ); self endon( "stop_burst_fire_unmanned" ); self endon( "death" ); 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"; self.script_shooting = false; for( ;; ) { if( IsDefined( self.manual_targets ) ) { self ClearTargetEntity(); self SetTargetEntity( self.manual_targets[RandomInt( self.manual_targets.size )] ); } duration = ( pauseUntilTime - GetTime() ) * 0.001; if( self IsFiringTurret() &&( duration <= 0 ) ) { if( turretState != "fire" ) { turretState = "fire"; self thread DoShoot(); self.script_shooting = true; } duration = mg42_burst + RandomFloat( mg42_burst_range ); self thread TurretTimer( duration ); self waittill( "turretstatechange" ); self.script_shooting = false; duration = mg42_delay + RandomFloat( mg42_delay_range ); pauseUntilTime = GetTime() + Int( duration * 1000 ); } else { if( turretState != "aim" ) { turretState = "aim"; } self thread TurretTimer( duration ); self waittill( "turretstatechange" ); } } } DoShoot() { self endon( "death" ); self endon( "turretstatechange" ); for( ;; ) { self ShootTurret(); wait( 0.1 ); } } TurretTimer( duration ) { if( duration <= 0 ) { return; } self endon( "turretstatechange" ); wait( duration ); if( IsDefined( self ) ) { self notify( "turretstatechange" ); } } 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( "death" ); 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" ); } } } 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 ); } 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 ) ); 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].keepClaimedNodeInGoal; ai[i].keepClaimedNodeInGoal = false; if( !( ai[i] UseCOverNode( node ) ) ) { ai[i].keepClaimedNodeInGoal = savekeepclaimed; } } } } setDifficulty() { init_mgTurretsettings(); mg42s = GetEntArray( "misc_turret", "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 ) { 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(); 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_shuffle( level.drones[drone.team], 1 ); drone notify( "drone_mg42_fail" ); } get_bestdrone( team ) { 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; } ent = level.drones[team].array[i]; break; } aitarget = self GetTurretTarget(); if( IsDefined( ent ) && IsDefined( aitarget ) && Distance( self.origin, aitarget.origin ) < Distance( self.origin, ent.origin ) ) { ent = undefined; } return ent; } saw_mgTurretLink( nodes ) { possible_turrets = getEntArray( "misc_turret", "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 ]; } 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 ) ); keys = getArrayKeys( turrets ); for ( i=0; i < keys.size; i++ ) { turret = turrets[ keys[ i ] ]; if ( distance( node.origin, turret.origin ) > 75 ) continue; turretForward = anglesToForward( ( 0, turret.angles[ 1 ], 0 ) ); dot = vectorDot( nodeForward, turretForward ); if ( dot < 0.9 ) continue; node.turretInfo = spawnstruct(); node.turretInfo.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; turrets[ keys[ i ] ] = undefined; turret delete(); println("PortableMG: " + turret.weaponinfo + " was set up to be portable."); } } keys = getArrayKeys( turrets ); for ( i=0; i < keys.size; i++ ) { turret = turrets[ keys[ i ] ]; println( "^1!!!ERROR: turret at " + turret.origin + " could not link to any node! You need to make sure that a node is directly behind the mg42 and less than 50 units behind it." ); } } auto_mgTurretLink( nodes ) { possible_turrets = GetEntArray( "misc_turret", "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( !IsDefined( possible_turrets[i].export ) ) { continue; } if( !IsDefined( possible_turrets[i].script_dont_link_turret ) ) { turrets[possible_turrets[i].origin + ""] = possible_turrets[i]; } } 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 ) ); 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." ); turrets[keys[i]] = undefined; } } nodes = undefined; } save_turret_sharing_info() { 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 ) ) { 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 ) ) { strings = Strtok( self.script_turret_ambush, " " ); for( i = 0; i < strings.size; i++ ) { self.shared_turrets["ambush"][strings[i]] = true; } } } 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_scale( 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; } dropTurret(); } turretDetacher() { self endon( "death" ); self endon( "kill_turret_detach_thread" ); self waittill( "dropped_gun" ); self Detach( self.turretModel, level.portable_mg_gun_tag ); } restoreDefaults() { self.run_noncombatanim = undefined; self.run_combatanim = undefined; self set_all_exceptions( get_overloaded_func( "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 ) { self endon( "death" ); self endon( "end_mg_behavior" ); self endon( "stop_updating_enemy_target_pos" ); old_turret_pos = self.turret.origin +( 0, 0, 16 ); dest_pos = new_spot.origin +( 0, 0, 16 ); for( ;; ) { wait( 0.05 ); if( SightTracePassed( ent.origin, dest_pos, 0, undefined ) ) { continue; } angles = VectorToAngles( old_turret_pos - ent.origin ); forward = AnglesToForward( angles ); forward = vector_scale( 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( ;; ) { 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 ) ) { ent.origin = self.last_enemy_sighting_position; return; } forward = AnglesToForward( turret.angles ); for( i = ent.bread_crumbs.size - 3; i >= 0; i-- ) { crumb = ent.bread_crumbs[i]; normal = VectorNormalize( crumb - turret.origin ); dot = VectorDot( forward, normal ); if( dot < 0.75 ) { continue; } ent.origin = crumb; if( SightTracePassed( turret.origin, crumb, 0, undefined ) ) { continue; } break; } } find_a_new_turret_spot( ent ) { array = get_portable_mg_spot( ent ); new_spot = array["spot"]; connection_type = array["type"]; if( !IsDefined( new_spot ) ) { return; } reserve_turret( new_spot ); 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 ); } new_spot SetTargetEntity( ent ); } leave_gun_and_run_to_new_spot( spot ) { assert( spot.reserved == self ); self StopUSeturret(); self animscripts\shared::placeWeaponOn( self.primaryweapon, "none" ); 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 ) { 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; self endon( "death" ); self endon( "dropped_gun" ); setup_anim = get_turret_setup_anim( spot ); self.turretModel = "weapon_mg42_carry"; self notify( "kill_get_gun_back_on_killanimscript_thread" ); self animscripts\shared::placeWeaponOn( self.weapon, "none" ); if( self.team == "axis" ) { self.health = 1; } self.run_noncombatanim = %saw_gunner_run_slow; self.run_combatanim = %saw_gunner_run_fast; self.crouchrun_combatanim = %saw_gunner_run_fast; self Attach( self.turretModel, level.portable_mg_gun_tag ); thread turretDeathDetacher(); 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 ); self set_all_exceptions( maps\_mgturret::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 notify( "kill_turret_detach_thread" ); if( self.team == "axis" ) { 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(); 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( get_overloaded_func( "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() { if( !using_a_turret() ) { clear_exception( "move" ); return; } array = find_connected_turrets( "connected" ); new_spots = array["spots"]; if( !new_spots.size ) { clear_exception( "move" ); return; } turret_node = self.node; 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 ); if( IsDefined( taken_nodes[turret_node.origin + ""] ) ) { return; } } } turret = turret_node.turret; if( IsDefined( turret.reserved ) ) { assert( turret.reserved != self ); return; } reserve_turret( turret ); if( turret.isSetup ) { leave_gun_and_run_to_new_spot( turret ); } else { 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 ); self.turret = spot; self thread mg42_firing( spot ); spot SetMode( "manual_ai" ); spot thread restorePitch(); self.turret = spot; spot.owner = self; 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() { 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; usable_spots = []; spot_exports = GetArrayKeys( spots ); taken_nodes = getTakenNodes(); taken_nodes[self.node.origin + ""] = undefined; 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++ ) { if( spots[export].export + "" != keys[p] ) { continue; } if( IsDefined( spots[export].reserved ) ) { continue; } if( IsDefined( taken_nodes[spots[export].node.origin + ""] ) ) { continue; } if( Distance( self.goalpos, spots[export].origin ) > self.goalradius ) { continue; } usable_spots[usable_spots.size] = spots[export]; } } array = []; 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 = []; for( i = 0; i < usable_spots.size; i++ ) { if( !within_fov( usable_spots[i].origin, usable_spots[i].angles, ent.origin, 0.75 ) ) { continue; } 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(); self.isSetup = true; assert( !IsDefined( self.reserved ) ); self.reserved = undefined; if( IsDefined( self.isvehicleattached ) ) { return; } if( self has_spawnflag(level.SPAWNFLAG_TURRET_PREPLACED) ) { return; } hide_turret(); } hide_turret() { assert( self.isSetup ); self notify( "stop_checking_for_flanking" ); self.isSetup = false; self Hide(); self.solid = false; self MakeTurretUnusable(); self SetDefaultDropPitch( 0 ); self thread restoreDefaultPitch(); } show_turret() { self Show(); self.solid = true; self MakeTurretUsable(); 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 ); } link_turrets( turretArray ) { self endon( "death" ); level.print3d_ran_already = false; if( !IsDefined( turretArray ) || turretArray.size <= 1 ) { return; } while( 1 ) { for( i = 0; i < turretArray.size; i++ ) { if( turretArray[i] IsFiringTurret() ) { self link_turrets_fireall( turretArray[i], turretArray ); } } wait( 0.05 ); } } link_turrets_fireall( leaderTurret, turretArray ) { self endon( "death" ); self.leadTurretState = 0; for( i = 0; i < turretArray.size; i++ ) { if( turretArray[i] != leaderTurret && !turretArray[i] IsFiringTurret() ) { turretArray[i] SetMode( "manual" ); } } while( leaderTurret IsFiringTurret() ) { if( leaderTurret.script_shooting ) { for( i = 0; i < turretArray.size; i++ ) { if( turretArray[i] != leaderTurret && !turretArray[i] IsFiringTurret() ) { turretArray[i] ShootTurret(); } } } wait( 0.1 ); } self notify( "lead_turret_stopped" ); for( i = 0; i < turretArray.size; i++ ) { if( turretArray[i] != leaderTurret && !turretArray[i] IsFiringTurret() ) { turretArray[i] SetMode( "auto_nonai" ); } } } init_mg_animent() { mg42s = GetEntArray( "misc_mg42", "classname" ); turrets = GetEntArray( "misc_turret", "classname" ); turrets = array_combine( mg42s, turrets ); for( i = 0; i < turrets.size; i++ ) { if( IsDefined( turrets[i].script_animent ) ) { turrets[i] thread mg_anim_ent(); } } } mg_anim_ent() { self endon( "stop_mg_anim_ent" ); self endon( "death" ); anim_ent = GetEnt( self.script_animent, "targetname" ); if( IsDefined( anim_ent.script_animname ) ) { anim_ent.animname = anim_ent.script_animname; } else { anim_ent.animname = anim_ent.targetname; } delay = 0.2; intro_time = GetAnimLength( level.scr_anim[anim_ent.animname]["intro"] ) - delay; anim_ent maps\_anim::SetAnimTree(); state = "outro"; for( ;; ) { owner = self GetTurretOwner(); if( !IsDefined( owner ) ) { if( state != "outro" ) { state = "outro"; anim_ent SetFlaggedAnimKnobRestart( "mg_animent_anim", level.scr_anim[anim_ent.animname][state], 1.0, 0.2, 1.0 ); } self waittill( "turretownerchange" ); owner = self GetTurretOwner(); } if( self mg_is_firing( owner ) ) { if( state == "outro" ) { state = "intro"; anim_ent SetFlaggedAnimKnobRestart( "mg_animent_anim", level.scr_anim[anim_ent.animname][state], 1.0, 0.2, 1.0 ); wait( intro_time ); } else if( state == "intro" || state == "loop" ) { state = "loop"; anim_ent SetFlaggedAnimKnob( "mg_animent_anim", level.scr_anim[anim_ent.animname][state], 1.0, 0.2, 1.0 ); } } else if( state != "outro" ) { state = "outro"; anim_ent SetFlaggedAnimKnobRestart( "mg_animent_anim", level.scr_anim[anim_ent.animname][state], 1.0, 0.2, 1.0 ); } wait( delay ); } } mg_is_firing( owner ) { if( !IsDefined( owner ) ) { return false; } if( IsPlayer( owner ) ) { return IsTurretFiring( self ); } else { if( IsDefined( self.doFiring ) && self.doFiring ) { return true; } if( IsDefined( self.script_shooting ) && self.script_shooting ) { return true; } } return false; } drop_turret() { maps\_mgturret::dropTurret(); self animscripts\weaponList::RefillClip(); self.a.needsToRechamber = 0; self notify ("dropped_gun"); maps\_mgturret::restoreDefaults(); } exception_exposed_mg42_portable() { drop_turret(); }