#include maps\mp\_utility; #include common_scripts\utility; ///////////////////////////////// // CONSTANTS ///////////////////////////////// CONST_KILLSTREAK_WEIGHT = 85; CONST_KILLSTREAK_DURATION = 40; CONST_KILLSTREAK_KILL_TIME = 3; CONST_FOG_FADE_TIME = 5; // Exploders EXPLODER_SNOW_DOORS_WINDOWS_ID = 50; // Debug CONST_DEBUG_BEAST_PLAYER = "scr_beast_attack_player"; CONST_DEBUG_BEAST_OTHER = "scr_beast_attack_other"; main() { maps\mp\mp_zerosub_precache::main(); maps\createart\mp_zerosub_art::main(); maps\mp\mp_zerosub_fx::main(); // setup custom killstreak level.mapCustomCrateFunc = ::zeroSubCustomCrateFunc; level.mapCustomKillstreakFunc = ::zeroSubCustomKillstreakFunc; level.mapCustomBotKillstreakFunc = ::zeroSubCustomBotKillstreakFunc; maps\mp\_load::main(); // AmbientPlay( "ambient_mp_setup_template" ); maps\mp\_compass::setupMiniMap( "compass_map_mp_zerosub" ); setdvar( "r_lightGridEnableTweaks", 1 ); setdvar( "r_lightGridIntensity", 1.33 ); SetDvar( "r_tessellationCutoffFalloffBase", 600 ); SetDvar( "r_tessellationCutoffDistanceBase", 2000 ); SetDvar( "r_tessellationCutoffFalloff", 600 ); SetDvar( "r_tessellationCutoffDistance", 2000 ); game["attackers"] = "allies"; game["defenders"] = "axis"; game[ "allies_outfit" ] = "urban"; game[ "axis_outfit" ] = "woodland"; // JC-10-28-13 - The door triggers were not lined up with the door script model // causing the door to push the player through the ground. Nudge both triggers // into position to fix this. tu_fix_door_trigger_positions(); maps\mp\gametypes\_door::door_system_init( "door_switch" ); thread maps\mp\_dlcalienegg::setupEggForMap( "alienEasterEgg" ); thread tvs(); level thread watchPlayerSpawn(); // 0 = The Beast - w/Juggs // 1 - The Beast - FX only level.zerosub_killstreak = 0; level.zerosub_fog_on = false; // Allow beasts the go indoors level.beastAllowedIndoors = true; // Custom Death Effect to play when the killstreak kills a player level.custom_death_effect = ::playCustomDeathFX; level.custom_death_sound = ::playCustomDeathSound; // Setup the events for the map setupEvents( true ); // Update the bot max distance when the fog comes rolling in level thread update_bot_maxsightdistsqrd(); // Reset the fog ( in case someone calls in the killstreak just before a round transition ) level thread resetFrostVisionSet(); // Start another vision set when the nuke vision set rolls in, so it corrects how dark it gets level thread watchNukeVisionSet(); } tu_fix_door_trigger_positions() { names = [ "slide_door", "garage_door" ]; foreach ( name in names ) { door_ents = GetEntArray( name, "targetname" ); foreach ( ent in door_ents ) { if ( IsDefined( ent.classname ) && ent.classname == "trigger_multiple" ) { if ( IsDefined( ent.script_parameters ) && IsSubStr( ent.script_parameters, "prone_only=true" ) ) { continue; } if ( name == "slide_door" ) { ent.origin = ( ent.origin[ 0 ] + 4, ent.origin[ 1 ], ent.origin[ 2 ] ); } else if ( name == "garage_door" ) { ent.origin = ( ent.origin[ 0 ] - 8.5, ent.origin[ 1 ], ent.origin[ 2 ] ); } } } } } resetFrostVisionSet() { level endon ( "game_ended" ); level waittill( "prematch_over" ); stopFrostFog(); } watchNukeVisionSet() { level endon ( "game_ended" ); level waittill( "nuke_aftermath_post_started" ); foreach ( player in level.players ) { player VisionSetStage( 1, 1 ); } } update_bot_maxsightdistsqrd() { fog_maxSightDist = 700; fog_maxSightDistSqrd = fog_maxSightDist * fog_maxSightDist; while ( !IsDefined( level.participants ) ) waitframe(); while ( true ) { foreach ( participant in level.participants ) { if ( !IsPlayer( participant ) ) continue; if ( IsBot( participant ) ) { if ( !IsDefined( participant.default_maxsightdistsqrd ) ) participant.default_maxsightdistsqrd = participant.maxsightdistsqrd; if ( level.zerosub_fog_on ) { participant.maxsightdistsqrd = fog_maxSightDistSqrd; } else { participant.maxsightdistsqrd = participant.default_maxsightdistsqrd; } } } waitframe(); } } ///////////////////////////////// // EVENT SETUP ///////////////////////////////// precacheItems() { level.zerosub_fx[ "beast" ][ "snowcover_screen" ] = level._effect[ "vfx_yeti_snowcover_scr" ]; level.zerosub_fx[ "beast" ][ "blood_explosion" ] = level._effect[ "vfx_blood_explosion" ]; level.zerosub_fx[ "beast" ][ "shadow_screen" ] = level._effect[ "vfx_yeti_shadow_scr" ]; level.zerosub_fx[ "beast" ][ "snowcover" ] = level._effect[ "vfx_yeti_snowcover" ]; level.zerosub_fx[ "beast" ][ "eyeglow" ] = level._effect[ "vfx_yeti_glowing_eye" ]; level.zerosub_fx[ "breath" ][ "screen" ] = level._effect[ "vfx_yeti_breath_scr" ]; level.zerosub_fx[ "frost" ][ "screen" ] = level._effect[ "vfx_yeti_frost_scr" ]; level.zerosub_fx[ "snow" ][ "screen" ] = level._effect[ "vfx_yeti_snow_scr" ]; level.zerosub_fx[ "snow" ][ "player" ] = level._effect[ "vfx_playercentric_snowamb" ]; level.zerosub_fx[ "dust" ][ "player" ] = level._effect[ "vfx_playercentric_indoors" ]; level.zerosub_fx[ "button"][ "green" ] = level._effect[ "vfx_button_light_green" ]; level.zerosub_fx[ "button"][ "red" ] = level._effect[ "vfx_button_light_red" ]; } setupEvents( setup ) { precacheItems(); playEnvironmentAnims(); playButtonFX(); // Debug /# SetDvarIfUninitialized( CONST_DEBUG_BEAST_PLAYER, 0 ); SetDvarIfUninitialized( CONST_DEBUG_BEAST_OTHER, 0 ); AddDebugCommand( "bind o \"set " + CONST_DEBUG_BEAST_PLAYER + " 1\"\n" ); AddDebugCommand( "bind p \"set " + CONST_DEBUG_BEAST_OTHER + " 1\"\n" ); level thread debugDvarWatcher( CONST_DEBUG_BEAST_PLAYER ); level thread debugDvarWatcher( CONST_DEBUG_BEAST_OTHER ); #/ } playEnvironmentAnims() { fanObj01 = GetEnt( "zerosub_fan_01", "targetname" ); // Vent Fan 1 fanObj02 = GetEnt( "zerosub_fan_02", "targetname" ); // Vent Fan 2 radarObj = GetEnt( "zerosub_radar_dish", "targetname" ); // Radar Dish bushObj01 = GetEntArray( "zerosub_bush_01", "targetname" ); // Snowy Bush treeObj02 = GetEntArray( "zerosub_tree_02", "targetname" ); // Tall Snowy Pine Tree treeObj03 = GetEntArray( "zerosub_tree_03", "targetname" ); // Medium Snowy Pine Tree treeObj04 = GetEntArray( "zerosub_tree_04", "targetname" ); // Short Snowy Pine tree treeObj05 = GetEntArray( "zerosub_tree_05", "targetname" ); // Dead Pine Tree treeObj06 = GetEntArray( "zerosub_tree_06", "targetname" ); // Small Dead Tree // 2 Big Fans on the main building if ( IsDefined( fanObj01 ) ) fanObj01 ScriptModelPlayAnim( "mp_zerosub_fan_spin_1" ); if ( IsDefined( fanObj02 ) ) fanObj02 ScriptModelPlayAnim( "mp_zerosub_fan_spin_2" ); // Big Radar Dish on the main building if ( IsDefined( radarObj ) ) radarObj ScriptModelPlayAnim( "mp_zerosub_radar_spin" ); // Bushes in the environment if ( IsDefined( bushObj01 ) ) { foreach ( bush in bushObj01 ) { randomDelay = RandomFloatRange( 1.0, 3.0 ); bush thread playDelayAnim( "mp_zerosub_bush_tree", randomDelay ); } } // Trees in the environment if ( IsDefined( treeObj02 ) ) { foreach ( tree in treeObj02 ) { randomDelay = RandomFloatRange( 1.0, 2.0 ); tree thread playDelayAnim( "mp_zerosub_spruce_tree_2", randomDelay ); } } if ( IsDefined( treeObj03 ) ) { foreach ( tree in treeObj03 ) { randomDelay = RandomFloatRange( 2.0, 3.0 ); tree thread playDelayAnim( "mp_zerosub_spruce_tree_3", randomDelay ); } } if ( IsDefined( treeObj04 ) ) { foreach ( tree in treeObj04 ) { randomDelay = RandomFloatRange( 4.0, 5.0 ); tree thread playDelayAnim( "mp_zerosub_spruce_tree_4", randomDelay ); } } if ( IsDefined( treeObj05 ) ) { foreach ( tree in treeObj05 ) { randomDelay = RandomFloatRange( 5.0, 6.0 ); tree thread playDelayAnim( "mp_zerosub_dead_pine", randomDelay ); } } if ( IsDefined( treeObj06 ) ) { foreach ( tree in treeObj06 ) { randomDelay = RandomFloatRange( 6.0, 7.0 ); tree thread playDelayAnim( "mp_zerosub_dead_tree", randomDelay ); } } } playButtonFX() { greenButtons = getstructarray( "button_green", "targetname" ); redButtons = getstructarray( "button_red", "targetname" ); if ( IsDefined( greenButtons ) ) { foreach ( button in greenButtons ) { button.fxObj = SpawnFx( level.zerosub_fx[ "button"][ "green" ], button.origin ); button.fxObj thread delayTriggerFX(); button.fxObj thread cleanupFX(); } } if ( IsDefined( redButtons ) ) { foreach ( button in redButtons ) { button.fxObj = SpawnFx( level.zerosub_fx[ "button"][ "red" ], button.origin ); button.fxObj thread delayTriggerFX(); button.fxObj thread cleanupFX(); } } } delayTriggerFX() { level endon ( "game_ended" ); level waittill( "prematch_over" ); TriggerFX( self ); } cleanupFX() { level waittill ( "game_ended" ); self Delete(); } playDelayAnim( animAlias, timeDelay ) { // self == animation entity level endon ( "game_ended" ); wait( timeDelay ); self ScriptModelPlayAnim( animAlias ); } /# debugDvarWatcher( DVAR ) { level endon ( "game_ended" ); while ( true ) { value = GetDvarInt( DVAR, 0 ); if ( value > 0 ) { switch ( DVAR ) { case CONST_DEBUG_BEAST_PLAYER: foreach( player in level.players ) { // Use the killstreak player tryUseZeroSubKillstreak(); break; } break; case CONST_DEBUG_BEAST_OTHER: if ( level.players.size > 1 ) { foreach( player in level.players ) { // Skip the first player ( you ) if ( !IsDefined( level.debugfirstPlayer ) ) { level.debugfirstPlayer = true; continue; } // Have the second player ( enemy ) to use the killstreak player tryUseZeroSubKillstreak(); level.debugfirstPlayer = undefined; break; } } else IPrintLnBold( "Need at least 1 enemy player to use this debug command" ); break; } SetDvar( DVAR, 0 ); } wait ( 0.5 ); } } #/ ///////////////////////////////// // LEVEL KILLSTREAK ///////////////////////////////// zeroSubCustomCrateFunc() { 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", "zerosub_level_killstreak", CONST_KILLSTREAK_WEIGHT, maps\mp\killstreaks\_airdrop::killstreakCrateThink, maps\mp\killstreaks\_airdrop::get_friendly_crate_model(), maps\mp\killstreaks\_airdrop::get_enemy_crate_model(), &"MP_ZEROSUB_LEVEL_KILLSTREAK_ACTIVATE" ); level thread zerosub_killstreak_watch_for_crate(); } zeroSubCustomKillstreakFunc() { AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/Care Package/The Beast\" \"set scr_devgivecarepackage zerosub_level_killstreak; set scr_devgivecarepackagetype airdrop_assault\"\n"); AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/The Beast\" \"set scr_givekillstreak zerosub_level_killstreak\"\n"); level.killStreakFuncs[ "zerosub_level_killstreak" ] = ::tryUseZeroSubKillstreak; } zeroSubCustomBotKillstreakFunc() { AddDebugCommand("devgui_cmd \"MP/Bots(Killstreak)/Level Events:5/The Beast\" \"set scr_testclients_givekillstreak zerosub_level_killstreak\"\n"); maps\mp\bots\_bots_ks::bot_register_killstreak_func( "zerosub_level_killstreak", maps\mp\bots\_bots_ks::bot_killstreak_simple_use ); } tryUseZeroSubKillstreak( lifeId, streakName ) { // Announce to everyone that you used the killstreak level thread teamPlayerCardSplash( "used_zerosub_level_killstreak", self ); level.zerosub_killstreak_user = self; // Environment Effects playFrostFog(); // Play Environmental/Screen FX etc... when a player connects level thread watchPlayerConnect(); // Play Environmental FX when a host migration happens level thread watchHostMigration(); foreach ( player in level.players ) { // The beast is coming! player PlayLocalSound( "mp_zero_monster_spawn" ); if ( player.team != level.zerosub_killstreak_user.team ) player thread watchRagDoll(); } // Keep track of when this killstreak will end, so we know when to clear everything level thread watchKillstreakEnd(); if ( level.zerosub_killstreak ) { // OLD KILLSTREAK LOGIC; this killstreak now uses the mp_beast_men::tryUseAgentKillstreak below //---------------------------------------------------------------------------------------------- // Time to start the hunt level thread killRandomTargets(); game[ "player_holding_level_killstrek" ] = false; return true; } else { // Overwrite the agent weapon in the kill cam, so it will show the name of the killstreak instead level.killstreakWeildWeapons[ "beast_agent_mp" ] = "zerosub_level_killstreak"; return maps\mp\mp_beast_men::tryUseAgentKillstreak(); } } zerosub_killstreak_watch_for_crate() { while ( true ) { level waittill( "createAirDropCrate", dropCrate ); if ( IsDefined( dropCrate ) && IsDefined( dropCrate.crateType ) && dropCrate.crateType == "zerosub_level_killstreak" ) { maps\mp\killstreaks\_airdrop::changeCrateWeight( "airdrop_assault", "zerosub_level_killstreak", 0 ); captured = wait_for_capture( dropCrate ); if ( !captured ) { maps\mp\killstreaks\_airdrop::changeCrateWeight( "airdrop_assault", "zerosub_level_killstreak", CONST_KILLSTREAK_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; } watchRagDoll() { // self == victim self endon ( "disconnected" ); level endon ( "game_ended" ); level endon ( "frost_clear" ); while ( true ) { self waittill ( "start_instant_ragdoll", sMeansOfDeath, eInflictor ); waitframe(); // Cause the body to be thrown if ( isBeastMan( eInflictor ) ) PhysicsExplosionSphere( eInflictor.origin + ( 0, 0, 50 ), 100, 90, 5.0 ); } } watchKillstreakEnd() { level endon ( "game_ended" ); killstreakDuration = CONST_KILLSTREAK_DURATION; while ( killstreakDuration > 0 ) { killstreakDuration -= 1; maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 1 ); } // The following players can now be killed by the level killstreak again foreach ( player in level.players ) { if ( IsDefined( player ) ) { if ( IsDefined( player.killedByYeti ) ) player.killedByYeti = undefined; if ( IsDefined( player.isBeingHunted ) ) player.isBeingHunted = undefined; if ( IsDefined( player.beastKillCam ) ) player.beastKillCam = undefined; } } // Only stop this if the nuke vision set isn't already in place if ( !IsDefined( level.nukeVisionInProgress ) || !level.nukeVisionInProgress ) stopFrostFog(); level notify ( "frost_clear" ); } killRandomTargets() { level endon ( "game_ended" ); level endon ( "frost_clear" ); // Let the fog roll in first wait ( CONST_FOG_FADE_TIME + 3 ); while ( true ) { // Choose the target, and kill after a slight delay killTarget = getKillTarget(); killInterval = RandomIntRange( 2, 4 ); if ( IsDefined( killTarget ) ) killTarget thread delayKill( CONST_KILLSTREAK_KILL_TIME ); // Wait some more time before targeting the next player wait ( killInterval ); } } getKillTarget() { killTargets = []; randomIndex = 0; foreach ( player in level.players ) { // Only enemies that are alive, outside, are not already being hunted, can be killed by killstreaks, and not riding on a Heli Sniper should be added to this list if ( IsDefined( player ) && IsReallyAlive( player ) && player.team != level.zerosub_killstreak_user.team && player isOutside() && !player isBeingHunted() && player.avoidKillstreakOnSpawnTimer <= 0 && !player maps\mp\killstreaks\_killstreaks::isUsingHeliSniper() ) { killTargets[ killTargets.size ] = player; } } if ( killTargets.size > 0 ) { randomIndex = RandomInt( killTargets.size ); // This person is now a marked target killTargets[ randomIndex ].isBeingHunted = true; return killTargets[ randomIndex ]; } } delayKill( delayTime ) { // self == victim level endon ( "game_ended" ); // Random Offset, where the explosion is going to come from randomOffset = []; randomOffset[ randomOffset.size ] = ( 0, 300, 0 ); randomOffset[ randomOffset.size ] = ( 300, 0, 0 ); randomOffset[ randomOffset.size ] = ( 300, 300, 0 ); randomOffset[ randomOffset.size ] = ( 0, -300, 0 ); randomOffset[ randomOffset.size ] = ( -300, 0, 0 ); randomOffset[ randomOffset.size ] = ( -300, -300, 0 ); randomOffset[ randomOffset.size ] = ( -300, 300, 0 ); randomOffset[ randomOffset.size ] = ( 300, -300, 0 ); randomIndex = RandomInt( randomOffset.size ); // Sound FX for the growl etc... if ( !self isUsingRemote() ) self PlayLocalSound( "mp_zerosub_monster_approach" ); // Approaching FX Beast self thread playBeastFX(); fXDelay = delayTime - 0.5; killDelay = delayTime - fXDelay; wait ( fXDelay ); // Make sure we don't show the shadow unless they are really outside if ( IsDefined ( self ) && isReallyAlive( self ) && !self isUsingRemote() ) { if ( self isOutside() && !self maps\mp\killstreaks\_killstreaks::isUsingHeliSniper() ) { // What was that?!?!?! --- NOOOOOOOOOOOO!!!!!!!! shadow_effect_ent = SpawnFXForClient( level.zerosub_fx[ "beast" ][ "shadow_screen" ], self GetEye(), self ); TriggerFX( shadow_effect_ent ); shadow_effect_ent setfxkilldefondelete(); self thread killFXOnPlayerDeath( shadow_effect_ent ); snow_effect_ent = SpawnFXForClient( level.zerosub_fx[ "beast" ][ "snowcover_screen" ], self GetEye(), self ); TriggerFX( snow_effect_ent ); snow_effect_ent setfxkilldefondelete(); self thread killFXOnPlayerDeath( snow_effect_ent ); } } wait ( killDelay ); // Since we have a slight delay before killing them, let's do one last check to see if they are a valid target if ( IsDefined ( self ) && isReallyAlive( self ) ) { if ( self isOutside() && !self maps\mp\killstreaks\_killstreaks::isUsingHeliSniper() ) { // Sound FX for the roar/attack if ( !self isUsingRemote() ) self PlayLocalSound( "mp_zero_plr_monster_attack" ); self PlaySound( "mp_zero_npc_monster_attack" ); // Explode the player in a bloody mess playerTagPos = self GetTagOrigin( "j_mainroot" ); self.customDeath = true; self.killedByYeti = true; // Bloody Explosion - bllaarrggggghhhhh ( death sound ) PlayFX( level.zerosub_fx[ "beast" ][ "blood_explosion" ], playerTagPos ); self DoDamage( 10000, self.origin, level.zerosub_killstreak_user, self.beastKillCam, "MOD_CRUSH" ); // Randomize the position from where the explosion is coming from PhysicsExplosionSphere( self.origin + randomOffset[ randomIndex ], 500, 400, 2.0 ); } // Player was either killed, or escaped to inside the base before being attacked self.isBeingHunted = undefined; } } playBeastFX() { self endon ( "death" ); self endon ( "disconnect" ); level endon ( "game_ended" ); multiplier = 100; while( true ) { // Spawn the FX behind, and have it move through the player forwardVector = AnglesToForward( self.angles ); fxOrigin = self.origin - ( forwardVector * multiplier ); snow_cover_fx = SpawnFx( level.zerosub_fx[ "beast" ][ "snowcover" ], fxOrigin ); TriggerFX( snow_cover_fx ); snow_cover_fx thread watchFXLifetime( 2 ); multiplier -= 20; wait ( 0.5 ); } } playFrostFog() { level.zerosub_fog_on = true; foreach ( player in level.players ) { player playFrostFogPlayer(); } } playFrostFogPlayer( playImmediate ) { // self == player // Vision Set change, and exploders self VisionSetStage( 1, CONST_FOG_FADE_TIME ); self thread playDoorWindowSnowCoverFX( playImmediate ); // Screen effects self thread delayPlayLoopScreenFX( "frost", 4, CONST_FOG_FADE_TIME ); self thread delayPlayLoopScreenFX( "snow", 1, CONST_FOG_FADE_TIME ); self thread delayPlayLoopScreenFX( "breath", 3, CONST_FOG_FADE_TIME + 1 ); } stopFrostFog() { level.zerosub_fog_on = false; foreach ( player in level.players ) { player VisionSetStage( 0, CONST_FOG_FADE_TIME ); } } watchPlayerConnect() { level endon ( "game_ended" ); level endon ( "frost_clear" ); while ( true ) { level waittill ( "connected", player ); // Environment FX player playFrostFogPlayer( true ); player thread startKillstreakWatchers(); } } watchHostMigration() { level endon ( "game_ended" ); level endon ( "frost_clear" ); while ( true ) { level waittill ( "host_migration_end" ); foreach ( player in level.players ) { player VisionSetStage( 1, 0.1 ); } } } startKillstreakWatchers() { self endon ( "disconnect" ); level endon ( "game_ended" ); level endon ( "frost_clear" ); self waittill ( "spawned_player" ); // Watch the death ragdoll if ( self.team != level.zerosub_killstreak_user.team ) self thread watchRagDoll(); } playDoorWindowSnowCoverFX( playImmediate ) { self endon ( "disconnect" ); level endon ( "game_ended" ); level endon ( "frost_clear" ); if ( !IsDefined( playImmediate ) || !playImmediate ) { // Make sure to only play this after the fog fades in wait ( CONST_FOG_FADE_TIME ); } // Only show this to players indoors while ( true ) { if ( !self isOutside() ) exploder( EXPLODER_SNOW_DOORS_WINDOWS_ID, self ); wait ( 0.5 ); } } delayPlayLoopScreenFX( effectType, intervalTime, delayTime ) { // self == player self notify ( "playScreenFX_" + effectType ); self endon ( "playScreenFX_" + effectType ); self endon ( "disconnect" ); self endon ( "death" ); level endon ( "game_ended" ); level endon ( "frost_clear" ); // Replay the effect once they spawn back in self thread watchPlayerSpawnFX( effectType, intervalTime, 0.05 ); // We need to keep track of how long the effect will play, so we can delete it after it's done effectDuration = 0; switch ( effectType ) { case "frost": effectDuration = 6; break; case "snow": effectDuration = 1; break; case "breath": effectDuration = 1; break; } // The fog doesn't roll in right away, so we need to wait before playing this wait ( delayTime ); while ( true ) { // Play the screen effects under these conditions if ( ( !level.beastAllowedIndoors && self isOutside() ) || // Beasts are not allowed inside, and players are outside ( level.beastAllowedIndoors && self isOutside() && effectType == "snow" ) || // Beast are allowed inside, and players are outside, and the effect type is "snow" ( level.beastAllowedIndoors && effectType != "snow" ) ) // Beast are allowed inside, and effect type is "frost" and "breath" { effect_ent = SpawnFXForClient( level.zerosub_fx[ effectType ][ "screen" ], self GetEye(), self ); if ( IsDefined ( effect_ent ) ) { TriggerFX( effect_ent ); effect_ent setfxkilldefondelete(); // Kill this previous effect on a delayed time, so it doesn't cut out right away effect_ent thread watchFXLifetime( effectDuration + 1 ); } } wait( intervalTime ); } } isOutside() { // self == player outside = true; if ( !IsDefined ( level.zerosub_inside_trigger ) ) level.zerosub_inside_trigger = GetEnt ( "mp_zerosub_indoor_triggers", "targetname" ); if ( IsDefined ( level.zerosub_inside_trigger ) ) { if ( self IsTouching( level.zerosub_inside_trigger ) ) outside = false; } return outside; } isBeingHunted() { beingHunted = false; if ( IsDefined( self.isBeingHunted ) && self.isBeingHunted ) beingHunted = true; return beingHunted; } watchFXLifetime( lifeTime ) { level endon ( "game_ended" ); wait ( lifeTime ); self delete(); } killFXOnPlayerDeath( effect ) { level endon ( "game_ended" ); self waittill_any ( "killed_player", "disconnect" ); if ( IsDefined ( effect ) ) { if ( IsArray( effect ) ) { foreach ( fx in effect ) { fx delete(); } } else effect delete(); } } watchPlayerSpawnFX( effectType, intervalTime, delayTime ) { // self == player self endon ( "disconnect" ); level endon ( "game_ended" ); level endon ( "frost_clear" ); self waittill ( "spawned_player" ); self thread delayPlayLoopScreenFX( effectType, intervalTime, delayTime ); } watchPlayerSpawn() { level endon ( "game_ended" ); while ( true ) { level waittill ( "player_spawned", player ); if ( Isdefined( player.customDeath ) ) player.customDeath = undefined; // Relink the killcam for the killstreak ( if it's still going ) if ( IsDefined( player.beastKillCam ) ) { player.beastKillCam.origin = player.origin + ( 100, 100, 100 ); player.beastKillCam LinkTo ( player ); } // Let it Snow player thread playEnvironmentFX(); } } playEnvironmentFX() { // self == player self endon ( "disconnect" ); self endon ( "death" ); level endon ( "game_ended" ); while ( true ) { forwardVector = AnglesToForward( self.angles ); // Play Snow if we are outside, and Dust particles while inside // Make sure we play it ahead of the player, so it keeps up with them as they are moving if ( self isOutside() ) { snowFX = SpawnFXForClient( level.zerosub_fx[ "snow" ][ "player" ], self.origin + ( forwardVector * 100 ), self ); TriggerFX( snowFX ); snowFX thread watchFXLifetime( 2 ); } else { dustFX = SpawnFXForClient( level.zerosub_fx[ "dust" ][ "player" ], self.origin + ( forwardVector * 100 ), self ); TriggerFX( dustFX ); dustFX thread watchFXLifetime( 2 ); } wait ( 1 ); } } playCustomDeathFX( victim, sMeansOfDeath, eInflictor ) { if ( isBeastMan( eInflictor ) ) { // Make the victim do an instant ragdoll victim.customDeath = true; // Bloody Explosion - bllaarrggggghhhhh ( death sound ) playerTagPos = victim GetTagOrigin( "j_mainroot" ); PlayFX( level.zerosub_fx[ "beast" ][ "blood_explosion" ], playerTagPos ); } } playCustomDeathSound( victim, sMeansOfDeath, eInflictor ) { if ( sMeansOfDeath == "MOD_MELEE" ) { if ( isBeastMan( eInflictor ) ) { type = "male"; if ( victim hasFemaleCustomizationModel() ) type = "female"; victim PlaySound( "knife_death_"+ type ); } } else victim playDeathSound(); } isBeastMan( eInflictor ) { beastMan = false; if ( IsDefined( eInflictor ) && IsAgent( eInflictor ) ) { if ( eInflictor.agent_type == "beastmen" ) beastMan = true; } return beastMan; } // Plays the hockey video on the TVs tvs() { foreach ( name in ["tv_hockey", "tv_hockey_scale1pt5"] ) { thread tvs_set(name); } } tvs_set(targetname) { /# SetDevDvarIfUninitialized( "tv_debug", 0 ); #/ if (!IsDefined(level._effect[targetname])) { error("level._effect["+targetname+"] not defined."); } num_tv_fx = 3; for (i=1; i<=num_tv_fx; i++) { if (!IsDefined(level._effect[targetname][i])) { error("level._effect["+targetname+"]["+i+"] not defined."); } if (!IsDefined(level.tv_info.effectLength[targetname][i])) { error("level.tv_info.effectLength["+targetname+"]["+i+"] not defined."); level.tv_info.effectLength[targetname][i] = 1; } } if (!IsDefined( level.tv_info.destroymodel[targetname] ) ) { error("level.tv_info.destroymodel["+targetname+"] not defined."); destroymodel = undefined; } else { destroymodel = level.tv_info.destroymodel[targetname]; } tvs = GetEntArray(targetname, "targetname"); if ( tvs.size == 0 ) { PrintLn( "^c * ERROR * No TVs found with targetname "+targetname+"."); } foreach ( tv in tvs ) { tv setCanDamage(true); tv.isHealthy = true; tv.destroymodel = destroymodel; tv.fxtag = level.tv_info.tag[targetname]; // Since the TVs are off for current gen, we are only going to play audio for next gen if ( IsDefined( tv.script_noteworthy ) ) { tv thread playTVAudio( tv.script_noteworthy ); } tv thread tv_death(); } level.tv_fx_num = num_tv_fx; while (true) { prev_fx = level.tv_fx_num; level.tv_fx_num = RandomIntRange( 1, num_tv_fx ); if (level.tv_fx_num >= prev_fx) level.tv_fx_num += 1; fx = level._effect[targetname][level.tv_fx_num]; foreach ( tv in tvs ) { if ( tv.isHealthy ) { PlayFXOnTag( fx, tv, tv.fxtag ); tv.currentFX = fx; } } wait level.tv_info.effectLength[targetname][level.tv_fx_num]; /#tvs_remaining = false; foreach ( tv in tvs ) { if ( tv.isHealthy == true ) tvs_remaining = true; } if ( GetDvarInt( "tv_debug" ) ) { if (!tvs_remaining) { wait 1; thread tvs(); return; } }#/ } } playTVAudio( targetname ) { delay = 15; wait ( delay ); if ( targetname == "tv_hockey_small_room" ) { self PlayLoopSound( "mp_zerosub_tv_small_room" ); } else { self PlayLoopSound( "mp_zerosub_tv_big_room" ); } } tv_death() { self endon("death"); self.health = 10000; self waittill("damage"); KillFXOnTag( self.currentFX, self, self.fxtag ); self StopLoopSound(); self SetModel( self.destroymodel ); PlayFXOnTag( level._effect["tv_explode"], self, self.fxtag ); playSoundAtPos(self.origin, "tv_shot_burst"); self.isHealthy = false; self setCanDamage(false); }