#include common_scripts\utility; #include maps\mp\_utility; #include maps\mp\gametypes\_hud_util; #include maps\mp\agents\_agent_utility; #using_animtree( "multiplayer" ); ///////////////////////////////// // CONSTANTS ///////////////////////////////// // Perk Sets CONST_RA_SET = 0; CONST_AMON_SET = 1; CONST_HORUS_SET = 2; CONST_ANUBIS_SET = 3; // Event IDs CONST_OBELISK_ID = "obelisk_event"; CONST_SCARABS_ID = "scarabs_event"; CONST_TREASURE_ID = "treasure_event"; CONST_SNAKES_ID = "snakes_event"; CONST_SHRINE_ID = "shrine_event"; // Obelisk Victim Types CONST_CHARACTERS = "CHARACTERS"; CONST_KILLSTREAKS = "KILLSTREAKS"; CONST_EQUIPMENT = "EQUIPMENT"; CONST_PACKAGES = "CARE PACKAGES"; CONST_INTEL = "INTEL"; // Events CONST_SNAKE_ANIM_LENGTH = 66.7; CONST_SCARAB_APPEAR_TIME = 2; CONST_SCARAB_KILL_RADIUS_SQUARED = 4000; CONST_SCARAB_DMG_TICK = 20; CONST_SCARAB_DMG_INTERVAL = 0.5; CONST_SCARAB_FALLOFF_TIME = 10; CONST_OBELISK_IMPACT_TIME = 3.1; CONST_OBELISK_FALL_ANIM_TIME = 7; CONST_FLAME_DMG_TICK = 20; CONST_FLAME_DMG_INTERVAL = 0.5; CONST_FLAME_LIFE_TIME = 10; CONST_DOOR_MOVE_TIME = 15; CONST_CHEST_USE_TIME = 4; CONST_CHEST_JACKPOT_LENGTH = 30; CONST_MAX_SCARAB_DEATH_BONES = 32; // Debug DVARs CONST_DEBUG_OBELISK = "scr_obelisk_fall"; CONST_DEBUG_OBELISK_RESET = "scr_obelisk_reset"; CONST_DEBUG_PLAYER_SCARAB = "scr_scarab_on_player"; CONST_DEBUG_PLAYER_SHRINE = "scr_shrine_on_player"; CONST_DEBUG_SCARAB_POT_RESET = "scr_scarab_pot_reset"; CONST_DEBUG_TREASURE_OPEN = "scr_treasure_open"; CONST_DEBUG_TREASURE_RESET = "scr_treasure_reset"; // Animation CONST_SCARAB_KILL_WEAPON = "iw6_scarabkill_mp"; // Exploders CONST_EXPLODER_SCAFFOLD_DESTORY_1 = 10; CONST_EXPLODER_SCAFFOLD_DESTORY_2 = 11; CONST_EXPLODER_SCAFFOLD_DESTORY_3 = 12; CONST_EXPLODER_SCAFFOLD_DESTORY_4 = 13; CONST_EXPLODER_SCAFFOLD_DESTORY_5 = 14; CONST_EXPLODER_SCAFFOLD_DESTORY_6 = 15; CONST_EXPLODER_SCAFFOLD_DESTORY_7 = 16; CONST_EXPLODER_OBELISK_FALL_1 = 20; CONST_EXPLODER_OBELISK_FALL_2 = 21; CONST_EXPLODER_OBELISK_FALL_3 = 22; CONST_EXPLODER_SHRINE = 55; CONST_EXPLODER_CHEST_SMOKE = 56; CONST_EXPLODER_CHEST_FLAMES = 57; CONST_EXPLODER_CHEST_DOOR = 59; main() { maps\mp\mp_dig_precache::main(); maps\createart\mp_dig_art::main(); maps\mp\mp_dig_fx::main(); // setup custom killstreak level.mapCustomCrateFunc = ::digCustomCrateFunc; level.mapCustomKillstreakFunc = ::digCustomKillstreakFunc; level.mapCustomBotKillstreakFunc = ::digCustomBotKillstreakFunc; level.allow_level_killstreak = true; level.custom_death_sound = ::playCustomDeathSound; level.nukeDeathVisionFunc = ::nukeDeathVision; maps\mp\_load::main(); // AmbientPlay( "ambient_mp_setup_template" ); maps\mp\_compass::setupMiniMap( "compass_map_mp_dig" ); setdvar_cg_ng( "r_specularColorScale", 2.5, 5 ); SetDvar( "r_lightGridEnableTweaks", 1 ); SetDvar( "r_lightGridIntensity" , 1.33 ); game[ "attackers" ] = "allies"; game[ "defenders" ] = "axis"; game[ "allies_outfit" ] = "urban"; game[ "axis_outfit" ] = "woodland"; // Setup any exceptions we want to leave out of the level for now // CONST_OBELISK_ID = Falling Obelisk in the center of the map // CONST_SCARABS_ID = Scarab Pots // CONST_TREASURE_ID = Hidden Treasure Room // CONST_SNAKES_ID = Random Snakes // CONST_SHRINE_ID = Level killstreak digEventExceptions = []; showEvents = true; // Hiding scarab pots for Safeguard, to avoid potential issues they may have for that mode if ( level.gameType == "horde" || level.gametype == "infect" ) showEvents = false; // Still showing most events, but hiding the treasure room for these specific game modes, and MLG matches if ( level.gameType == "gun" || level.gametype == "sotf" || level.gametype == "sotf_ffa" || isAnyMLGMatch() ) digEventExceptions[ digEventExceptions.size ] = CONST_TREASURE_ID; // Setup Traps/Events for Dig setupEvents( showEvents, digEventExceptions ); setupShrinePerks(); thread maps\mp\_dlcalienegg::setupEggForMap( "alienEasterEgg" ); thread nuke_custom_visionset(); } ///////////////////////////////// // EVENT SETUP ///////////////////////////////// precacheItems() { level.breakables_fx[ "scarabpot" ][ "break" ] = LoadFX ( "vfx/moments/mp_dig/vfx_kstr_urnbreak_01" ); level.breakables_fx[ "scarabpot" ][ "break_top" ] = LoadFX ( "vfx/moments/mp_dig/vfx_pot_smk_wispy_damage" ); level.breakables_fx[ "scarabpot" ][ "break_scarabs" ] = LoadFX ( "vfx/moments/mp_dig/vfx_scarab_pot_explosion_r" ); level.breakables_fx[ "scarab" ][ "player" ] = LoadFX ( "vfx/moments/mp_dig/vfx_scarab_on_player_r" ); level.breakables_fx[ "scarab" ][ "ground" ] = LoadFX ( "vfx/moments/mp_dig/vfx_scarab_walk_up_r" ); level.breakables_fx[ "scarab" ][ "screen" ] = LoadFX ( "vfx/moments/mp_dig/vfx_scarab_screen_r" ); level.breakables_fx[ "scarab" ][ "flyers" ] = LoadFX ( "vfx/moments/mp_dig/vfx_scarab_groupwflyers" ); level.dig_fx[ "torch" ][ "fire" ] = LoadFX ( "vfx/moments/mp_dig/vfx_torch_fire_03" ); level.dig_fx[ "torch" ][ "sand" ] = LoadFX ( "vfx/moments/mp_dig/vfx_falling_sand_torch" ); //level.dig_fx[ "shrine" ][ "activate" ] = LoadFX ( "vfx/moments/mp_dig/vfx_torch_fire_03" ); level.dig_fx[ "shrine" ][ "player" ] = LoadFX ( "vfx/moments/mp_dig/vfx_kstr_loadedguy" ); level.dig_fx[ "shrine" ][ "screen" ] = LoadFX ( "vfx/moments/mp_dig/vfx_kstr_loadedguy_scr" ); level.dig_fx[ "scarab" ][ "deathAnim" ] = LoadFX ( "vfx/moments/mp_dig/vfx_scarab_death_r" ); level.dig_fx[ "flametrap" ][ "player" ] = LoadFX ( "vfx/moments/mp_dig/vfx_kstr_manflame" ); level.dig_fx[ "flametrap" ][ "screen" ] = LoadFX ( "vfx/moments/mp_dig/vfx_kstr_fireroom_scr" ); level.dig_fx[ "flametrap" ][ "room" ] = LoadFX ( "vfx/moments/mp_dig/vfx_kstr_flameroom_flame" ); } setupEvents( setup, setupExceptions ) { precacheItems(); // Debug /# SetDvarIfUninitialized( CONST_DEBUG_OBELISK , 0 ); SetDvarIfUninitialized( CONST_DEBUG_OBELISK_RESET , 0 ); SetDvarIfUninitialized( CONST_DEBUG_PLAYER_SCARAB , 0 ); SetDvarIfUninitialized( CONST_DEBUG_PLAYER_SHRINE , 0 ); SetDvarIfUninitialized( CONST_DEBUG_SCARAB_POT_RESET, 0 ); SetDvarIfUninitialized( CONST_DEBUG_TREASURE_OPEN , 0 ); SetDvarIfUninitialized( CONST_DEBUG_TREASURE_RESET , 0 ); AddDebugCommand( "bind o \"set " + CONST_DEBUG_OBELISK + " 1\"\n" ); AddDebugCommand( "bind p \"set " + CONST_DEBUG_OBELISK_RESET + " 1\"\n" ); AddDebugCommand( "bind k \"set " + CONST_DEBUG_PLAYER_SCARAB + " 1\"\n" ); AddDebugCommand( "bind l \"set " + CONST_DEBUG_PLAYER_SHRINE + " 1\"\n" ); AddDebugCommand( "bind semicolon \"set " + CONST_DEBUG_SCARAB_POT_RESET + " 1\"\n" ); AddDebugCommand( "bind u \"set " + CONST_DEBUG_TREASURE_OPEN + " 1\"\n" ); AddDebugCommand( "bind i \"set " + CONST_DEBUG_TREASURE_RESET + " 1\"\n" ); level thread debugDvarWatcher( CONST_DEBUG_OBELISK ); level thread debugDvarWatcher( CONST_DEBUG_OBELISK_RESET ); level thread debugDvarWatcher( CONST_DEBUG_PLAYER_SCARAB ); level thread debugDvarWatcher( CONST_DEBUG_PLAYER_SHRINE ); level thread debugDvarWatcher( CONST_DEBUG_SCARAB_POT_RESET ); level thread debugDvarWatcher( CONST_DEBUG_TREASURE_OPEN ); level thread debugDvarWatcher( CONST_DEBUG_TREASURE_RESET ); #/ if ( setup ) { // Should we show these events? obelisk = true; scarabs = true; treasure = true; snakes = true; shrine = true; // See if we need to disable a specific one if ( IsDefined( setupExceptions ) && setupExceptions.size > 0 ) { foreach ( exception in setupExceptions ) { switch( exception ) { case CONST_OBELISK_ID: obelisk = false; break; case CONST_SCARABS_ID: scarabs = false; break; case CONST_TREASURE_ID: treasure = false; break; case CONST_SNAKES_ID: snakes = false; break; case CONST_SHRINE_ID: shrine = false; break; } } } //////////////////////////////// // MAIN EVENTS //////////////////////////////// // Giant falling Obelisk if ( obelisk ) level thread setupObelisk(); // Scarab Pots if ( scarabs ) { level.digScarabPots = []; level.digScarabPots = getstructarray( "scarab_pot", "targetname" ); foreach ( pot in level.digScarabPots ) { level thread setupScarabPot( pot.origin ); } } // Hidden Treasure Room if ( treasure ) { level thread setupRadio(); setupTreasureRoom(); } //////////////////////////////// // MISC //////////////////////////////// // Snakes if ( snakes ) { snakeLocation = ( -362, 1982, 733 ); level thread setupSnakes( snakeLocation ); } //////////////////////////////// // LEVEL KILLSTREAK //////////////////////////////// // Pharaoh's Blessing if ( !shrine ) level.allow_level_killstreak = false; } else { // Show the obelisk, but you can't knock it down level thread setupObelisk( true ); } } /# debugDvarWatcher( DVAR ) { level endon ( "game_ended" ); while ( true ) { value = GetDvarInt( DVAR, 0 ); if ( value > 0 ) { switch ( DVAR ) { case CONST_DEBUG_OBELISK: level notify( "obelisk_activated" ); break; case CONST_DEBUG_OBELISK_RESET: level notify( "obelisk_reset" ); break; case CONST_DEBUG_PLAYER_SCARAB: firstPlayer = undefined; foreach ( player in level.participants ) { if ( IsDefined( player ) && isReallyAlive ( player ) ) { if ( !IsDefined ( firstPlayer ) ) { firstPlayer = true; continue; } watchScarabKill( player, true ); } } break; case CONST_DEBUG_PLAYER_SHRINE: foreach ( player in level.participants ) { if ( IsDefined( player ) && isReallyAlive ( player ) && !IsDefined( player.shrineEffect ) ) { player.shrineEffect = true; player givePerkBonus(); } else { player.shrineEffect = undefined; player resetPerkBonus(); } } break; case CONST_DEBUG_SCARAB_POT_RESET: level notify( "scarab_pot_reset" ); break; case CONST_DEBUG_TREASURE_OPEN: foreach ( player in level.players ) { if ( IsPlayer ( player ) && isReallyAlive ( player ) ) { owner = player; foreach ( torch in level.key_torches ) { if ( !torch.used ) torch activateTorch( owner ); wait ( 0.5 ); } break; } } break; case CONST_DEBUG_TREASURE_RESET: level notify( "treasure_room_reset" ); break; } SetDvar( DVAR, 0 ); } wait ( 0.5 ); } } #/ getDomFlagB() { domFlags = GetEntArray( "flag_primary", "targetname" ); bFlag = undefined; foreach ( f in domFlags ) { if ( f.script_label == "_b" ) { bFlag = f; break; } } return bFlag; } updateBFlagPos( resetFlag ) { level endon( "game_ended" ); bFlag = getDomFlagB(); if ( !IsDefined( level.dig_old_bflagPos ) ) level.dig_old_bflagPos = bflag.origin; newFlag = getStruct( "flag_b_after", "targetname" ); upangles = VectorToAngles( ( 0, 0, 1 ) ); newFlagPos = newFlag.origin; if ( IsDefined( resetFlag ) && resetFlag ) { // Set it back to the original newFlagPos = level.dig_old_bflagPos; traceStart = newFlagPos + ( 0, 0, 32 ); traceEnd = newFlagPos + ( 0, 0, -32 ); trace = BulletTrace( traceStart, traceEnd, false, undefined ); upangles = VectorToAngles( trace[ "normal" ] ); } // Move the trigger and effects to the new position bFlag.origin = newFlagPos; bFlag.useObj.curOrigin = newFlagPos; bFlag.useObj.visuals[ 0 ].origin = newFlagPos; bFlag.useObj.baseEffectPos = newFlagPos; bflag.useObj.baseeffectforward = AnglesToForward( upangles ); if ( IsDefined( bFlag.useObj.neutralFlagFx ) ) { bFlag.useObj maps\mp\gametypes\dom::playFlagNeutralFX(); } foreach ( player in level.players ) { if ( IsDefined( player._domFlagEffect ) && IsDefined( player._domFlagEffect[ "_b" ] ) ) bFlag.useObj maps\mp\gametypes\dom::showCapturedBaseEffectToPlayer( bFlag.useObj.ownerTeam, player); } } updateBFlagObjIcon() { bFlag = getDomFlagB(); // Force the objective icons to update tag_origin = spawn_tag_origin(); tag_origin show(); tag_origin.origin = bFlag.origin + ( 0, 0, 100 ); tag_origin LinkTo( bFlag ); bFlag.useObj.objIconEnt = tag_origin; bFlag.useObj maps\mp\gametypes\_gameobjects::updateWorldIcons(); } ///////////////////////////////// // OBELISK ///////////////////////////////// setupObelisk( stopTrigger ) { level endon ( "game_ended" ); // Models level.obeliskBefore = GetEnt( "obelisk_before", "targetname" ); level.obeliskAnimated = GetEnt( "obelisk_anim", "targetname" ); level.obeliskAnimated2 = GetEnt( "obelisk_anim_2", "targetname" ); level.obeliskAfter = GetEnt( "obelisk_after", "targetname" ); //TODO: REMOVE WHEN NEW ANIMATION COMES IN level.obeliskFloor = GetEnt( "obelisk_floor", "targetname" ); // Clips level.obeliskBeforeClip = GetEnt( "obelisk_before_clip", "targetname" ); level.obeliskAfterClip = GetEnt( "obelisk_after_clip", "targetname" ); // Blocker that contains the path nodes on top of the fallen obelisk level.obeliskPathBlocker = GetEnt( "obelisk_path_blocker", "targetname" ); // Invisible holder that will catch the nodes when they appear, so they don't fall through to the ground level.obeliskPathHolder = GetEnt( "obelisk_path_holder", "targetname" ); // Obelisk Damage Trigger level.obeliskDamage = GetEnt( "obelisk_damage_trigger", "targetname" ); // Kill Trigger - Ground level.obeliskKillTrigger_Ground = GetEnt( "obelisk_kill_trigger", "targetname" ); // Kill Trigger - Air level.obeliskKillTrigger_Air = GetEnt( "obelisk_kill_trigger_2", "targetname" ); // Hide all of the unused Models/Clips etc... for now if ( IsDefined ( level.obeliskAnimated ) ) hideEnt( level.obeliskAnimated ); if ( IsDefined ( level.obeliskAnimated2 ) ) hideEnt( level.obeliskAnimated2 ); if ( IsDefined ( level.obeliskFloor ) ) hideEnt( level.obeliskFloor ); if ( IsDefined ( level.obeliskAfter ) ) hideEnt( level.obeliskAfter ); if ( IsDefined ( level.obeliskAfterClip ) ) { if ( !IsDefined( level.obeliskAfterClip.killCamEnt ) ) { level.obeliskAfterClip.killCamEnt = Spawn( "script_model", level.obeliskAfterClip.origin + ( 400, -800, 400 ) ); level.obeliskAfterClip.killCamEnt SetModel( "tag_origin" ); } hideEnt( level.obeliskAfterClip ); } if ( IsDefined ( level.obeliskPathHolder ) ) hideEnt( level.obeliskPathHolder ); if ( IsDefined ( level.obeliskPathBlocker ) ) { showEnt( level.obeliskPathBlocker ); level.obeliskPathBlocker DisconnectPaths(); level thread delayHide( level.obeliskPathBlocker, 0.05 ); } // Make sure the damage for the kill zone is initially set to 0 if ( IsDefined ( level.obeliskKillTrigger_Ground ) ) { level.obeliskKillTrigger_Ground.dmg = 0; level.obeliskKillTrigger_Ground thread killAll( "obelisk_impact", 5000, "MOD_CRUSH" ); level.obeliskKillTrigger_Ground trigger_off(); } if ( IsDefined ( level.obeliskKillTrigger_Air ) ) { level.obeliskKillTrigger_Air.dmg = 0; level.obeliskKillTrigger_Air trigger_off(); } // Setup the first part of the obelisk if ( IsDefined ( level.obeliskBefore ) ) showEnt( level.obeliskBefore ); if ( IsDefined ( level.obeliskBeforeClip ) ) showEnt( level.obeliskBeforeClip ); // Everything is setup level.obeliskfallen = false; if ( !IsDefined( stopTrigger ) || !stopTrigger ) { level childthread watchObeliskActivation(); /# level childthread resetObelisk(); #/ if ( IsDefined( level.obeliskDamage ) ) { level.obeliskDamage waittill( "trigger", player ); level.obeliskOwner = player; level notify( "obelisk_activated" ); } } } watchObeliskActivation() { level waittill ( "obelisk_activated" ); hideEnt( level.obeliskBefore ); hideEnt( level.obeliskBeforeClip ); // Timber!!!! level thread delayExploder( 0.24, CONST_EXPLODER_SCAFFOLD_DESTORY_1 ); level thread delayExploder( 0.9, CONST_EXPLODER_SCAFFOLD_DESTORY_2 ); level thread delayExploder( 1.5, CONST_EXPLODER_SCAFFOLD_DESTORY_3 ); level thread delayExploder( 1.7, CONST_EXPLODER_SCAFFOLD_DESTORY_4 ); level thread delayExploder( 1.8, CONST_EXPLODER_SCAFFOLD_DESTORY_5 ); level thread delayExploder( 2.1, CONST_EXPLODER_SCAFFOLD_DESTORY_6 ); level thread delayExploder( 2.25, CONST_EXPLODER_SCAFFOLD_DESTORY_7 ); level thread delayExploder( 2.9, CONST_EXPLODER_OBELISK_FALL_1 ); level thread delayExploder( 2.95, CONST_EXPLODER_OBELISK_FALL_2 ); level thread delayExploder( 3, CONST_EXPLODER_OBELISK_FALL_3 ); showEnt( level.obeliskAnimated ); showEnt( level.obeliskAnimated2 ); // Animation Time level.obeliskAnimated ScriptModelPlayAnimDeltaMotion( "mp_dig_obelisk_dest_anim_01" ); level.obeliskAnimated2 ScriptModelPlayAnimDeltaMotion( "mp_dig_obelisk_dest_anim_02" ); // Turn on the trigger to make sure things within the air are killed as it is falling down level.obeliskKillTrigger_Air thread delayTrigger( 1, "on" ); level.obeliskKillTrigger_Air thread delayTrigger( 2, "off" ); // Handles all the extra stuff that happens when the obelisk hits the ground level thread obeliskImpact(); // Sound objects for the obelisk level thread obeliskSounds(); wait ( CONST_OBELISK_FALL_ANIM_TIME ); // Make sure the floor version is in place hideEnt( level.obeliskAnimated ); hideEnt( level.obeliskAnimated2 ); showEnt( level.obeliskAfter ); level.obeliskfallen = true; } resetObelisk() { while ( true ) { level waittill ( "obelisk_reset" ); if ( level.obeliskfallen ) { level.obeliskAfterClip ConnectPaths(); if ( level.gameType == "dom" || level.gameType == "siege" ) { updateBFlagPos( true ); updateBFlagObjIcon(); } level thread setupObelisk(); } } } obeliskSounds() { level endon ( "game_ended" ); offsetBase = ( 0, 0, -300 ); offsetMid = ( 0, 0, 0 ); offsetHigh = ( 0, 0, 400 ); level.obeliskSoundBaseObj = Spawn( "script_model", level.obeliskAnimated.origin + offsetBase ); level.obeliskSoundMidObj = Spawn( "script_model", level.obeliskAnimated.origin + offsetMid ); level.obeliskSoundTopObj = Spawn( "script_model", level.obeliskAnimated.origin + offsetHigh ); level.obeliskSoundBaseObj SetModel( "tag_origin" ); level.obeliskSoundMidObj SetModel( "tag_origin" ); level.obeliskSoundTopObj SetModel( "tag_origin" ); level.obeliskSoundMidObj Linkto( level.obeliskAnimated, "tag_mp_dig_obelisk_dyn_08" ); level.obeliskSoundTopObj Linkto( level.obeliskAnimated, "tag_mp_dig_obelisk_dyn_04" ); // /# // level.obeliskSoundBaseObj thread drawLinkedSphere( 100, 30, true ); // level.obeliskSoundMidObj thread drawLinkedSphere( 100, 30, true ); // level.obeliskSoundTopObj thread drawLinkedSphere( 100, 30, true ); // #/ waitframe(); level.obeliskSoundBaseObj PlaySound( "mp_dig_obelisk_fall_base" ); level.obeliskSoundMidObj PlaySoundOnMovingEnt( "mp_dig_obelisk_fall_mid" ); level.obeliskSoundTopObj PlaySoundOnMovingEnt( "mp_dig_obelisk_fall_top" ); } obeliskImpact() { level endon ( "game_ended" ); earthquakeOrigin = ( -1140, -108, 712 ); wait ( CONST_OBELISK_IMPACT_TIME ); Earthquake( 0.4, 3, earthquakeOrigin, 2000 ); showEnt( level.obeliskAfterClip ); level.obeliskAfterClip DisconnectPaths(); showEnt( level.obeliskPathBlocker ); level.obeliskPathBlocker ConnectPaths(); level thread delayHide( level.obeliskPathBlocker, 0.05 ); if ( level.gameType == "dom" || level.gameType == "siege" ) { updateBFlagPos(); updateBFlagObjIcon(); } level notify ( "obelisk_impact" ); } ///////////////////////////////// // SCARAB POTS ///////////////////////////////// setupScarabPot( origin ) { level endon ( "game_ended" ); breakablePot = Spawn( "script_model", origin ); breakablePot SetModel( "dig_pottery_06" ); breakablePot.origin = origin; breakablePot.health = 300; breakablePot.damagetaken = 0; breakablePot.idleSound = "mp_dig_scarab_pot_rattle"; breakablePot.lidSound = "mp_dig_scarab_pot_hit_lid"; breakablePot.smokeSound = "mp_dig_scarab_pot_smoke_lp"; breakablePot.breakSound = "mp_dig_scarab_pot_explode"; breakablePot.hasScarabs = true; breakablePot.broken = false; breakablePot SetCanDamage( true ); breakablePot Solid( true ); breakablePot thread playPotRattle(); breakablePotLid = Spawn( "script_model", origin + ( 0, 0, 33 ) ); breakablePotLid SetModel( "dig_pottery_06_lid" ); /# level childthread resetScarabPot( breakablePot, origin ); #/ while ( true ) { breakablePot waittill( "damage", amount, attacker, direction_vec, point, type ); breakablePot.damagetype = type; breakablePot.damageOwner = attacker; breakablePot.damageTaken += amount; if ( breakablePot.damageTaken == amount ) breakablePot thread startPotBreaking( breakablePotLid ); } } resetScarabPot( pot, origin ) { while ( true ) { level waittill( "scarab_pot_reset" ); if ( !IsDefined( pot ) ) { setupScarabPot( origin ); } } } playPotRattle() { // self == breakable pot self endon ( "pot_lid_broken" ); level endon ("game_ended"); while ( true ) { if ( IsDefined( self ) ) self PlaySound( self.idleSound ); maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( RandomIntRange( 8, 15 ) ); } } startPotBreaking( breakablePotLid ) { // self == pot count = 0; startedfx = false; up = AnglesToUp( self.angles ); worldup = AnglesToUp( ( 0, 90, 0 ) ); dot = VectorDot( up, worldup ); offset1 = ( 0, 0, 0 ); offset2 = ( 0, 0, 32 ); if ( dot < 0.5 ) { offset1 = ( up * 22 ) - ( 0, 0, 30 ); offset2 = ( up * 22 ) + ( 0, 0, 14 ); } if ( self.damagetype != "MOD_GRENADE_SPLASH" && self.damagetype != "MOD_GRENADE" ) { while ( self.damageTaken < self.health ) { if ( !startedfx ) { breakablePotLid Hide(); self PlaySound( self.lidSound ); self notify ( "pot_lid_broken" ); self thread playPotFX( offset2 ); self PlayLoopSound( self.smokeSound ); startedfx = true; } if ( count > 20 ) count = 0; if ( count == 0 ) { self.damageTaken += ( 10 + RandomFloat( 10 ) ); } count++; wait 0.05; } } self breakPot( breakablePotLid ); } playPotFX( fxOrigin ) { level endon ( "game_ended" ); while ( IsDefined( self ) && !self.broken ) { PlayFX ( level.breakables_fx[ "scarabpot" ][ "break_top" ], self.origin + fxOrigin ); wait( 0.1 ); } } breakPot( breakablePotLid ) { // self == pot breakablePotLid Hide(); self PlaySound( self.lidSound ); self notify ( "pot_lid_broken" ); self StopLoopSound( self.smokeSound ); self.broken = true; self Solid( false ); self SetCanDamage( false ); up = AnglesToUp( self.angles ); worldup = AnglesToUp( ( 0, 90, 0 ) ); dot = VectorDot( up, worldup ); offset = ( 0, 0, 0 ); if ( dot < 0.5 ) { start = ( self.origin + ( up * 22 ) ); end = PhysicsTrace( start, ( start + ( 0, 0, -64 ) ) ); offset = end - self.origin; } offset += ( 0, 0, 4 ); self PlaySound ( self.breakSound ); //level thread play_sound_in_space(level.barrelExpSound, self.origin); PlayFX ( level.breakables_fx[ "scarabpot" ][ "break" ], self.origin + offset + ( 0, 0, 20 ) ); PlayFX ( level.breakables_fx[ "scarabpot" ][ "break_scarabs" ], self.origin + offset + ( 0, 0, 30 ) ); self Hide(); if ( self.hasScarabs ) self setupDeathZone(); } setupDeathZone() { // self == pot // Create the death zone zoneRadius = 65; zoneHeight = 10; deathZone = Spawn( "trigger_radius", self.origin, 0, zoneRadius, zoneHeight ); deathZone thread setupScarab( self.damageOwner ); // /# // Cylinder( self.origin, self.origin + ( 0, 0, zoneHeight ), zoneRadius, ( 255, 255, 255 ), false, 900 ); // #/ // Make sure we clean up the pot self Delete(); } setupScarab( attacker ) { // self == deathZone level endon ( "game_ended" ); // Create the seeker scarabLifeTime = 15; scarabMover = Spawn( "script_model", self.origin ); scarabMover SetModel( "tag_origin" ); scarabMover.sound = "mp_dig_scarab_swarm"; scarabMover.owner = attacker; scarabMover.killcamEnt = Spawn( "script_model", scarabMover.origin + ( 0, 0, 100 ) ); scarabMover.killcamEnt setModel( "tag_origin" ); // Used for spawnfactor if ( !IsDefined( level.traps ) ) level.traps = []; level.traps[ level.traps.size ] = scarabMover; scarabMover thread watchLifeTime( "scarabs_death", "scarabs_attacked_player", scarabLifeTime ); scarabMover thread cleanUpScarab( self ); scarabMover thread playScarabFX(); scarabMover PlaySound( scarabMover.sound ); // Wait a sec to allow scarabs to appear on the ground before killing anyone wait ( CONST_SCARAB_APPEAR_TIME ); scarabMover thread watchScarabTrigger( self ); } cleanUpScarab( deathZone ) { // self == scarab self waittill_any ( "scarabs_killed_player", "scarabs_death" ); // No longer a threat, so players should be able to spawn close to its position level.traps = array_remove( level.traps, self ); self Delete(); deathZone Delete(); } playScarabFX() { // self == scarab level endon ( "game_ended" ); // FX will not show up if I don't wait at least a frame before playing it waitframe(); PlayFXOnTag ( level.breakables_fx[ "scarab" ][ "ground" ], self, "tag_origin" ); PlayFXOnTag ( level.breakables_fx[ "scarab" ][ "flyers" ], self, "tag_origin" ); } delayStopSound( delayTime ) { level endon ( "game_ended" ); wait ( delayTime ); self StopSounds(); } watchScarabTrigger( deathZone ) { // self == scarab self endon ( "scarabs_death" ); while ( true ) { // Someone is within the aggro range, kill him! deathZone waittill ( "trigger", victim ); if ( level.teamBased ) { if ( IsDefined( victim ) && IsPlayer( victim ) && ( victim.team != self.owner.team || victim == self.owner ) ) { self thread delayStopSound( 0.5 ); self thread killNearbyVictim( victim ); self notify ( "scarabs_attacked_player" ); break; } } else { if ( IsDefined( victim ) && IsPlayer( victim ) ) { self thread delayStopSound( 0.5 ); self thread killNearbyVictim( victim ); self notify ( "scarabs_attacked_player" ); break; } } } } killNearbyVictim( victim ) { // self == scarab self endon ( "scarabs_killed_player" ); self endon ( "scarabs_death" ); self thread watchScarabKill( victim ); if ( IsDefined( victim ) && IsAlive( victim ) ) { StopFXOnTag ( level.breakables_fx[ "scarab" ][ "flyers" ], self, "tag_origin" ); self MoveTo ( victim.origin, 0.8 ); } } watchScarabKill( victim, killImmediate ) { // self == scarab victim endon ( "death" ); self endon ( "scarabs_killed_player" ); self endon ( "scarabs_death" ); // Make sure the player is touching the scarabs before killing them if ( !IsDefined( killImmediate ) || killImmediate == false ) { while ( DistanceSquared( victim.origin, self.origin ) > CONST_SCARAB_KILL_RADIUS_SQUARED ) { wait ( 0.05 ); } StopFXOnTag ( level.breakables_fx[ "scarab" ][ "ground" ], self, "tag_origin" ); } // Swarm the player scarab_effect_ent = undefined; if ( !victim isUsingRemote() ) { scarab_effect_ent = SpawnFXForClient( level.breakables_fx[ "scarab" ][ "screen" ], victim GetEye(), victim ); TriggerFX( scarab_effect_ent ); scarab_effect_ent setfxkilldefondelete(); victim PlayLocalSound( "mp_dig_scarab_plr_overtaken" ); } victim PlaySoundOnMovingEnt( "mp_dig_scarab_npc_overtaken" ); victim thread killFXOnPlayerDeath( scarab_effect_ent ); victim thread doPeriodicDamage( scarab_effect_ent, CONST_SCARAB_DMG_TICK, CONST_SCARAB_DMG_INTERVAL, "MOD_SCARAB", self, "scarabs_killed_player", self.owner, self ); //victim thread playDeathAnimScarabs(); } playCustomDeathSound( victim, sMeansofDeath, eInflictor ) { if ( sMeansofDeath == "MOD_SCARAB" ) { // Left blank on purpose, since we don't want death sounds to play for this type of damage } else victim playDeathSound(); } playDeathAnimScarabs() { // self = victim level endon ( "game_ended" ); self waittill ( "death_delay_start" ); deathAnim = self.body getCorpseAnim(); deathAnimModel = Spawn( "script_model", self.body.origin ); deathAnimModel SetModel( "scarab_fullbody_bone_fx" ); deathAnimModel.origin = self.body.origin; deathAnimModel.angles = self.body.angles; deathAnimModel LinkTo( self.body, "tag_origin", ( 0, 50, 0 ), deathAnimModel.angles ); if ( deathAnim == %mp_scarab_death_stand_1 ) deathAnimModel ScriptModelPlayAnim( "scarab_fullbody_bone_fx_stand_anim" ); else if ( deathAnim == %mp_scarab_death_crouch_1 ) deathAnimModel ScriptModelPlayAnim( "scarab_fullbody_bone_fx_crouch_anim" ); else deathAnimModel ScriptModelPlayAnim( "scarab_fullbody_bone_fx_prone_anim" ); for ( index = 0; index < CONST_MAX_SCARAB_DEATH_BONES; index++ ) { if ( index < 2 ) continue; else if ( index < 10 ) PlayFXOnTag( level.dig_fx[ "scarab" ][ "deathAnim" ], deathAnimModel, "Point00" + index ); else PlayFXOnTag( level.dig_fx[ "scarab" ][ "deathAnim" ], deathAnimModel, "Point0" + index ); waitframe(); } self thread stopFXOnPlayerSpawn( deathAnimModel ); } stopFXOnPlayerSpawn( model ) { level endon ( "game_ended" ); maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( CONST_SCARAB_FALLOFF_TIME ); for ( index = 0; index < CONST_MAX_SCARAB_DEATH_BONES; index++ ) { if ( index < 2 ) continue; else if ( index < 10 ) StopFXOnTag( level.dig_fx[ "scarab" ][ "deathAnim" ], model, "Point00" + index ); else StopFXOnTag( level.dig_fx[ "scarab" ][ "deathAnim" ], model, "Point0" + index ); waitframe(); } model delete(); } ///////////////////////////////// // HIDDEN TREASURE ROOM ///////////////////////////////// setupRadio() { level endon ( "game_ended" ); level.treasure_room_jackpot = false; level.radioArray = GetEntArray( "secret_room_radio", "targetname" ); // Minutes until jackpot jackPotStartTime = RandomIntRange ( 2, 5 ); radioLoopTime = RandomIntRange( 30, 60 ); level thread jackpotWatcher( jackPotStartTime, radioLoopTime ); } jackpotWatcher( jackPotStartTime, radioLoopTime ) { level endon ( "game_ended" ); while ( true ) { if ( getMinutesPassed() > jackPotStartTime && ( IsDefined( level.treasure_room_jackpot ) && !level.treasure_room_jackpot ) ) { // Jackpot Time foreach ( radio in level.radioArray ) { // Play the jackpot voices if ( IsDefined ( radio ) ) radio PlaySound( "mp_dig_uk_tomb_special" ); } level thread jackpotTimeout( CONST_CHEST_JACKPOT_LENGTH ); level.treasure_room_jackpot = true; } else { foreach ( radio in level.radioArray ) { // Play the regular voices if ( IsDefined ( radio ) ) radio PlaySound( "mp_dig_uk_tomb1" ); } } maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( radioLoopTime ); } } jackpotTimeout( jackpotLength ) { level endon ( "game_ended" ); level endon ( "chest_raided" ); maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( jackpotLength ); // If no one grabs the jackpot after a certain amount of time, it will go away level.treasure_room_jackpot = undefined; } setupTreasureRoom() { level.treasure_room_open = false; level.treasureDoor = GetEnt( "secret_room_door_model", "targetname" ); level.treasureDoorClip = GetEnt( "secret_room_door", "targetname" ); // Setup the door if ( IsDefined( level.treasureDoor ) && IsDefined( level.treasureDoorClip ) ) level.treasureDoorClip LinkTo( level.treasureDoor ); level.flameDeathZone = GetEnt( "secret_room_kill", "targetname" ); // Make sure the damage for the kill zone is initially set to 0 if ( IsDefined( level.flameDeathZone ) ) { level.flameDeathZone.dmg = 0; level.flameDeathZone thread killAll( "flame_ended", 5000, "MOD_CRUSH" ); level.flameDeathZone trigger_off(); } // Grab all of the torches that we want to act as the key for the room level thread watchTorchesUsed(); level.key_torches = GetEntArray( "torch_trigs", "targetname" ); foreach ( torch in level.key_torches ) { //"Hold [{+activate}] to Rotate the Torch." torch SetHintString ( &"MP_DIG_ACTIVATE_TORCH" ); torch thread watchTorchUse(); torch.used = false; } } watchTorchUse() { // self == torch trigger level endon ( "game_ended" ); self waittill( "trigger", player ); self activateTorch( player ); } activateTorch( player ) { // self == torch trigger self MakeUnusable(); // Rotate the torch 45 degrees torch = GetEnt ( self.target, "targetname" ); torch RotatePitch( -45, 4 ); torch PlaySound( "mp_dig_torch_rotate" ); torchRotateFX = SpawnFx( level.dig_fx[ "torch" ][ "sand" ], torch.origin ); TriggerFX( torchRotateFX ); self.used = true; level notify ( "torch_used", player ); } watchTorchesUsed() { level endon ( "game_ended" ); torchesUsed = 0; while ( true ) { level waittill ( "torch_used", player ); torchesUsed += 1; if ( torchesUsed == 3 ) { level.dig_hidden_door_owner = player; level thread openTreasureRoom(); torchesUsed = 0; } } } openTreasureRoom() { level endon ( "game_ended" ); offset = ( -5, -105, 0 ); if ( !IsDefined( level.doorSoundObj ) ) { level.doorSoundObj = Spawn( "script_model", level.treasureDoor.origin + offset ); level.doorSoundObj SetModel( "tag_origin" ); level.doorSoundObj LinkTo( level.treasureDoor ); // /# // level.doorSoundObj thread drawLinkedSphere( 10, 1 ); // #/ } level thread doorSounds(); level.treasureDoor RotateYaw( 75, CONST_DOOR_MOVE_TIME ); Earthquake( 0.1, CONST_DOOR_MOVE_TIME, level.treasureDoor.origin, 500 ); exploder( CONST_EXPLODER_CHEST_DOOR ); wait ( CONST_DOOR_MOVE_TIME ); level.treasureDoorClip ConnectPaths(); level.treasure_room_open = true; spawnKillstreakChest(); /# level thread resetTreasureRoom(); #/ } doorSounds() { level endon ( "game_ended" ); waitframe(); level.doorSoundObj PlaySoundOnMovingEnt( "mp_dig_treasure_door_open" ); } spawnKillstreakChest() { level.chest_rewardType = random( [ "uplink_support", "deployable_vest", "deployable_ammo", "ball_drone_radar", "aa_launcher", "jammer", "ims" ] ); // If the player opened the door after the special message has played, then make sure to give them the uber killstreak if ( IsDefined( level.treasure_room_jackpot ) && level.treasure_room_jackpot ) level.chest_rewardType = "odin_assault"; level.chest_rewardHint = game[ "strings" ][ level.chest_rewardType + "_hint" ]; if ( !IsDefined( level.chest_trigger ) ) level.chest_trigger = GetEnt ( "secret_room_chest", "targetname" ); if ( IsDefined( level.chest_trigger ) ) { if ( !IsDefined( level.chest_useObj ) ) { level.chest_useObj = maps\mp\gametypes\_gameobjects::createUseObject( "neutral", level.chest_trigger, [level.chest_trigger], (0,0,0) ); level.chest_useObj.id = "care_package"; } else { level.chest_useObj maps\mp\gametypes\_gameobjects::enableObject(); } level.chest_useObj maps\mp\gametypes\_gameobjects::setUseTime( CONST_CHEST_USE_TIME ); level.chest_useObj maps\mp\gametypes\_gameobjects::setUseHintText( level.chest_rewardHint ); level.chest_useObj maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" ); level.chest_useObj maps\mp\gametypes\_gameobjects::allowUse( "any" ); level.chest_useObj.onUse = ::chestOnUse; level.chest_useObj.onBeginUse = ::chestOnBeginUse; level.chest_useObj.onEndUse = ::chestOnEndUse; // Create the killstreak head icon if ( !IsDefined( level.chest_display ) ) level.chest_display = getStruct ( level.chest_trigger.target, "targetname" ); level.chest_display maps\mp\_entityheadIcons::setHeadIcon( level.dig_hidden_door_owner, getKillstreakOverheadIcon( level.chest_rewardType ), ( 0, 0, 0 ), 14, 14, false, undefined, undefined, undefined, undefined, false ); } } chestOnUse( player ) { player chestUpdate(); self maps\mp\gametypes\_gameobjects::disableObject(); } chestOnBeginUse( player ) { } chestOnEndUse( team, player, result ) { if( IsPlayer( player ) ) { player maps\mp\gametypes\_gameobjects::updateUIProgress( self, false ); } } chestUpdate() { level.chest_display maps\mp\_entityheadIcons::setHeadIcon( level.dig_hidden_door_owner, "", ( 0, 0, 10 ), 14, 14, false, undefined, undefined, undefined, undefined, false ); // OMG WE GONNA DIE!!! exploder( CONST_EXPLODER_CHEST_SMOKE ); level.chest_trigger PlaySound( "mp_dig_treasure_smoke" ); offset = ( 120, 70, -50); earthquakePoint = Spawn( "script_origin", level.chest_trigger.origin + offset ); earthquakePoint Hide(); // /# // earthquakePoint thread drawLinkedSphere( 400, 60, true ); // #/ Earthquake( 0.2, CONST_DOOR_MOVE_TIME + 1, earthquakePoint.origin, 400 ); earthquakePoint PlaySound( "mp_dig_door_rumble" ); self thread maps\mp\killstreaks\_killstreaks::giveKillstreak( level.chest_rewardType, false, false, self ); // We can only get the jackpot once if ( IsDefined( level.treasure_room_jackpot ) && level.treasure_room_jackpot ) { level.treasure_room_jackpot = undefined; level notify ( "chest_raided" ); } level thread notifyDeadPlayers(); level thread closeTreasureRoom(); } resetTreasureRoom() { // self == crate level endon ( "game_ended" ); level waittill ( "treasure_room_reset" ); if ( level.treasure_room_open ) level thread closeTreasureRoom(); level.chest_display maps\mp\_entityheadIcons::setHeadIcon( level.dig_hidden_door_owner, "", ( 0, 0, 10 ), 14, 14, false, undefined, undefined, undefined, undefined, false ); if ( IsDefined ( level.chest_useObj ) ) level.chest_useObj maps\mp\gametypes\_gameobjects::disableObject(); foreach ( torch in level.key_torches ) { if ( torch.used ) { torchModel = GetEnt ( torch.target, "targetname" ); torchModel RotatePitch( 45, 0.1 ); //"Hold [{+activate}] to Rotate the Torch." torch MakeUsable(); torch SetHintString ( &"MP_DIG_ACTIVATE_TORCH" ); torch thread watchTorchUse(); torch.used = false; } } level.treasure_room_jackpot = false; } notifyDeadPlayers() { level endon ( "game_ended" ); maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( CONST_DOOR_MOVE_TIME - 4 ); // Turn the trigger on early, since we still need to see who is in the room level.flameDeathZone trigger_on(); foreach ( player in level.players ) { if ( player IsTouching( level.flameDeathZone ) ) player PlayLocalSound( "mp_dig_tomb_die1" ); } } closeTreasureRoom() { level endon ( "game_ended" ); if ( IsDefined ( level.doorSoundObj ) ) level.doorSoundObj PlaySoundOnMovingEnt( "mp_dig_treasure_door_close" ); level.treasureDoor RotateYaw( -75, CONST_DOOR_MOVE_TIME ); Earthquake( 0.1, CONST_DOOR_MOVE_TIME, level.treasureDoor.origin, 500 ); maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( CONST_DOOR_MOVE_TIME ); level.treasureDoorClip DisconnectPaths(); level.treasure_room_open = false; level thread triggerFlameTrap(); } triggerFlameTrap() { // Flames ignite! exploder( CONST_EXPLODER_CHEST_FLAMES ); // Kill all players still in the room flameCenterPipes = getStructArray( "secret_room_fire_mid", "targetname" ); foreach ( index, pipe in flameCenterPipes ) { fireSoundObj = Spawn( "script_origin", pipe.origin ); fireSoundObj Hide(); fireSoundObj PlaySound ( "mp_dig_fire_wall" + ( index + 1 ) ); } foreach ( player in level.participants ) { if ( IsPlayer( player ) && isReallyAlive( player ) && player IsTouching ( level.flameDeathZone ) ) { // Play all appropriate Fire Effects on the player PlayFXOnTag ( level.dig_fx[ "flametrap" ][ "player" ], player, "j_spineupper" ); fire_effect_ent = SpawnFXForClient( level.dig_fx[ "flametrap" ][ "screen" ], player GetEye(), player ); TriggerFX( fire_effect_ent ); fire_effect_ent setfxkilldefondelete(); player thread killFXOnPlayerDeath( fire_effect_ent ); player thread stopFXOnSpawn( level.dig_fx[ "flametrap" ][ "player" ], "j_spineupper" ); player thread doPeriodicDamage( fire_effect_ent, CONST_FLAME_DMG_TICK, CONST_FLAME_DMG_INTERVAL, "MOD_SCARAB" ); } } // Notify the kill trigger to clean up anything remaining in the room after this event is finished level thread delayNotify( "flame_ended", 4 ); } setupSnakes( origin ) { level endon ( "game_ended" ); if ( !IsDefined ( level.dig_snake ) ) { level.dig_snake = Spawn( "script_model", origin ); level.dig_snake SetModel( "snake_many_mp_dig_secretroom" ); level.dig_snake.origin = origin; } while ( true ) { level.dig_snake ScriptModelPlayAnim( "snake_many_mp_dig_secretroom_anim" ); maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( CONST_SNAKE_ANIM_LENGTH ); } } ///////////////////////////////// // LEVEL KILLSTREAK ///////////////////////////////// setupShrinePerks() { level.shrinePerks = []; level.abilityCategories = maps\mp\gametypes\_class::getNumAbilityCategories(); level.abilityPerCategory = maps\mp\gametypes\_class::getNumSubAbility(); for ( abilityCategoryIndex = 0; abilityCategoryIndex < level.abilityCategories; abilityCategoryIndex++ ) { for ( abilityIndex = 0; abilityIndex < level.abilityPerCategory; abilityIndex++ ) { perkName = TableLookup( "mp/cacAbilityTable.csv", 0, abilityCategoryIndex + 1, 4 + abilityIndex ); if ( validShrinePerk( perkName ) ) level.shrinePerks[ level.shrinePerks.size ] = perkName; } } } validShrinePerk( perkName ) { validPerk = true; // There are certain exceptions we need to handle, since not all perks will work correctly mid game switch( perkName ) { case "specialty_extra_equipment": case "specialty_extra_deadly": case "specialty_extraammo": case "specialty_extra_attachment": case "specialty_gambler": case "specialty_hardline": case "specialty_twoprimaries": validPerk = false; break; } return validPerk; } // Custom Killstreak functions DIG_LEVEL_KILLSTREAK_WEIGHT = 85; digCustomCrateFunc() { if ( !IsDefined( game[ "player_holding_level_killstrek" ] ) ) game[ "player_holding_level_killstrek" ] = false; if ( !allowLevelKillstreaks() || game[ "player_holding_level_killstrek" ] || !level.allow_level_killstreak ) return; //"Hold [{+activate}] to reveal the Pharaoh's Shrine." maps\mp\killstreaks\_airdrop::addCrateType( "airdrop_assault", "dig_level_killstreak", DIG_LEVEL_KILLSTREAK_WEIGHT, ::digCrateThink, maps\mp\killstreaks\_airdrop::get_friendly_crate_model(), maps\mp\killstreaks\_airdrop::get_enemy_crate_model(), &"MP_DIG_ACTIVATE_SHRINE" ); maps\mp\killstreaks\_airdrop::generateMaxWeightedCrateValue(); level thread watch_for_dig_killstreak(); } digCrateThink( dropType ) { self endon ( "death" ); self endon ( "restarting_physics" ); level endon ( "game_ended" ); if ( IsDefined( game[ "strings" ][ self.crateType + "_hint" ] ) ) crateHint = game[ "strings" ][ self.crateType + "_hint" ]; else //"[{+activate}] Killstreak" crateHint = &"PLATFORM_GET_KILLSTREAK"; maps\mp\killstreaks\_airdrop::crateSetupForUse( crateHint, getKillstreakOverheadIcon( self.crateType ) ); // Adding custom functions to make sure people using Juggernauts are not allowed to use it self thread crateOtherCaptureThink(); self thread crateOwnerCaptureThink(); while ( true ) { self waittill ( "captured", player ); level.dig_killstreak_user = player; if ( IsPlayer( player ) ) { player SetClientOmnvar( "ui_securing", 0 ); player.ui_securing = undefined; } tryUseDigKillstreak(); self maps\mp\killstreaks\_airdrop::deleteCrate(); } } crateOtherCaptureThink( useText ) { self endon( "restarting_physics" ); while ( IsDefined( self ) ) { self waittill ( "trigger", player ); if ( IsDefined( self.owner ) && player == self.owner ) continue; if ( !self maps\mp\killstreaks\_airdrop::validateOpenConditions( player ) ) continue; if ( player isJuggernaut() ) { player IPrintLnBold( &"MP_DIG_LEVEL_KILLSTREAK_REJECT" ); } else { if ( IsDefined( level.overrideCrateUseTime ) ) useTime = level.overrideCrateUseTime; else useTime = undefined; player.isCapturingCrate = true; useEnt = self maps\mp\killstreaks\_airdrop::createUseEnt(); result = useEnt maps\mp\killstreaks\_airdrop::useHoldThink( player, useTime, useText ); if ( IsDefined( useEnt ) ) useEnt delete(); if ( !IsDefined( player ) ) return; if ( !result ) { player.isCapturingCrate = false; continue; } player.isCapturingCrate = false; self notify ( "captured", player ); } } } crateOwnerCaptureThink( useText ) { self endon( "restarting_physics" ); while ( IsDefined( self ) ) { self waittill ( "trigger", player ); if ( IsDefined( self.owner ) && player != self.owner ) continue; if ( !self maps\mp\killstreaks\_airdrop::validateOpenConditions( player ) ) continue; if ( player isJuggernaut() ) { player IPrintLnBold( &"MP_DIG_LEVEL_KILLSTREAK_REJECT" ); } else { player.isCapturingCrate = true; if ( !maps\mp\killstreaks\_airdrop::useHoldThink( player, 500, useText ) ) { player.isCapturingCrate = false; continue; } player.isCapturingCrate = false; self notify ( "captured", player ); } } } watch_for_dig_killstreak() { while ( true ) { level waittill( "createAirDropCrate", dropCrate ); if ( IsDefined( dropCrate ) && IsDefined( dropCrate.crateType ) && dropCrate.crateType == "dig_level_killstreak" ) { disable_level_killstreak(); captured = wait_for_capture( dropCrate ); if ( !captured ) { enable_level_killstreak(); } 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; } enable_level_killstreak() { maps\mp\killstreaks\_airdrop::changeCrateWeight( "airdrop_assault", "dig_level_killstreak", DIG_LEVEL_KILLSTREAK_WEIGHT ); level.intelRewardOverride = "dig_level_killstreak"; } disable_level_killstreak() { maps\mp\killstreaks\_airdrop::changeCrateWeight( "airdrop_assault", "dig_level_killstreak", 0 ); level.intelRewardOverride = undefined; } digCustomKillstreakFunc() { AddDebugCommand( "devgui_cmd \"MP/Killstreak/Level Event:5/Care Package/Dig Killstreak\" \"set scr_devgivecarepackage dig_level_killstreak; set scr_devgivecarepackagetype airdrop_assault\"\n" ); level.killStreakFuncs[ "dig_level_killstreak" ] = ::tryUseDigKillstreak; } digCustomBotKillstreakFunc() { AddDebugCommand("devgui_cmd \"MP/Bots(Killstreak)/Level Events:5/Dig Killstreak\" \"set scr_testclients_givekillstreak dig_level_killstreak\"\n"); maps\mp\bots\_bots_ks::bot_register_killstreak_func( "dig_level_killstreak", maps\mp\bots\_bots_ks::bot_killstreak_simple_use ); } tryUseDigKillstreak( lifeId, streakName ) { // Give the perks directly from the care package level.dig_killstreak_user givePerkBonus(); // Give the blessing back on spawn level.dig_killstreak_user thread watchPerkBlessing(); // Make sure to take away the FX after a player equips the juggernaut level.dig_killstreak_user thread watchJuggUse(); // Watch Remote Killstreak use level.dig_killstreak_user thread watchRemoteUse( "using_remote" ); level.dig_killstreak_user thread watchRemoteUse( "stopped_using_remote" ); // Give the effect back on teleport if ( level.gameType == "blitz" ) level.dig_killstreak_user thread watchBlitzTeleport(); // Apply the FX for any new player coming into the game level thread watchPlayersConnect( level.dig_killstreak_user ); level thread watchBlessingEnd( level.dig_killstreak_user ); level thread teamPlayerCardSplash( "used_dig_level_killstreak", level.dig_killstreak_user ); } givePerkBonus() { // self == player if ( !IsDefined( level.player_life_counter ) ) level.player_life_counter = 0; // All your perks are belong to us foreach ( perk in level.shrinePerks ) { if ( !self _hasPerk( perk ) ) self givePerk( perk, false ); } self thread maps\mp\gametypes\_hud_message::SplashNotify( "mp_dig_all_perks" ); // FX level thread showBlessingFX( self ); self thread stopFXOnSpawn( level.dig_fx[ "shrine" ][ "player" ], "tag_origin" ); if ( !IsDefined( self.shrine_effect_ent ) ) { self.shrine_effect_ent = SpawnFXForClient( level.dig_fx[ "shrine" ][ "screen" ], self GetEye(), self ); TriggerFX( self.shrine_effect_ent ); self.shrine_effect_ent setfxkilldefondelete(); self thread killFXOnPlayerDeath( self.shrine_effect_ent, "reset_perk_bonus", "user_juggernaut", "user_remotekillstreak" ); } // Sound self PlayLocalSound( "mp_dig_plr_spawn_with_powers" ); self PlayLoopSound( "mp_dig_magic_powers_flame_lp" ); // Re-update the sit rep perk level notify ( "update_bombsquad" ); level.player_life_counter += 1; } resetPerkBonus() { level.player_life_counter = 0; self SetClientOmnvar( "ui_dig_killstreak_show", -1 ); StopFXOnTag ( level.dig_fx[ "shrine" ][ "player" ], self, "tag_origin" ); if( IsDefined( self.shrine_effect_ent ) ) self.shrine_effect_ent delete(); level notify ( "reset_perk_bonus" ); } watchPerkBlessing() { self endon ( "disconnect" ); self endon ( "blessing_ended" ); self endon ( "joined_team" ); level endon ( "game_ended" ); while ( true ) { self waittill ( "spawned_player" ); self SetClientOmnvar( "ui_dig_killstreak_show", -1 ); // Since we are stopping the effect on spawn, we need to wait a frame before it is set again waitframe(); if ( level.player_life_counter < 4 && !self isJuggernaut() ) { self givePerkBonus(); // Because Blindeye and Off the Grid are given to the player previously ( because of the Spawn Perks system ), we need to regive them the perks again if ( !self hasPerkInLoadout( "specialty_blindeye" ) || !self hasPerkInLoadout( "specialty_gpsjammer" ) ) self childthread reGiveSpawnPerks(); } else if ( level.player_life_counter == 4 ) self notify ( "blessing_ended" ); } } hasPerkInLoadout( checkPerk ) { existingPerk = false; if ( IsDefined ( self.pers[ "loadoutPerks" ] ) && self.pers[ "loadoutPerks" ].size > 0 ) { foreach ( perk in self.pers[ "loadoutPerks" ] ) { if ( checkPerk == perk ) { existingPerk = true; break; } } } return existingPerk; } reGiveSpawnPerks() { self waittill ( "starting_perks_unset" ); if ( !self _hasPerk ( "specialty_blindeye" ) ) self givePerk( "specialty_blindeye", false ); if ( !self _hasPerk ( "specialty_gpsjammer" ) ) self givePerk( "specialty_gpsjammer", false ); } watchJuggUse() { self endon ( "disconnect" ); self endon ( "blessing_ended" ); self endon ( "joined_team" ); level endon ( "game_ended" ); while ( true ) { level waittill ( "juggernaut_equipped", player ); if ( self == player ) { self.shrine_effect_ent delete(); StopFXOnTag ( level.dig_fx[ "shrine" ][ "player" ], self, "tag_origin" ); level notify ( "user_juggernaut" ); } } } watchRemoteUse( notifyMSG ) { self endon ( "disconnect" ); self endon ( "blessing_ended" ); self endon ( "joined_team" ); level endon ( "game_ended" ); while ( true ) { self waittill ( notifyMSG ); if ( notifyMSG == "using_remote" ) { if ( IsDefined ( self.shrine_effect_ent ) ) { self.shrine_effect_ent delete(); level notify ( "user_remotekillstreak" ); } } else if ( notifyMSG == "stopped_using_remote" ) { if ( !IsDefined( self.shrine_effect_ent ) ) { self.shrine_effect_ent = SpawnFXForClient( level.dig_fx[ "shrine" ][ "screen" ], self GetEye(), self ); TriggerFX( self.shrine_effect_ent ); self.shrine_effect_ent setfxkilldefondelete(); self thread killFXOnPlayerDeath( self.shrine_effect_ent, "reset_perk_bonus", "user_juggernaut", "user_remotekillstreak" ); } } } } watchBlitzTeleport() { self endon ( "disconnect" ); self endon ( "blessing_ended" ); self endon ( "joined_team" ); level endon ( "game_ended" ); while ( true ) { level waittill ( "portal_used", team ); waitframe(); if ( team != self.team && ( IsDefined( self.teleporting ) && self.teleporting ) ) { // For some reason on blitz, the effect gets cut off when the player's origin is suddenly changed // So we will need to reapply the fx on the player ( after the teleport ), so it shows up correctly level thread showBlessingFX( self ); } } } watchPlayersConnect( blessingUser ) { blessingUser endon ( "disconnect" ); blessingUser endon ( "blessing_ended" ); blessingUser endon ( "joined_team" ); level endon ( "game_ended" ); while ( true ) { level waittill( "connected", player ); player thread playFXOnPlayerSpawn( blessingUser ); } } watchBlessingEnd( blessingUser ) { level endon ( "game_ended" ); blessingUser waittill_any ( "disconnect", "joined_team", "blessing_ended" ); level.player_life_counter = 0; } showBlessingFX( blessingUser ) { blessingUser endon ( "disconnect" ); blessingUser endon ( "blessing_ended" ); blessingUser endon ( "joined_team" ); level endon ( "game_ended" ); // For some reason PlayFXOnTag would not always show the FX again to other clients, so we are making sure all clients can see the FX by using PlayFXOnTagForClients foreach ( player in level.players ) { if ( IsDefined( player ) && IsDefined( blessingUser ) && IsReallyAlive( blessingUser ) ) PlayFXOnTagForClients( level.dig_fx[ "shrine" ][ "player" ], blessingUser, "tag_origin", player ); waitframe(); } } playFXOnPlayerSpawn( blessingUser ) { blessingUser endon ( "disconnect" ); blessingUser endon ( "blessing_ended" ); blessingUser endon ( "joined_team" ); level endon ( "game_ended" ); self waittill ( "spawned_player" ); if ( IsDefined( self ) && IsDefined( blessingUser ) && IsReallyAlive( blessingUser ) ) PlayFXOnTagForClients( level.dig_fx[ "shrine" ][ "player" ], blessingUser, "tag_origin", self ); } ///////////////////////////////// // MISC ///////////////////////////////// watchLifeTime( sendNotify, endonNotify, lifeTime ) { self endon ( endonNotify ); while ( lifeTime > 0 ) { lifeTime -= 1; wait ( 1 ); } self notify ( sendNotify ); } doPeriodicDamage( screenEffect, damage, interval, damageType, notifyObject, notifyMsg, owner, source ) { level endon ( "game_ended" ); if ( !IsDefined( damageType ) ) damageType = "MOD_CRUSH"; hasPlayedDeathSound = false; attacker = undefined; inflictor = undefined; if ( IsDefined( owner ) && IsDefined( source ) ) { attacker = owner; inflictor = source; } while( IsReallyAlive( self ) ) { self DoDamage( damage, self.origin, attacker, inflictor, damageType ); if ( damageType == "MOD_SCARAB" ) { // Start Screaming if ( self.health <= 30 && !hasPlayedDeathSound ) { rand = RandomIntRange( 1,8 ); type = "male"; if ( self hasFemaleCustomizationModel() ) type = "female"; if ( self.team == "axis" ) self PlaySound( type + "_scarab_death_russian"+ rand ); else self PlaySound( type + "_scarab_death_american"+ rand ); hasPlayedDeathSound = true; } } wait ( interval ); } if ( IsDefined( notifyObject ) && IsDefined ( notifyMsg ) ) notifyObject notify ( notifyMsg ); } killFXOnPlayerDeath( effect, endonMSG, endonMSG2, endonMSG3 ) { level endon ( "game_ended" ); // Mostly used for when things are reset via dvar command if ( IsDefined( endonMSG ) ) level endon ( endonMSG ); if ( IsDefined( endonMSG2 ) ) level endon ( endonMSG2 ); if ( IsDefined( endonMSG3 ) ) level endon ( endonMSG3 ); self waittill_any ( "killed_player", "disconnect" ); if ( IsDefined ( effect ) ) { if ( IsArray( effect ) ) { foreach ( fx in effect ) { fx delete(); } } else effect delete(); } } stopFXOnSpawn( effect, tag ) { level endon ( "game_ended" ); self waittill ( "spawned_player" ); StopFXOnTag( effect, self, tag ); } drawLinkedSphere( radius, length, singleShot ) { level endon ( "game_ended" ); previousPosition = ( 0, 0, 0 ); while ( true ) { if ( previousPosition != self.origin ) { Sphere( self.origin, radius, (255, 255, 255), false, int( length * 30 ) ); previousPosition = self.origin; if ( IsDefined ( singleShot ) && singleShot ) break; } waitframe(); } } getFirstAlivePlayer() { foreach( player in level.participants ) { if ( IsDefined( player ) && isReallyAlive ( player ) ) return player; } } delayHide( ent, time ) { level endon ( "game_ended" ); wait ( time ); ent Hide(); ent NotSolid(); } killAll( waitNotify, damage, meansOfDeath ) { // self == trigger level endon ( "game_ended" ); level waittill ( waitNotify ); if ( IsDefined( self.trigger_off ) ) self trigger_on(); waitframe(); attacker = getAttacker( waitNotify ); inflictor = getInflictor( waitNotify ); // Character ( Player/Agents ) self killAllTouchingTrigger( level.characters, CONST_CHARACTERS, damage, meansOfDeath, attacker, inflictor ); // Turrets self killAllTouchingTrigger( level.turrets, CONST_KILLSTREAKS, damage, meansOfDeath, attacker, inflictor ); // IMS self killAllTouchingTrigger( level.placedIMS, CONST_KILLSTREAKS, damage, meansOfDeath, attacker, inflictor ); // Ball Drones self killAllTouchingTrigger( level.ballDrones, CONST_KILLSTREAKS, damage, meansOfDeath, attacker, inflictor ); // SatComs self killAllTouchingTrigger( level.uplinks, CONST_KILLSTREAKS, damage, meansOfDeath, attacker, inflictor ); // Remote UAVs ( Gryphon ) self killAllTouchingTrigger( level.remote_uav, CONST_KILLSTREAKS, damage, meansOfDeath, attacker, inflictor ); // Deployable Boxes boxes = []; script_models = GetEntArray( "script_model", "classname" ); foreach ( mod in script_models ) { if ( IsDefined( mod.boxtype ) ) { boxes[ boxes.size ] = mod; } } self killAllTouchingTrigger( boxes, CONST_KILLSTREAKS, damage, meansOfDeath, attacker, inflictor ); // This Effects All Placed Equipments self killAllTouchingTrigger( level.mines, CONST_EQUIPMENT, damage, meansOfDeath, attacker, inflictor ); /////////////////////// // Special Cases /////////////////////// // SD/SR Bomb if ( level.gametype == "sd" || level.gametype == "sr" ) { if ( IsDefined ( level.sdBomb ) && level.sdBomb.visuals[0] IsTouching( self ) ) { level.sdBomb notify( "stop_pickup_timeout" ); level.sdBomb maps\mp\gametypes\_gameobjects::returnHome(); } } // Care Packages foreach ( package in level.carePackages ) { if ( package.friendlyModel IsTouching( self ) || package.enemyModel IsTouching( self ) ) package maps\mp\killstreaks\_airdrop::deleteCrate(); } //Intel // TODO: Need to kill intels that get stuck inside of the obelisk if ( IsDefined( level.intelEnt ) ) { if ( level.intelEnt[ "visuals" ] IsTouching( self ) ) level.intelEnt[ "dropped_time" ] = -60000; //Force intel to reset } // Cleanup anything else remaining, such as sd/sr bombs level thread activateKillTrigger( self, damage ); } killAllTouchingTrigger( entArray, type, damage, meansOfDeath, attacker, inflictor ) { // self == trigger foreach( ent in entArray ) { if ( IsDefined( ent ) && ent IsTouching( self ) ) { switch ( type ) { case CONST_CHARACTERS: // If you have two agents getting killed by the obelisk at the same time, it won't kill the second one ( if they are both owned by the person who initiated the obelisk ) // To get around this issue, I need to force the attacker to undefined if ( IsAgent ( ent ) && IsDefined ( ent.team ) && ent.team == attacker.team ) ent DoDamage( damage, ent.origin, undefined, inflictor, meansOfDeath ); else ent DoDamage( damage, ent.origin, attacker, inflictor, meansOfDeath ); break; case CONST_KILLSTREAKS: case CONST_EQUIPMENT: ent destroyKillstreak( damage, meansOfDeath, attacker ); break; } } } } destroyKillstreak( damage, meansOfDeath, attacker ) { // self == killstreak direction_vec = ( 0, 0, 0 ); point = ( 0, 0, 0 ); modelName = ""; tagName = ""; partName = ""; iDFlags = undefined; weaponName = "killstreak_emp_mp"; self notify( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weaponName ); } activateKillTrigger( trigger, damage ) { level endon ( "game_ended" ); if ( IsDefined ( trigger ) ) { trigger.dmg = damage; waitframe(); trigger.dmg = 0; trigger trigger_off(); } } delayNotify( notifyMsg, delayTime ) { level endon ( "game_ended" ); wait ( delayTime ); level notify ( notifyMsg ); } showEnt( ent ) { ent Show(); ent Solid(); } hideEnt( ent ) { ent Hide(); ent NotSolid(); } getAttacker( waitNotify ) { attacker = undefined; switch( waitNotify ) { case "obelisk_impact": attacker = level.obeliskOwner; break; case "flame_ended": attacker = level.dig_hidden_door_owner; break; } return attacker; } getInflictor( waitNotify ) { inflictor = undefined; switch( waitNotify ) { case "obelisk_impact": inflictor = level.obeliskAfterClip; break; case "flame_ended": inflictor = level.treasureDoorClip; break; } return inflictor; } nuke_custom_visionset() { level waittill( "nuke_death" ); wait 1.3; level notify ( "nuke_death" ); thread nuke_custom_visionset(); } nukeDeathVision() { level.nukeVisionSet = "aftermath_mp_dig"; setExpFog(512, 2048, 0.578828, 0.802656, 1, 0.5, 0.75, 5, 0.382813, 0.350569, 0.293091, .5, (1, -0.109979, 0.267867), 0, 80, 1, 0.179688, 26, 180); VisionSetNaked( level.nukeVisionSet, 5 ); VisionSetPain( level.nukeVisionSet ); } delayExploder( time, exploderID ) { level endon ( "game_ended" ); wait ( time ); exploder( exploderID ); } delayTrigger( time, state ) { level endon ( "game_ended" ); wait ( time ); if ( state == "on" ) self trigger_on(); else self trigger_off(); }