iw6-scripts-dev/maps/mp/mp_ca_red_river_bridge_device.gsc
2024-12-11 11:28:08 +01:00

671 lines
19 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
#include maps\mp\gametypes\_hud_util;
#using_animtree("mp_ca_red_river_animtree");
REDRIVER_NUKE_WEIGHT = 85;
redriverCustomCrateFunc()
{
if(!IsDefined(game["player_holding_level_killstrek"]))
game["player_holding_level_killstrek"] = false;
level.allow_level_killstreak = allowLevelKillstreaks();
if(!level.allow_level_killstreak || game["player_holding_level_killstrek"])
return;
maps\mp\killstreaks\_airdrop::addCrateType( "airdrop_assault", "warhawk_mortars", REDRIVER_NUKE_WEIGHT, maps\mp\killstreaks\_airdrop::killstreakCrateThink, maps\mp\killstreaks\_airdrop::get_friendly_crate_model(), maps\mp\killstreaks\_airdrop::get_enemy_crate_model(), &"KILLSTREAKS_HINTS_WARHAWK_MORTARS" );
level thread watch_for_redriver_nuke_crate();
}
watch_for_redriver_nuke_crate()
{
while(1)
{
level waittill("createAirDropCrate", dropCrate);
if(IsDefined(dropCrate) && IsDefined(dropCrate.crateType) && dropCrate.crateType=="warhawk_mortars")
{
disable_redriver_nuke();
captured = wait_for_capture(dropCrate);
if(!captured)
{
enable_redriver_nuke();
}
else
{
game["player_holding_level_killstrek"] = true;
break;
}
}
}
}
wait_for_capture(dropCrate)
{
result = watch_for_air_drop_death(dropCrate);
return !IsDefined(result);
}
watch_for_air_drop_death(dropCrate)
{
dropCrate endon("captured");
dropCrate waittill("death");
waittillframeend;
return true;
}
enable_redriver_nuke()
{
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", "warhawk_mortars", REDRIVER_NUKE_WEIGHT);
}
disable_redriver_nuke()
{
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", "warhawk_mortars", 0);
}
bridge_device_init()
{
self.device_model = GetEnt( "bridge_device_model", "targetname" );
if ( !IsDefined( self.device_model ) )
{
PrintLn( "ERROR: No bridge_device_model available." );
return;
}
self.mortar_delay_range = 3.0;
self.end_of_match_volley = false;
self.device_det_radius = 600;
self.device_det_splash = 200;
// killcamPos = self.device_model.origin + (0, 0, 800);
killcamPos = ( -1191, -1126, 394 ); // a position in space looking at the truck. I should have used a struct, but not enough time.
self.device_model.killCamEnt = Spawn( "script_model", killcamPos );
self.device_model.killCamEnt SetScriptMoverKillCam( "explosive" );
self thread bridge_device_sequence_waitforhit();
}
bridge_extras_init( bridge_device )
{
thread bridge_event_handle_glass( bridge_device );
thread bridge_event_handle_churchbells( bridge_device.device_model.origin );
thread bridge_event_handle_bell_sounds();
vehicles = get_bridge_vehicles();
if ( vehicles.size > 0 )
{
thread bridge_event_handle_vehicles( vehicles, bridge_device );
thread bridge_event_handle_caralarms( vehicles );
}
thread bridge_device_scramble_radar( bridge_device );
}
bridge_device_scramble_radar( bridge_device )
{
level endon( "bridge_fully_exploded" );
device_scrambler = GetStruct( "device_scrambler", "targetname" );
if ( !IsDefined( device_scrambler ) )
{
PrintLn( "No Device Radar Scrambler" );
return;
}
thread bridge_device_radioactive_sound( device_scrambler.origin + (-5, 0, -155) );
level.device_scrambler_active = true;
while ( 1 )
{
level waittill( "connected", player );
if ( !IsBot( player ) )
player thread run_func_after_spawn( ::bridge_device_static, device_scrambler.origin, bridge_device.device_det_radius );
}
}
bridge_device_radioactive_sound( position )
{
soundEnt = play_loopsound_in_space( "emt_geiger_level1", position );
level waittill( "bridge_fully_exploded" );
soundEnt StopLoopSound();
soundEnt Delete();
}
run_func_after_spawn( func, device_origin, det_radius )
{
self endon( "disconnect" );
self endon( "death" );
self waittill( "spawned_player" );
self thread [[ func ]]( device_origin, det_radius );
}
bridge_device_sequence_waitforhit()
{
self.device_model PlayLoopSound( "device_stage1_loop" );
wait 2.0;
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
Objective_Add( curObjID, "active", self.device_model.origin, "waypoint_radioactive" );
Objective_OnEntity( curObjID, self.device_model );
self.curObjID = curObjID;
self thread bridge_device_activate_at_end_of_match();
level waittill( "bridge_device_activate", player );
disable_redriver_nuke();
wait 2.0;
//self.device_model PlaySound( "device_stage1_start" );
wait 2.0;
self bridge_device_sequence_volley( player );
}
bridge_device_sequence_volley( player )
{
start_org = GetStruct( "mortar_launch_start", "targetname" );
target_org = GetStruct( start_org.target, "targetname" );
while ( 1 )
{
self thread bridge_device_mortar_attack( start_org, target_org, player );
if ( !IsDefined( target_org.target ) )
break;
target_org = GetStruct( target_org.target, "targetname" );
}
}
bridge_device_activate_at_end_of_match()
{
level endon( "bridge_device_activate" );
level waittill ( "spawning_intermission" );
self.mortar_delay_range = 0.5;
self.end_of_match_volley = true;
self bridge_device_sequence_volley( level.players[0] );
}
random_mortars_incoming_sound(org)
{
PlaySoundAtPos( org, "mortar_incoming" );
}
bridge_device_mortar_attack( start_org, end_org, owner )
{
wait RandomFloatRange( 0.0, self.mortar_delay_range );
air_time = RandomFloatRange( 4.0, 5.0 );
if ( self.end_of_match_volley )
air_time = 2.5;
gravity = (0,0,-800);
launch_dir = TrajectoryCalculateInitialVelocity(start_org.origin, end_org.origin, gravity, air_time);
play_fx = true;
dirt_effect_radius = 350;
mortar_model = random_mortars_get_model(start_org.origin);
mortar_model.origin = start_org.origin;
mortar_model.in_use = true;
waitframe();
PlayFXOnTag( getfx("random_mortars_trail"), mortar_model, "tag_fx");
mortar_model.angles = VectorToAngles(launch_dir) * (-1,1,1);
PlaySoundAtPos( start_org.origin, "mortar_launch" );
delayThread(air_time-2.0, ::random_mortars_incoming_sound, end_org.origin);
mortar_model MoveGravity(launch_dir, air_time);
mortar_model waittill("movedone");
if(level.createFX_enabled && !IsDefined(level.players))
level.players = [];
// RadiusDamage doesn't like removed entities, which can happen if the owner quits
if ( !IsDefined( owner ) )
owner = undefined;
mortar_model RadiusDamage( end_org.origin, 350, 750, 500, owner, "MOD_EXPLOSIVE", "warhawk_mortar_mp" );
PlayRumbleOnPosition("artillery_rumble", end_org.origin);
dirt_effect_radiusSq = dirt_effect_radius * dirt_effect_radius;
foreach ( player in level.participants )
{
if ( player isUsingRemote() )
continue;
if ( DistanceSquared( end_org.origin, player.origin ) > dirt_effect_radiusSq )
continue;
if ( player DamageConeTrace( end_org.origin ) )
player thread maps\mp\gametypes\_shellshock::dirtEffect( end_org.origin );
}
if( play_fx )
PlayFX( getfx("mortar_impact_00"), end_org.origin);
StopFXOnTag( getfx("random_mortars_trail"), mortar_model, "tag_fx");
if ( IsDefined( end_org.script_noteworthy ) && (end_org.script_noteworthy == "device_target" ) )
self thread bridge_device_mortar_hit_nuke( owner, mortar_model );
wait 0.05;
mortar_model Delete();
}
bridge_device_mortar_hit_nuke( owner, mortar_model )
{
level notify( "bridge_trigger_explode" );
Objective_Delete( self.curObjID );
Earthquake( 0.85, .5, self.device_model.origin, 2500 );
det_radius = self.device_det_radius;
splash_radius = det_radius + self.device_det_splash;
oversplash_radius = det_radius + 3.0 * self.device_det_splash;
foreach ( agent in level.participants )
{
if ( IsPlayer( agent ) && ( agent.sessionstate != "playing" ))
continue;
agent thread update_bridge_event_player_effects( self.device_model.origin, det_radius, oversplash_radius, self.end_of_match_volley );
agent_distanceSq = DistanceSquared( self.device_model.origin, agent.origin );
if ( agent_distanceSq > splash_radius * splash_radius )
continue;
damage_owner = owner;
if ( !IsDefined( damage_owner ) )
{
// damage_owner might be a removed entity, so clear it out so that the later DoDamage calls will still function
damage_owner = undefined;
}
else if ( IsDefined( damage_owner.team ) && IsDefined( agent.team ) && damage_owner.team == agent.team )
{
damage_owner = mortar_model;
}
if ( agent_distanceSq > det_radius * det_radius )
{
// 2014-01-13 wallace: event should do flat damage and kill wounded enemies outside of det radius
agent DoDamage( agent.maxhealth * 0.9, self.device_model.origin, damage_owner, self.device_model, "MOD_EXPLOSIVE" );
}
else
{
// 2014-01-13 wallace: should do enough damage to kill prone juggernauts
agent DoDamage( 1000, self.device_model.origin, damage_owner, self.device_model, "MOD_EXPLOSIVE" );
}
}
splashRadiusSq = splash_radius * splash_radius;
dogs = maps\mp\agents\_agent_utility::getActiveAgentsOfType( "dog" );
foreach ( dog in dogs )
{
dog_distanceSq = DistanceSquared( self.device_model.origin, dog.origin );
if ( dog_distanceSq <= splashRadiusSq )
{
dog maps\mp\agents\_agent_utility::killDog();
}
}
destroyTargetArray( self.device_model.origin, splashRadiusSq, level.remote_uav );
// it would be nicer to just give each killstreak a test function and an outcome function, and let it figure out how to handle each instance
foreach ( boxType in level.deployable_box )
{
destroyTargetArray( self.device_model.origin, splashRadiusSq, boxtype );
}
destroyTargetArray( self.device_model.origin, splashRadiusSq, level.turrets );
destroyTargetArray( self.device_model.origin, splashRadiusSq, level.uplinks );
destroyTargetArray( self.device_model.origin, splashRadiusSq, level.placedIMS );
destroyTargetArray( self.device_model.origin, splashRadiusSq, level.mines );
self.device_model StopLoopSound( "device_stage1_loop" );
self.device_model NotSolid();
self.device_model Hide();
self.device_model maps\mp\_movers::notify_moving_platform_invalid();
if ( !self.end_of_match_volley )
VisionSetNaked( "mp_ca_red_river_exploding", 0.5 );
PhysicsExplosionSphere( self.device_model.origin, splash_radius, det_radius, 1.0 );
wait 0.5;
level notify( "bridge_fully_exploded" );
maps\mp\_compass::setupMiniMap( "compass_map_mp_ca_red_river_exploded" );
SetExpFog(6.6, 5530, 0.78, 0.81, 0.75, 0.87, 0.77);
if ( !self.end_of_match_volley )
{
if( IsDefined( level.nukeDetonated ) )
{
VisionSetNaked( "", 3.0 );
maps\mp\killstreaks\_nuke::setNukeAftermathVision( 0 );
}
else
{
VisionSetNaked( "mp_ca_red_river_exploded", 3.0 );
}
}
// self.device_model.killCamEnt Delete();
// self.device_model.killCamEnt = undefined;
}
destroyTargetArray( refPos, explosionRadiusSq, targetList )
{
foreach ( target in targetList )
{
distSq = DistanceSquared( refPos, target.origin );
if ( distSq < explosionRadiusSq )
{
target notify( "death" );
}
}
}
update_bridge_event_player_effects( refPoint, det_distance, splash_distance, skip_shock )
{
self PlayRumbleOnEntity( "artillery_rumble" );
wait 0.3;
// 2014-01-14 wallace: we need to calculate this instead of passing in, because the player may have been killed in the last 0.3 seconds
if ( !skip_shock && !( self isUsingRemote() ))
{
shock_time = 0.0; // needs to be 0; players respawning should not be shocked
playerDistSq = DistanceSquared( self.origin, refPoint );
if ( playerDistSq < splash_distance * splash_distance )
{
shock_time = 8.0;
if ( playerDistSq > det_distance * det_distance )
{
// not thrilled about the sqrt
shock_time = shock_time * (1 - ( (sqrt(playerDistSq) - det_distance) / (splash_distance - det_distance)) );
}
}
if ( shock_time > 0.0 )
self shellshock( "mp_ca_red_river_event", shock_time );
}
wait 1.0;
self PlayRumbleOnEntity( "artillery_rumble" );
wait 1.3;
self PlayRumbleOnEntity( "artillery_rumble" );
}
bridge_device_static( static_center, static_dist )
{
self endon( "disconnect" );
wait RandomFloat( 0.5 );
self maps\mp\killstreaks\_emp_common::staticFieldInit();
self childthread bridge_device_static_update( static_center, static_dist );
level waittill( "bridge_fully_exploded" );
self maps\mp\killstreaks\_emp_common::staticFieldSetStrength( 0 );
}
bridge_device_static_update( static_center, static_dist )
{
level endon( "bridge_trigger_explode" );
static_dist_squared = static_dist * static_dist;
while ( 1 )
{
in_range = Length2DSquared( self.origin - static_center ) < static_dist_squared;
self maps\mp\killstreaks\_emp_common::staticFieldSetStrength( in_range );
wait( 0.5 );
}
}
random_mortars_get_model(origin)
{
mortar_model = spawn("script_model", origin);
mortar_model SetModel("projectile_rpg7");
return mortar_model;
}
get_bridge_vehicles()
{
vehicles = GetScriptableArray( "vehicle_pickup_destructible_mp_rr", "targetname" );
if ( vehicles.size <= 0 )
PrintLn( "No destructable vehicles found." );
return vehicles;
}
bridge_event_handle_vehicles( vehicles, bridge_device )
{
blow_distanceSq = bridge_device.device_det_radius + (bridge_device.device_det_splash * 2.0);
blow_distanceSq = blow_distanceSq * blow_distanceSq;
device_origin = bridge_device.device_model.origin;
foreach ( vehicle in vehicles )
{
if ( DistanceSquared( vehicle.origin, device_origin ) <= blow_distanceSq )
vehicle thread bridge_event_handle_vehicle();
}
}
bridge_event_handle_vehicle()
{
self endon( "death" );
level waittill( "bridge_fully_exploded" );
// self SetScriptablePartState( "vehicle", "damaged" ); // vehicle_pickup_truck_destructible_dlc has states named
self SetScriptablePartState( 0, 3 ); // vehicle_pickup_truck_destructible does not have states named
}
bridge_event_handle_caralarms( vehicles )
{
array_thread( vehicles, ::bridge_event_update_caralarm );
}
bridge_event_update_caralarm()
{
self endon( "death" );
level waittill ( "bridge_fully_exploded" );
wait RandomFloatRange( 0.75, 1.25 );
self thread bridge_event_play_caralarm();
}
bridge_event_play_caralarm()
{
sound_ent = play_loopsound_in_space( "rr_car_alarm", self GetTagOrigin( "tag_engine" ) );
alarm_time = RandomFloatRange( 12.0, 16.0 );
self waittill_any_timeout( alarm_time, "death" );
sound_ent StoploopSound( "rr_car_alarm" );
sound_ent PlaySound( "rr_car_alarm_off" );
wait (5);
sound_ent Delete();
}
bridge_event_handle_glass( bridge_device )
{
windows = GetGlassArray( "bridge_event_glass" );
if ( windows.size <= 0 )
{
PrintLn( "No Windows found" );
return;
}
blast_origin = bridge_device.device_model.origin;
level waittill ( "bridge_fully_exploded" );
foreach ( window in windows )
DestroyGlass( window, GetGlassOrigin( window ) - blast_origin );
}
bridge_event_handle_churchbells( device_origin )
{
// level endon( "game_ended" );
bells = GetEntArray( "church_bell", "targetname" );
if (bells.size <= 0 )
{
PrintLn( "No church bells found." );
return;
}
level.small_bells = 0;
array_thread( bells, ::redriver_detecthit_churchbell );
array_thread( bells, ::bridge_event_update_churchbell, device_origin );
}
redriver_detecthit_churchbell()
{
self SetCanDamage( true );
self.is_swaying = false;
sound_alias = bell_sound_alias( self.script_noteworthy );
while ( 1 )
{
self waittill( "damage", amount, attacker, direction_vec, hit_point, type );
if ( !IsDefined( attacker ) || !IsPlayer( attacker ) ) // somehow, the heli sniper helicopter will trigger this.
continue;
current_weapon = attacker GetCurrentWeapon();
if ( !IsDefined( current_weapon ) || (WeaponClass( current_weapon ) != "sniper") )
continue;
self PlaySOund( sound_alias );
self thread redriver_update_hitsway_churchbell( attacker );
wait 0.5;
}
}
redriver_update_hitsway_churchbell( attacker )
{
level endon( "bridge_fully_exploded" );
if ( self.is_swaying )
return;
vec = AnglesToRight( self.angles );
vec2 = VectorNormalize( attacker.origin - self.origin );
swing_dir = vectordot( vec, vec2 ) * 2.0;
if ( swing_dir > 0.0 )
swing_dir = Max( 0.3, swing_dir );
else
swing_dir = Min( -0.3, swing_dir );
self.is_swaying = true;
self RotateRoll( 15 * swing_dir, 1.0, 0, 0.5 );
wait 1;
self RotateRoll( -25 * swing_dir, 2.0, 0.5, 0.5 );
wait 2;
self RotateRoll( 15 * swing_dir, 1.5, 0.5, 0.5 );
wait 1.5;
self RotateRoll( -5 * swing_dir, 1.0, 0.5, 0.5 );
wait 1.0;
self.is_swaying = false;
}
bridge_event_handle_bell_sounds()
{
level waittill( "bridge_device_activate" ); // on event start
//DR 01-13-2014: playing two sounds in space here
PlaySoundAtPos( (-510, -1617, 556), "scn_church_bells_long_large" );
PlaySoundAtPos( (-480, -1986, 423), "scn_church_bells_long_small" );
//DR 01-13-2014: end of fix for bells for level killstreak
}
bridge_event_update_churchbell( blast_origin )
{
sound_alias = bell_sound_alias( self.script_noteworthy );
level waittill( "bridge_device_activate" ); // on event start
// level waittill ( "bridge_fully_exploded" ); // on event end
//DR 01/13/2014: playing sounds at only the two bell positions we want - commenting out others...
// // 2014-01-10 wallace: this is hacky and should probably be built into the script_noteworthy
// if ( sound_alias == "rr_church_bell" )
// {
// self PlaySound( "scn_church_bells_long_large" );
// }
// else if ( sound_alias == "rr_church_bell_smaller" ) // this assumes small/smaller/smallest bells are always grouped together
// {
// self PlaySound( "scn_church_bells_long_small" );
// }
//DR 01/13/2014: done with fix for bells during killstreak
vec = AnglesToRight( self.angles );
vec2 = VectorNormalize( blast_origin - self.origin );
swing_dir = vectordot( vec, vec2 ) * 2.0;
if ( swing_dir > 0.0 )
swing_dir = Max( 0.7, swing_dir );
else
swing_dir = Min( -0.7, swing_dir );
ring_offset = RandomFloatRange( 0.1, 0.8 );
self.is_swaying = true;
self.angles = (self.angles[0], self.angles[1], 0);
self RotateRoll( 40 * swing_dir, ring_offset, 0, ring_offset );
wait ring_offset;
self RotateRoll( -70 * swing_dir, 2.0, 0.5, 0.5 );
wait 1.75;
wait 0.25;
self RotateRoll( 60 * swing_dir, 2.0, 0.5, 0.5 );
wait 1.75;
wait 0.25;
self RotateRoll( -50 * swing_dir, 2.0, 0.5, 0.5 );
wait 1.75;
wait 0.25;
self RotateRoll( 40 * swing_dir, 2.0, 0.5, 0.5 );
wait 1.75;
wait 0.25;
self RotateRoll( -30 * swing_dir, 2.0, 0.5, 0.5 );
wait 1.75;
wait 0.25;
self RotateRoll( 20 * swing_dir, 1.5, 0.5, 0.5 );
wait 1.5;
self RotateRoll( -15 * swing_dir, 1.3, 0.5, 0.5 );
wait 1.3;
self RotateRoll( 5 * swing_dir, 1.0, 0.5, 0.5 );
wait 1.0;
self.is_swaying = false;
}
bell_sound_alias( bell_info )
{
sound_alias = "rr_church_bell";
if ( IsDefined( bell_info ) && (bell_info == "small") )
{
if ( !IsDefined( level.small_bells ) )
level.small_bells = 0;
level.small_bells++;
if ( level.small_bells == 1 )
sound_alias = "rr_church_bell_smallest";
else if ( level.small_bells == 2 )
sound_alias = "rr_church_bell_smaller";
else
sound_alias = "rr_church_bell_small";
}
return sound_alias;
}