#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=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= 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 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 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; } } } }