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

1423 lines
36 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
main()
{
maps\mp\mp_ca_rumble_precache::main();
maps\createart\mp_ca_rumble_art::main();
maps\mp\mp_ca_rumble_fx::main();
level thread update_mortars(); //setup and update.
level.air_raid_active = false;
level.mapCustomCrateFunc = ::rumbleCustomCrateFunc;
level.mapCustomKillstreakFunc = ::rumbleCustomKillstreakFunc;
level.mapCustomBotKillstreakFunc = ::rumbleCustomBotKillstreakFunc;
maps\mp\_load::main();
// AmbientPlay( "ambient_mp_setup_template" );
maps\mp\_compass::setupMiniMap( "compass_map_mp_ca_rumble" );
setdvar( "r_lightGridEnableTweaks", 1 );
setdvar( "r_lightGridIntensity", 1.33 );
if ( level.ps3 )
{
SetDvar( "sm_sunShadowScale", "0.55" ); // ps3 optimization
SetDvar( "sm_sunsamplesizenear", ".15" );
}
else if ( level.xenon )
{
SetDvar( "sm_sunShadowScale", "0.56" +
"" ); // optimization
SetDvar( "sm_sunsamplesizenear", ".22" );
}
else
{
SetDvar( "sm_sunShadowScale", "0.9" ); // optimization
SetDvar( "sm_sunsamplesizenear", ".27" );
}
setdvar_cg_ng( "r_specularColorScale", 1.7, 5 );
game["attackers"] = "allies";
game["defenders"] = "axis";
game[ "allies_outfit" ] = "urban";
game[ "axis_outfit" ] = "woodland";
fixMLGCameraPosition();
thread setup_fish();
thread setup_destructibles();
thread setup_metal_detectors();
thread setup_watertanks();
thread setup_bouys();
thread setup_dock_boats();
thread setup_monitors();
thread update_destroyer();
thread update_trolley();
thread update_lighthouse_light();
thread setup_fountain_fx();
thread update_artillery_fx();
thread update_heli_fx();
thread update_flybyjet_fx();
thread maps\mp\_dlcalienegg::setupEggForMap( "alienEasterEgg" );
level thread initExtraCollision();
}
initExtraCollision()
{
collision1 = GetEnt( "clip128x128x256", "targetname" );
collision1Ent = spawn( "script_model", (-459.25, 1848, 487.75) );
collision1Ent.angles = ( 270, 322.793, 37.2067);
collision1Ent CloneBrushmodelToScriptmodel( collision1 );
collision2 = GetEnt( "clip256x256x128", "targetname" );
collision2Ent = spawn( "script_model", (-1984, 344, -120) );
collision2Ent.angles = (0,0,0);
collision2Ent CloneBrushmodelToScriptmodel( collision2 );
}
RUMBLE_MORTARS_WEIGHT = 85;
rumbleCustomCrateFunc()
{
if(!IsDefined(game["player_holding_level_killstrek"]))
game["player_holding_level_killstrek"] = false;
if(!allowLevelKillstreaks() || game["player_holding_level_killstrek"])
return;
maps\mp\killstreaks\_airdrop::addCrateType( "airdrop_assault", "warhawk_mortars", RUMBLE_MORTARS_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_rumble_mortars_crate();
}
watch_for_rumble_mortars_crate()
{
while(1)
{
level waittill("createAirDropCrate", dropCrate);
if(IsDefined(dropCrate) && IsDefined(dropCrate.crateType) && dropCrate.crateType=="warhawk_mortars")
{
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", "warhawk_mortars", 0);
captured = wait_for_capture(dropCrate);
if(!captured)
{
//reEnable warhawk mortars care packages if it expires with out anyone picking it up
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", "warhawk_mortars", RUMBLE_MORTARS_WEIGHT);
}
else
{
//Once its picked up it needs to remain off.
game["player_holding_level_killstrek"] = true;
break;
}
}
}
}
//death and capture are sent on the same frame but death is processed first :(
wait_for_capture(dropCrate)
{
result = watch_for_air_drop_death(dropCrate);
return !IsDefined(result); //If !isdefined the captured notify was also sent.
}
watch_for_air_drop_death(dropCrate)
{
dropCrate endon("captured");
dropCrate waittill("death");
waittillframeend;
return true;
}
rumbleCustomKillstreakFunc()
{
AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/Care Package/Rumble Mortars\" \"set scr_devgivecarepackage warhawk_mortars; set scr_devgivecarepackagetype airdrop_assault\"\n");
AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/Rumble Mortars\" \"set scr_givekillstreak warhawk_mortars\"\n");
level.killStreakFuncs[ "warhawk_mortars" ] = ::tryUseRumbleMortars;
level.killstreakWeildWeapons["warhawk_mortar_mp"] ="warhawk_mortars";
}
rumbleCustomBotKillstreakFunc()
{
AddDebugCommand("devgui_cmd \"MP/Bots(Killstreak)/Level Events:5/Rumble Mortars\" \"set scr_testclients_givekillstreak warhawk_mortars\"\n");
maps\mp\bots\_bots_ks::bot_register_killstreak_func( "warhawk_mortars", maps\mp\bots\_bots_ks::bot_killstreak_simple_use );
}
tryUseRumbleMortars(lifeId, streakName)
{
if(level.air_raid_active)
{
self iPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
return false;
}
game["player_holding_level_killstrek"] = false;
level notify("rumble_mortar_killstreak", self);
return true;
}
mortars_activate_at_end_of_match()
{
level endon( "rumble_mortar_killstreak" );
level waittill ( "spawning_intermission" );
level.ending_flourish = true;
mortar_fire(0.1,0.3,6,level.players[0]);
}
update_mortars()
{
level endon("stop_dynamic_events");
waitframe(); //allow load main to finish
//Build list of structs
level.mortar_sources = getstructarray("rumble_mortar_source", "targetname");
foreach(source in level.mortar_sources)
{
if(!IsDefined(source.radius))
source.radius = 300;
}
level.mortar_targets = getstructarray("rumble_mortar_target", "targetname");
foreach(mortart_target in level.mortar_targets)
{
if(!IsDefined(mortart_target.radius))
mortart_target.radius = 100;
}
while(1)
{
level.air_raid_active = false;
level.air_raid_team_called = "none";
thread mortars_activate_at_end_of_match();
level waittill("rumble_mortar_killstreak", player);
level.air_raid_active = true;
level.air_raid_team_called = player.team;
thread mortar_siren(10);
wait 3; //Delay between siren start and mortar fire
mortar_fire(.5,.6, 25, player);
}
}
mortar_siren(siren_time)
{
if(!IsDefined(level.mortar_siren_ent))
{
level.mortar_siren_ent = getEnt("mortar_siren", "targetname");
}
if(IsDefined(level.mortar_siren_ent))
{
level.mortar_siren_ent PlaySound("mortar_siren");
}
wait siren_time;
if(IsDefined(level.mortar_siren_ent))
{
level.mortar_siren_ent StopSounds();
}
}
mortar_fire(delay_min, delay_max, mortar_time_sec, owner)
{
motar_strike_end_time = GetTime() + mortar_time_sec*1000;
//pick team appropriate for owner team
source_structs = random_mortars_get_source_structs(level.air_raid_team_called);
if (source_structs.size <= 0)
{
PrintLn("Rumble Mortars: Didn't find any sources: targetname = rumble_mortar_source");
return;
}
air_raid_num = 0;
while(motar_strike_end_time>GetTime())
{
mortars_per_loop = 12;
mortars_launched = 0;
foreach(player in level.players)
{
if(!isReallyAlive(player))
continue;
if(level.teamBased)
{
if(player.team == level.air_raid_team_called)
continue;
}
else
{
if( IsDefined( owner ) && player == owner)
continue;
}
if(player.spawnTime+8000>GetTime())
continue;
vel = player GetVelocity();
mortar_air_time = RandomFloatRange(3,4);
mortar_target_pos = player.origin + (vel*mortar_air_time);
nodes_near = GetNodesInRadiusSorted(mortar_target_pos,100,0,60);
foreach(node in nodes_near)
{
if(NodeExposedToSky(node))
{
source_struct = random(source_structs);
if( random_mortars_fire( source_struct.origin, node.origin, undefined, owner, true, true ) )
{
wait RandomFloatRange(delay_min, delay_max);
mortars_launched++;
break;
}
}
}
}
//If there weren't enough player targets, drop the rest randomly:
if (level.mortar_targets.size > 0)
{
source_structs = array_randomize(source_structs);
while(mortars_launched<mortars_per_loop)
{
source_struct = source_structs[air_raid_num];
air_raid_num++;
if(air_raid_num>=source_structs.size)
air_raid_num = 0;//loop
target_struct = random(level.mortar_targets);
start = random_point_in_circle(source_struct.origin, source_struct.radius);
end = random_point_in_circle(target_struct.origin, target_struct.radius);
thread random_mortars_fire( start, end, undefined, owner, false, true);
wait RandomFloatRange(delay_min, delay_max);
mortars_launched++;
}
}
else
{
break; // no targets!
}
}
}
random_point_in_circle(origin, radius)
{
if(radius>0)
{
rand_dir = AnglesToForward((0,RandomFloatRange(0,360),0));
rand_radius = RandomFloatRange(0, radius);
origin = origin + (rand_dir*rand_radius);
}
return origin;
}
random_mortars_fire( start_org, end_org, air_time, owner, trace_test, play_fx )
{
PlaySoundAtPos( start_org, "mortar_launch" );
gravity = (0,0,-800);
if(!IsDefined(air_time))
{
if ( IsDefined( level.ending_flourish ) && level.ending_flourish )
air_time = 2.5;
else
air_time = RandomFloatRange( 10.0, 12.0 );
}
launch_dir = TrajectoryCalculateInitialVelocity(start_org, end_org, gravity, air_time);
if(IsDefined(trace_test) && trace_test)
{
delta_height = TrajectoryComputeDeltaHeightAtTime(launch_dir[2], -1*gravity[2], air_time/2);
trace_point = ((end_org - start_org)/2) + start_org + (0,0,delta_height);
//self thread drawLine(trace_point, end_org, 60*10, (0,1,0));
if(BulletTracePassed(trace_point, end_org, false, undefined))
{
thread random_mortars_fire_run( start_org, end_org, air_time, owner, launch_dir, play_fx );
return true;
}
else
{
return false;
}
}
random_mortars_fire_run( start_org, end_org, air_time, owner, launch_dir, play_fx );
}
random_mortars_fire_run( start_org, end_org, air_time, owner, launch_dir, play_fx )
{
dirt_effect_radius = 350;
mortar_model = random_mortars_get_model(start_org);
mortar_model.origin = start_org;
mortar_model.in_use = true;
waitframe();//Model may have just spawned
PlayFXOnTag( getfx("random_mortars_trail"), mortar_model, "tag_fx");
mortar_model.angles = VectorToAngles(launch_dir) * (-1,1,1);
delayThread(air_time-2.0, ::random_mortars_incoming_sound, end_org);
mortar_model MoveGravity(launch_dir, air_time - 0.05); // dial back by a frame so the mortar/killcam doesn't go below ground.
mortar_model waittill("movedone");
if(level.createFX_enabled && !IsDefined(level.players))
level.players = [];
if(IsDefined(owner))
{
mortar_model RadiusDamage(end_org, 250, 750, 500, owner, "MOD_EXPLOSIVE", "warhawk_mortar_mp");
}
else
{
mortar_model RadiusDamage(end_org, 140, 5, 5, undefined, "MOD_EXPLOSIVE", "warhawk_mortar_mp");
}
PlayRumbleOnPosition("artillery_rumble", end_org);
//Dirt effect on Players:
dirt_effect_radiusSq = dirt_effect_radius * dirt_effect_radius;
foreach ( player in level.players )
{
if ( player isUsingRemote() )
{
continue;
}
if ( DistanceSquared( end_org, player.origin ) > dirt_effect_radiusSq )
{
continue;
}
if ( player DamageConeTrace( end_org ) )
{
player thread maps\mp\gametypes\_shellshock::dirtEffect( end_org );
}
}
if( play_fx )
{
PlayFX( getfx("mortar_impact_00"), end_org);
}
mortar_model Delete();
}
random_mortars_incoming_sound(org)
{
PlaySoundAtPos( org, "mortar_incoming" );
}
random_mortars_get_model(origin)
{
mortar_model = spawn("script_model", origin);
mortar_model SetModel("projectile_rpg7");
return mortar_model;
}
random_mortars_get_source_structs( owner_team )
{
source_structs = [];
if( level.teamBased )
{
foreach( struct in level.mortar_sources )
{
if (IsDefined(struct.script_team) && struct.script_team == owner_team )
source_structs[source_structs.size] = struct;
}
}
//No approprate team source, just use any:
if (source_structs.size == 0)
source_structs = level.mortar_sources;
return source_structs;
}
setup_destructibles()
{
orcas = GetEntArray("dest_orca", "targetname");
sharks = GetEntArray("dest_shark", "targetname");
if(sharks.size)
{
foreach(shark in sharks)
shark thread update_destructibles(level._effect["vfx_mp_ca_rumble_shark_hit"], level._effect["vfx_mp_ca_rumble_shark_death"]);
}
if(orcas.size)
{
foreach(orca in orcas)
orca thread update_destructibles(level._effect["vfx_mp_ca_rumble_orca_hit"], level._effect["vfx_mp_ca_rumble_orca_death"]);
}
}
update_destructibles(effect_id, death_effect_id)
{
//PrintLn( "Spawning a destructible!" );
self Show();
self SetCanDamage( true );
explosionDirection = undefined;
hitCounter = RandomIntRange( 2, 4 );
while ( hitCounter > 0 )
{
self waittill( "damage", amount, attacker, direction_vec, hit_point, damage_type );
hitCounter--;
explosionDirection = direction_vec;
thread play_hit( effect_id, hit_point, direction_vec);
if ( IsSubStr( damage_type, "MELEE") || IsSubStr( damage_type, "GRENADE" ) )
{
hitCounter = 0;
}
else if ( IsSubStr( damage_type, "BULLET" ) )
{
if ( amount > 60.0 )
{
hitCounter = 0;
}
else
{
if ( IsDefined( attacker ) && IsDefined( attacker GetCurrentWeapon() ) && ( WeaponClass( attacker GetCurrentWeapon() ) == "sniper" ) )
{
hitCounter = 0;
}
}
}
}
// if this wasn't a grenade explosion, don't fire off the final effect again
if (!IsDefined(explosionDirection))
self waittill( "damage", amount, attacker, direction_vec, hit_point, damage_type );
else
direction_vec = explosionDirection;
// hide it, set it so it can't be damaged
self Hide();
self SetCanDamage( false);
// play the final hit effect at the object's origin
thread play_hit( death_effect_id, self GetOrigin(), direction_vec);
//PrintLn("A destructible has been destroyed");
//wait 5.0;
//self thread update_destructibles(effect_id, death_effect_id);
}
play_hit( effect_id, spawn_point, spawn_dir)
{
//PrintLn("Playing hit!");
vfx_ent = SpawnFx( effect_id, spawn_point, AnglesToForward( spawn_dir ), AnglesToUp( spawn_dir ) );
TriggerFX( vfx_ent );
wait 5.0;
vfx_ent Delete();
}
#using_animtree( "mp_ca_rumble_animtree" );
setup_fish()
{
//PrintLn( "OZZ>***** Getting fish *****" );
// get the fish entity
fish = GetEnt( "bannerfish", "targetname");
// if it is defined, thread it
if (IsDefined(fish))
{
fish thread update_fish();
}
}
update_fish()
{
//PrintLn("OZZ>Entering fish updaters");
while ( 1 )
{
// pick a random integer
testInt = RandomInt(3);
self ScriptModelClearAnim();
if (testInt == 0)
{
//PrintLn("OZZ>Playing Animation A");
self ScriptModelPlayAnim("mp_ca_rumble_fish_swim_anim");
wait( GetAnimLength( %mp_ca_rumble_fish_swim_anim ) );
}
else if (testInt == 1)
{
//PrintLn("OZZ>Playing Animation B");
self ScriptModelPlayAnim("mp_ca_rumble_fish_swim_anim_b");
wait( GetAnimLength( %mp_ca_rumble_fish_swim_anim_b ) );
}
else
{
//PrintLn("OZZ>Playing Animation C");
self ScriptModelPlayAnim("mp_ca_rumble_fish_swim_anim_c");
wait( GetAnimLength( %mp_ca_rumble_fish_swim_anim_c ) );
}
}
}
setup_metal_detectors()
{
metal_detectors = GetEntArray( "metal_detector_trigger", "targetname" );
if ( metal_detectors.size > 0 )
array_thread( metal_detectors, ::update_metal_detector );
}
update_metal_detector()
{
self endon( "detector_destroyed" );
my_devices = [];
detector_devices = GetEntArray( "metal_detector_device", "targetname" );
foreach ( device in detector_devices )
{
if ( DistanceSquared( device.origin, self.origin ) < 10000.0 )
{
device.md_health = 100.0;
device.light_on = GetEnt( device.target, "targetname" );
device.light_off = GetEnt( device.light_on.target, "targetname" );
device.light_broke = GetEnt( device.light_off.target, "targetname" );
device thread metal_detector_damage_monitor( self );
my_devices = array_add( my_devices, device );
}
}
self thread metal_detector_monitor_alive( my_devices );
while ( 1 )
{
foreach ( device in my_devices )
device thread metal_detector_on();
self waittill( "trigger" );
foreach ( device in my_devices )
device thread metal_detector_off();
wait 5.0;
}
}
metal_detector_monitor_alive( my_devices )
{
self waittill( "detector_damaged" );
foreach ( device in my_devices )
device DoDamage( 10000, device.origin );
self notify( "detector_destroyed" );
}
metal_detector_damage_monitor( metal_detector )
{
self.light_on thread metal_detectorpart_damage_monitor( self );
self.light_off thread metal_detectorpart_damage_monitor( self );
self SetCanDamage( true );
while ( self.md_health > 0.0 )
{
self waittill( "damage", amount, attacker, direction_vec, point, type );
if ( IsPlayer( attacker ) )
self.maintain_fx = true;
self.md_health -= amount;
}
self thread metal_detector_broke( metal_detector );
}
metal_detectorpart_damage_monitor( parent )
{
parent endon( "detector_destroyed" );
self SetCanDamage( true );
while ( 1 )
{
self waittill( "damage", amount, attacker, direction_vec, point, type );
parent DoDamage( 10000, parent.origin );
}
}
metal_detector_on()
{
if ( self.md_health <= 0.0 )
return;
self.light_on Show();
self.light_off Hide();
self.light_broke Hide();
self PlaySound( "metaldetector_reset" );
}
metal_detector_off()
{
if ( self.md_health <= 0.0 )
return;
self.light_on Hide();
self.light_off Show();
self.light_broke Hide();
self PlaySound( "metaldetector_alarm" );
}
metal_detector_broke( metal_detector )
{
metal_detector notify( "detector_damaged" );
self.light_on Hide();
self.light_off Hide();
self.light_broke Show();
if ( IsDefined( self.maintain_fx ) && ( self.maintain_fx == true ) )
{
while ( 1 )
{
self.light_broke PlaySound( "metaldetector_sparks" );
PlayFX( level._effect[ "vfx_metaldetector_explosion" ], self.light_broke.origin + (0.0, 0.0, 7.5) );
wait RandomFloatRange( 45.0, 150.0 );
}
}
}
setup_bouys()
{
bouys = GetEntArray( "harbor_bouy", "targetname" );
if ( bouys.size <= 0 )
{
PrintLn( "No bouys found" );
return;
}
array_thread( bouys, ::update_bouy );
}
update_bouy()
{
start_point = self.origin;
start_rot = self.angles;
bob_rateSq = 15.0 * 15.0;
while( 1 )
{
offset = ( 10.0, 10.0, 0.0 ) + randomvector( 10.0 );
offset *= ( 3.0, 3.0, 2.0 );
dest = start_point + offset;
time = Max( 3.0, DistanceSquared( self.origin, dest ) / bob_rateSq ); // the math isn't perfect, but it's much faster
self RotateTo( VectorToAngles( dest - start_point ), time, 1.5, 1.5 );
self MoveTo( dest, time, 1.5, 1.5 );
wait time;
}
}
setup_dock_boats()
{
boats = GetEntArray( "dock_boats", "targetname" );
if ( boats.size <= 0 )
{
PrintLn( "No dock boats found" );
return;
}
array_thread( boats, ::update_dock_boat );
}
update_dock_boat()
{
start_rot = self.angles;
bobbing_down = true;
tilt_rate = 1.5;
while( 1 )
{
tilt = ( 0.0, 0.0, RandomFloatRange( 6.0, 8.0 ) );
time = Max( 2.0, tilt[2] / tilt_rate );
if ( bobbing_down )
tilt *= -1.0;
bobbing_down = !bobbing_down;
self RotateTo( start_rot + tilt, time, 1.0, 1.0 );
wait time;
}
}
setup_monitors()
{
monitors = GetEntArray( "rumble_monitor", "targetname" );
if ( monitors.size <= 0 )
{
PrintLn( "No monitors found" );
return;
}
array_thread( monitors, ::update_monitor );
}
update_monitor()
{
damage_state = GetEnt( self.target, "targetname" );
if ( !IsDefined( damage_state ) )
{
PrintLn( "No monitor damage state found." );
return;
}
self Show();
self SetCandamage( true );
damage_state Hide();
self waittill( "damage" );
self Hide();
damage_state Show();
damage_state PlaySound( "flatscreen_sparks" );
PlayFX( level._effect[ "vfx_flatscreen_explosion" ], damage_state.origin );
}
setup_watertanks()
{
level.tank_hitfx_throttle = 600;
level.tank_hitfx_throttle_max = 1200;
level.next_tank_hitfx_time = -1.0;
watertanks = GetEntArray( "watertank_glass", "targetname" );
if ( watertanks.size > 0 )
{
for ( i=0; i<watertanks.size; i++ )
watertanks[i] thread update_watertank( i );
}
watertanks = GetEntArray( "watertank_invulnerable", "targetname" );
array_thread( watertanks, ::update_watertank_invulnerable );
}
update_watertank( index )
{
water_surface = GetEnt( self.target, "targetname" );
if ( !IsDefined( water_surface ) )
{
PrintLn( "Water tank is missing a water_surface" );
return;
}
water_bottom = GetEnt( water_surface.target, "targetname" );
if ( !IsDefined( water_bottom ) )
{
PrintLn( "Water tank is missing a water_bottom" );
return;
}
broken_glass = GetEnt( water_bottom.target, "targetname" );
if ( !IsDefined( broken_glass ) )
{
PrintLn( "Water tank is missing a broken glass mesh" );
return;
}
water_surface thread update_watertank_fish();
water_surface.destroyExplosiveOnCollision = false;
broken_glass Hide();
self SetCanDamage( true );
self.tank_damage = 0;
surface_offset = 41.5;
lowest_hit_z = water_surface.origin[2] + surface_offset;
drain_rate = 2.0;
drain_time = 4.0;
start_time = 1.0;
stop_time = 2.0;
while ( self.tank_damage < 1200.0 )
{
self waittill( "damage", amount, attacker, direction_vec, hit_point, damage_type );
// PrintLn( "Hit at " + amount + " by " + attacker.code_classname + " from " + direction_vec + " at " + hit_point + " by " + damage_type );
self.tank_damage += amount;
if ( !IsSubStr( damage_type, "BULLET" ) )
continue;
if ( !can_allocate_new_tank_crack() )
continue;
current_surface = water_surface.origin[2] + surface_offset;
if ( hit_point[2] >= current_surface )
continue;
hit_angles = get_watertank_hit_angle( attacker, direction_vec, hit_point );
if ( !IsDefined( hit_angles ) )
continue;
drain_limit = Min( drain_time * drain_rate, current_surface - water_bottom.origin[2] );
actual_move_z = Max( water_bottom.origin[2], Max( hit_point[2], current_surface - drain_limit ) );
move_dist = actual_move_z - current_surface;
move_time = Abs( move_dist ) / drain_rate;
move_time = Max( move_time, start_time + stop_time );
self thread spawn_watertank_hit( level._effect["vfx_watertank_bullet_hit"], attacker, hit_angles, hit_point, move_time, water_surface, surface_offset );
if ( actual_move_z >= lowest_hit_z )
continue;
lowest_hit_z = actual_move_z;
water_surface MoveZ( move_dist, move_time, start_time, stop_time );
}
self PlaySound( "water_tank_splash" );
water_surface notify ( "tank_destroyed" );
wait 0.05;
attachedExplosives = self GetLinkedChildren();
foreach ( explosive in attachedExplosives )
{
explosive notify( "detonateExplosive" );
}
self Delete();
water_surface Delete();
broken_glass Show();
broken_glass ScriptModelPlayAnim( "mp_ca_rumble_glass_anim" );
if ( index == 0 )
{
PlayFX( level._effect["vfx_glass_shatter_splash"], (-46.0003, 1409.25, 138.056), AnglesToForward( (0,0,0) ), AnglesToUp( (0,0,0) ) );
PlayFX( level._effect["vfx_glass_ground_splash"], (-39.7033, 1414.08, 89), AnglesToForward( (270,0,0) ), AnglesToUp( (0,0,0) ) );
}
else
{
PlayFX( level._effect["vfx_glass_shatter_splash"], (-47.9208, 1144.29, 141.575), AnglesToForward( (0,0,0) ), AnglesToUp( (0,0,0) ) );
PlayFX( level._effect["vfx_glass_ground_splash"], (-51.5828, 1139.69, 89), AnglesToForward( (270,0,0) ), AnglesToUp( (0,0,0) ) );
}
}
update_watertank_fish()
{
self endon( "death" );
wait 2.0;
effect_id = level._effect["vfx_fish_school"];
fish_ent = Spawn( "script_model", self.origin );
fish_ent SetModel( "tag_origin" );
fish_ent LinkTo( self );
self thread maintain_watertank_fish( effect_id, fish_ent );
self waittill( "tank_destroyed" );
KillFXOnTag( effect_id, fish_ent, "tag_origin" );
wait 0.05;
fish_ent Delete();
}
maintain_watertank_fish( effect_id, fish_ent )
{
self endon( "tank_destroyed" );
while ( 1 )
{
PlayFXOnTag( effect_id, fish_ent, "tag_origin" );
wait 5.0;
StopFXOnTag( effect_id, fish_ent, "tag_origin" );
wait 1.0;
}
}
spawn_watertank_hit( effect_id, attacker, hit_angles, hit_point, drain_time, water_surface, surface_offset )
{
allocate_new_tank_crack();
water_fx_ent = SpawnFx( effect_id, hit_point, hit_angles );
TriggerFX( water_fx_ent );
water_fx_ent PlaySound( "water_tank_hit" );
stop_time = GetTime() + drain_time * 1000.0;
water_fx_ent monitor_watertank_hit( water_surface, stop_time, surface_offset );
water_fx_ent StopSounds();
wait 0.05;
water_fx_ent Delete();
}
monitor_watertank_hit( water_surface, stop_time, surface_offset )
{
water_surface endon( "tank_destroyed" );
while ( GetTime() < stop_time )
{
if ( self.origin[2] >= ((water_surface.origin[2] + surface_offset) - 1.0) )
break;
wait 0.05;
}
}
update_watertank_invulnerable()
{
self SetCanDamage( true );
while ( 1 )
{
self waittill( "damage", amount, attacker, direction_vec, hit_point, damage_type );
if ( !IsSubStr( damage_type, "BULLET" ) )
continue;
if ( !can_allocate_new_tank_crack() )
continue;
hit_angles = get_watertank_hit_angle( attacker, direction_vec, hit_point );
if ( !IsDefined( hit_angles ) )
continue;
allocate_new_tank_crack();
hit_angles = VectorToAngles( hit_angles );
PlayFX( level._effect["vfx_watertank_bullet_hit"], hit_point, AnglesToForward( hit_angles ), AnglesToUp( hit_angles ) );
PlaySoundAtPos( hit_point, "water_tank_hit" );
}
}
get_watertank_hit_angle( attacker, direction_vec, hit_point )
{
E = attacker.origin;
temp_vec = hit_point - E;
trace = BulletTrace( E, E + 1.5 * temp_vec, false, attacker, false );
if ( IsDefined ( trace[ "normal" ] ) && IsDefined( trace[ "entity" ] ) && (trace["entity"] == self) )
return trace[ "normal" ];
return undefined;
}
can_allocate_new_tank_crack()
{
if ( GetTime() < level.next_tank_hitfx_time )
return false;
return true;
}
allocate_new_tank_crack()
{
level.next_tank_hitfx_time = GetTime() + RandomFloatRange( level.tank_hitfx_throttle, level.tank_hitfx_throttle_max );
}
update_trolley()
{
waitframe(); // I don't know why a level's "main" is run before other systems have had a chance to set up, but we need to wait
trolley = GetEnt( "moving_trolley", "targetname" );
if ( !IsDefined( trolley ) )
{
PrintLn( "Trolley is missing." );
return;
}
// setup some flags to handle mover collisions against airdrop crates and killstreak drones
trolley.destroyAirdropOnCollision = true;
trolley.destroyDroneOnCollision = false;
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
Objective_Add( curObjID, "active", trolley.origin,"waypoint_trolley" );
Objective_OnEntityWithRotation( curObjID, trolley );
self.curObjID = curObjID;
trolley_lights = getstructarray( "trolley_light", "targetname" );
array_thread( trolley_lights, ::trolley_attach_lights, trolley );
trolley_mesh = getent( "moving_trolley_mesh", "targetname" );
if ( IsDefined( trolley_mesh ) )
trolley_mesh linkto( trolley );
trolley_mesh_extras = getentarray( "moving_trolley_extras", "targetname" );
if ( trolley_mesh_extras.size > 0 )
{
foreach ( extra in trolley_mesh_extras )
extra linkto( trolley );
}
trolley_wheels = GetEntArray( "moving_trolley_wheel", "targetname" );
if ( trolley_wheels.size <= 0 )
{
Println( "Trolley is missing wheels." );
return;
}
wheel_offsets = [];
wheel_rotations = [];
for ( i=0; i<trolley_wheels.size; i++ )
{
wheel_offsets[i] = trolley.origin - trolley_wheels[i].origin;
wheel_rotations[i] = trolley.angles - trolley_wheels[i].angles;
}
current_point = GetEnt( "trolley_path_start", "targetname" );
if ( !IsDefined( current_point ) )
{
PrintLn( "No trolley path to follow." );
return;
}
trolley.origin = current_point.origin;
trolley.angles = current_point.angles;
for ( i=0; i<trolley_wheels.size; i++ )
{
trolley_wheels[i].origin = current_point.origin - RotateVector( wheel_offsets[i], current_point.angles );
trolley_wheels[i].angles = current_point.angles + wheel_rotations[i];
}
trolley.enabled = true;
trolley thread monitor_trolley_dvar();
default_speed = 140.0;
stop_time = 0.0;
start_time = 0.0;
if ( IsDefined( current_point.script_accel ) )
start_time = current_point.script_accel;
current_point = GetEnt( current_point.target, "targetname" );
while ( IsDefined( current_point ) )
{
if ( !trolley.enabled )
{
wait 0.05;
continue;
}
stop_time = 0.0;
if ( IsDefined( current_point.script_decel ) )
stop_time = current_point.script_decel;
move_speed = default_speed;
move_speed /= Max( 1, GetDvarInt( "trolley_throttle", 1 ) );
if ( IsDefined( current_point.script_physics ) )
move_speed *= current_point.script_physics;
move_time = Distance( trolley.origin, current_point.origin ) / move_speed;
move_time = Max( move_time, stop_time + start_time );
trolley MoveTo( current_point.origin, move_time, start_time, stop_time );
trolley RotateTo( current_point.angles, move_time, start_time, stop_time );
wheel_speed = move_speed * 2.1;
if ( IsDefined( current_point.script_anglevehicle ) && (current_point.script_anglevehicle == 1) )
wheel_speed *= -1.0;
point_angle = current_point.angles[1];
for ( i=0; i<trolley_wheels.size; i++ )
{
wheel_spot = current_point.origin - RotateVector( wheel_offsets[i], current_point.angles );
trolley_wheels[i] MoveTo( wheel_spot, move_time, start_time, stop_time );
wheel_turn = (point_angle + wheel_rotations[i][1] - trolley_wheels[i].angles[1]) / move_time;//(point_angle - trolley_wheels[i].angles[1]) / move_time;
specific_wheel_speed = wheel_speed;
if ( IsDefined( trolley_wheels[i].script_index ) && (trolley_wheels[i].script_index == 1) )
specific_wheel_speed *= -1.0;
trolley_wheels[i] RotateVelocity( (0,wheel_turn,specific_wheel_speed), move_time, start_time, stop_time );
}
if ( start_time > 0.0 )
{
trolley_mesh PlaySoundOnMovingEnt( "trolley_bell" );
trolley_mesh PlaySoundOnMovingEnt( "trolley_start" );
wait ( start_time - 0.2 );
trolley_mesh PlayLoopSound( "trolley_motor" );
wait 0.2;
}
wait move_time - (start_time + stop_time);
if ( stop_time > 0.0 )
{
//trolley_mesh PlaySoundOnMovingEnt( "trolley_bell" );
trolley_mesh PlaySoundOnMovingEnt( "trolley_stop" );
wait 0.2;
trolley_mesh StopLoopSound( "trolley_motor" );
wait ( stop_time - 0.2 );
}
if ( IsDefined( current_point.script_node_pausetime ) )
wait current_point.script_node_pausetime;
for ( i=0; i<trolley_wheels.size; i++ )
trolley_wheels[i].angles = (trolley_wheels[i].angles[0], point_angle + wheel_rotations[i][1], trolley_wheels[i].angles[2]);
start_time = 0.0;
if ( IsDefined( current_point.script_accel ) )
start_time = current_point.script_accel;
if ( IsDefined( current_point.script_index ) )
trolley_mesh PlaySoundOnMovingEnt( "trolley_corner" );
current_point = GetEnt( current_point.target, "targetname" );
}
}
trolley_attach_lights( trolley )
{
light_mount = Spawn( "script_model", self.origin );
light_mount.angles = self.angles;
light_mount SetModel( "tag_origin" );
light_mount LinkTo( trolley );
while ( 1 )
{
PlayFXOnTag( level._effect[ "vfx_pot_lights_trolley" ], light_mount, "tag_origin" );
wait RandomFloatRange( 4.0, 6.0 );
StopFXOnTag( level._effect[ "vfx_pot_lights_trolley" ], light_mount, "tag_origin" );
waitframe();
}
}
monitor_trolley_dvar()
{
dvar_name = "trolley_toggle";
default_value = 0;
SetDevDvarIfUninitialized( dvar_name, default_value );
dvar_throttle = "trolley_throttle";
default_throttle = 1;
SetDevDvarIfUninitialized( dvar_throttle, default_throttle );
while( 1 )
{
value = GetDvarInt( dvar_name, default_value );
if ( value == default_value )
{
waitframe();
}
else
{
SetDevDvar( dvar_name, default_value );
self.enabled = !self.enabled;
}
}
}
update_destroyer()
{
ship = GetEnt( "roaming_destroyer", "targetname" );
if ( !IsDefined( ship ) )
{
PrintLn( "No roaming_destroyer ship." );
return;
}
current_point = GetEnt( ship.target, "targetname" );
if ( !IsDefined( current_point ) )
{
PrintLn( "No roaming_destroyer ship path to follow." );
return;
}
ship.origin = current_point.origin;
ship.angles = current_point.angles;
default_speed = 500.0;
accel_time = 0.0;
decel_time = 0.0;
current_point = GetEnt( current_point.target, "targetname" );
if ( IsDefined( current_point.script_decel ) )
decel_time = current_point.script_decel;
while ( IsDefined( current_point ) )
{
move_speed = default_speed;
if ( IsDefined( current_point.script_physics ) )
move_speed = current_point.script_physics;
accel_time = 0.0;
if ( IsDefined( current_point.script_accel ) )
accel_time = current_point.script_accel;
base_move_time = Distance( ship.origin, current_point.origin ) / move_speed;
move_time = Max( base_move_time, accel_time + decel_time );
ship MoveTo( current_point.origin, move_time, accel_time, decel_time );
ship RotateTo( current_point.angles, move_time, accel_time, decel_time );
wait move_time;
next_point = GetEnt( current_point.target, "targetname" );
if ( IsDefined( next_point ) )
{
if ( IsDefined( next_point.script_node_pausetime ) )
wait next_point.script_node_pausetime;
decel_time = 0.0;
if ( IsDefined( next_point.script_decel ) )
decel_time = next_point.script_decel;
}
current_point = next_point;
}
}
update_lighthouse_light()
{
lighthouse_light = GetEnt( "lighthouse_light", "targetname" );
if ( !IsDefined( lighthouse_light ) )
{
PrintLn( "No lighthouse light." );
return;
}
if ( !IsDefined( lighthouse_light.animation ) )
{
PrintLn( "No animation for the lighthouse light." );
return;
}
lighthouse_light ScriptModelPlayAnim( "mp_ca_rumble_lighthouse_rotate" );
}
update_artillery_fx()
{
fx_alias = level._effect[ "vfx_battleship_firing_timing" ];
fx_pos = (14701.5, -17741.3, 854.587);
fx_rot = AnglesToForward((354.236, 135.865, 13.443));
fx_up = AnglesToUp((354.236, 135.865, 13.443));
sound_ent = Spawn( "script_model", fx_pos );
sound_ent SetModel( "tag_origin" );
sound_alias = "emt_rumb_dist_arty_fire";
wait RandomFloatRange( 5.0, 20.0 );
while ( 1 )
{
next_shot = 30.0 + RandomFloat( 20.0 );
sound_ent.origin = fx_pos + (0,0,1000);
sound_ent MoveTo( sound_ent.origin + (-55000,-60000,16000), LookupSoundLength( sound_alias ) * 0.001, 0.2 );
sound_ent PlaySoundOnMovingEnt( sound_alias );
PlayFX( fx_alias, fx_pos, fx_rot, fx_up );
wait next_shot;
}
}
update_heli_fx()
{
fx_alias = level._effect[ "vfx_heli_timing" ];
fx_pos = (19354.8, -24794.2, 539.564);
fx_rot = AnglesToForward((0, 190, 0));
fx_up = AnglesToUp((0, 190, 0));
sound_ent = Spawn( "script_model", fx_pos );
sound_ent SetModel( "tag_origin" );
sound_alias = "scn_rumb_chopper_by";
wait RandomFloatRange( 5.0, 20.0 );
while ( 1 )
{
next_heli = 60.0 + RandomFloat( 30.0 );
sound_ent.origin = fx_pos + (0,0,700);
sound_ent MoveTo( sound_ent.origin + (-55000,-10000,3000), 40.0, 10.0 );
sound_ent PlaySoundOnMovingEnt( sound_alias );
PlayFX( fx_alias, fx_pos, fx_rot, fx_up );
wait next_heli;
}
}
update_flybyjet_fx()
{
fx_alias = level._effect[ "vfx_jet_flyby_timing" ];
fx_pos = (22547.9, -308.47, 1440.73);
fx_rot = AnglesToForward((0.855041, 223.034, -0.529352));
fx_up = AnglesToUp((0.855041, 223.034, -0.529352));
sound_ent = Spawn( "script_model", fx_pos );
sound_ent SetModel( "tag_origin" );
sound_alias = "scn_rumb_jets_by";
wait RandomFloatRange( 5.0, 20.0 );
while ( 1 )
{
next_plane = 90.0 + RandomFloat( 30.0 );
sound_ent.origin = fx_pos + (0,0,1000);
sound_ent MoveTo( sound_ent.origin + (-55000,-55000,0), 20.0, 0.6 );
sound_ent PlaySoundOnMovingEnt( sound_alias );
PlayFX( fx_alias, fx_pos, fx_rot, fx_up );
wait next_plane;
}
}
setup_fountain_fx()
{
thread update_fountain_fx( (200.517, 631.669, 12), 1.1 );
thread update_fountain_fx( (-47.933, 575.054, 26) );
thread update_fountain_fx( (-300.421, 633.173, 8), 1.1 );
wait 1.0;
while ( 1 )
{
level notify( "spawn_stair_fountain_fx" );
wait 8.0;
}
}
update_fountain_fx( fx_pos, fx_delay )
{
fx_alias = level._effect[ "vfx_water_fountain" ];
sound_alias = "emt_rumb_fount_spray";
while ( 1 )
{
level waittill( "spawn_stair_fountain_fx" );
if ( IsDefined( fx_delay ) && ( fx_delay > 0.0 ) )
wait fx_delay;
PlayFX( fx_alias, fx_pos, AnglesToForward((0,0,0)), AnglesToUp((0,0,0)) );
PlaySoundAtPos( fx_pos, sound_alias );
wait 1.1;
PlaySoundAtPos( fx_pos + (0.0,32.0,36.0), sound_alias );
wait 1.1;
PlaySoundAtPos( fx_pos + (0.0,80.0,68.0), sound_alias );
wait 1.1;
PlaySoundAtPos( fx_pos + (0.0,120.0,96.0), sound_alias );
}
}
// move one of the placed mlg cameras to have a better view of the Fed Blitz goal (bug 161265)
fixMLGCameraPosition()
{
mlgcameras = GetEntArray( "mp_mlg_camera", "classname");
if ( IsDefined( mlgcameras ) && mlgcameras.size )
{
foreach ( camEnt in mlgcameras)
{
if ( camEnt.origin == (-1064, -224, 272) )
{
camEnt.origin = (-632, -1712, 368);
camEnt.angles = (10, 113, -1.174);
break;
}
}
}
}