#using scripts\mp\_teamops; #using scripts\mp\_util; #using scripts\mp\bots\_bot; #using scripts\mp\gametypes\_battlechatter; #using scripts\mp\gametypes\_deathicons; #using scripts\mp\gametypes\_dogtags; #using scripts\mp\gametypes\_globallogic; #using scripts\mp\gametypes\_globallogic_audio; #using scripts\mp\gametypes\_globallogic_defaults; #using scripts\mp\gametypes\_globallogic_score; #using scripts\mp\gametypes\_globallogic_spawn; #using scripts\mp\gametypes\_globallogic_ui; #using scripts\mp\gametypes\_globallogic_utils; #using scripts\mp\gametypes\_killcam; #using scripts\mp\gametypes\_loadout; #using scripts\mp\gametypes\_prop_controls; #using scripts\mp\gametypes\_prop_dev; #using scripts\mp\gametypes\_spawning; #using scripts\mp\gametypes\_spawnlogic; #using scripts\mp\killstreaks\_killstreaks; #using scripts\mp\teams\_teams; #using scripts\shared\ai_shared; #using scripts\shared\array_shared; #using scripts\shared\callbacks_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\damagefeedback_shared; #using scripts\shared\flag_shared; #using scripts\shared\fx_shared; #using scripts\shared\gameobjects_shared; #using scripts\shared\hostmigration_shared; #using scripts\shared\hud_util_shared; #using scripts\shared\math_shared; #using scripts\shared\scoreevents_shared; #using scripts\shared\system_shared; #using scripts\shared\tweakables_shared; #using scripts\shared\util_shared; #using scripts\shared\weapons\_weapons; #namespace prop; #precache( "string", "OBJECTIVES_PH_ATTACKER" ); #precache( "string", "OBJECTIVES_PH_DEFENDER" ); #precache( "string", "OBJECTIVES_PH_ATTACKER_SCORE" ); #precache( "string", "OBJECTIVES_PH_DEFENDER_SCORE" ); #precache( "string", "OBJECTIVES_PH_ATTACKER_HINT" ); #precache( "string", "OBJECTIVES_PH_DEFENDER_HINT" ); #precache( "string", "MP_PH_STARTS_IN" ); #precache( "string", "MP_PH_WHISTLE_IN" ); #precache( "string", "MP_PH_WHISTLING" ); #precache( "string", "MP_PH_ALIVE" ); #precache( "string", "MP_PH_FINAL_WHISTLE" ); #precache( "string", "MP_PH_SPIN" ); #precache( "string", "MP_PH_SPIN_PC" ); #precache( "string", "MP_PH_SLOPE" ); #precache( "string", "MP_PH_SLOPE_PC" ); #precache( "string", "MP_PH_CHANGE" ); #precache( "string", "MP_PH_CLONE" ); #precache( "string", "MP_PH_ZOOM" ); #precache( "string", "MP_PH_SPECKEY" ); #precache( "string", "MP_PH_SLOPED" ); #precache( "string", "MP_PH_SLOPED_PC" ); #precache( "string", "MP_PH_LOCK" ); #precache( "string", "MP_PH_LOCKED" ); #precache( "string", "MP_PH_SPECCOMMANDS" ); #precache( "string", "MP_PH_FLASH" ); #precache( "string", "MP_PH_HIDEPROP" ); #precache( "string", "MP_PH_SHOWPROP" ); #precache( "string", "MP_PH_TIEBREAKER" ); #precache( "string", "SCORE_LAST_ALIVE" ); #precache( "string", "MP_PH_TIEBREAKER_KILL" ); #precache( "string", "MP_PH_TIEBREAKER_TIME" ); #precache( "string", "MP_PH_MINIGAME_FIRST" ); #precache( "string", "MP_PH_MINIGAME_SECOND" ); #precache( "string", "MP_PH_MINIGAME_THIRD" ); #precache( "string", "MP_PH_PREGAME_HUNT" ); #precache( "string", "MP_PH_PREGAME_CHASE" ); #precache( "string", "MP_PH_PING" ); #precache( "string", "MP_PH_EMPTY" ); #precache( "string", "SCORE_DECOY_KILLED" ); #precache( "material", "t7_hud_waypoints_safeguard_location" ); #precache( "fx", "explosions/fx_exp_grenade_concussion" ); #precache( "fx", "explosions/fx_prop_exp" ); #precache( "fx", "player/fx_plyr_clone_reaper_appear" ); /* Prop Hunt */ function autoexec __init__sytem__() { system::register("prop",&__init__,undefined,undefined); } function __init__() { clientfield::register( "allplayers", "hideTeamPlayer" , 27000, 2, "int" ); clientfield::register( "allplayers", "pingHighlight" , 27000, 1, "int" ); } function main() { globallogic::init(); util::registerRoundSwitch( 0, 9 ); util::registerTimeLimit( 0, 4 ); util::registerScoreLimit( 0, 0 ); util::registerRoundLimit( 0, 4 ); util::registerRoundWinLimit( 0, 3 ); util::registerNumLives( 0, 1 ); level.phSettings = SpawnStruct(); level.phSettings.propHideTime = 30; level.phSettings.propWhistleTime = getMapMaxWhistleTime(); level.phSettings.propChangeCount = 2; level.phSettings.propNumFlashes = 1; level.phSettings.propNumClones = 3; level.phSettings.propSpeedScale = 1.4; level.phSettings.hunterNumPings = 2; level.phSettings.allowLoadouts = false; level.phSettings.nuketownVariant = ( level.script == "mp_nuketown_x" ); level.phSettings.altModeManyClones = level.phSettings.nuketownVariant; if ( level.phSettings.altModeManyClones ) { level.phSettings.propNumClones = 9; } globallogic::registerFriendlyFireDelay( level.gameType, 15, 0, 1440 ); level.isPropHunt = true; level.allow_teamchange = "1"; level.killstreaksEnabled = false; level.teamBased = true; level.overrideTeamScore = true; level.alwaysUseStartSpawns = true; level.scoreRoundWinBased = ( GetGametypeSetting( "cumulativeRoundScores" ) == false ); level.teamScorePerKill = GetGametypeSetting( "teamScorePerKill" ); level.teamScorePerDeath = GetGametypeSetting( "teamScorePerDeath" ); level.teamScorePerHeadshot = GetGametypeSetting( "teamScorePerHeadshot" ); level.killstreaksGiveGameScore = GetGametypeSetting( "killstreaksGiveGameScore" ); level.onStartGameType =&onStartGameType; level.onSpawnPlayer =&onSpawnPlayer; level.onPlayerDisconnect =&onPlayerDisconnect; level.onRoundEndGame =&onRoundEndGame; level.onRoundSwitch =&onRoundSwitch; level.gameModeAssistedSuicide =&gameModeAssistedSuicide; level.onPlayerKilled =&onPlayerKilled; level.onOneLeftEvent =&onOneLeftEvent; level.onTimeLimit =&onTimeLimit; level.onDeadEvent =&onDeadEvent; level.customPlayPainSound =&playDamageSoundPH; level.customPlayDeathSound =&playDeathSoundPH; level.overridePlayerDamage =&gamemodeModifyPlayerDamage; level.gametypeRoundEndScoreHud =&gametypeRoundEndScoreHud; level.checkSpawnPoint =&checkSpawnPoint; level.finalizeStartSpawn =&finalizeStartSpawn; level.maySpawnGameMode =&maySpawnGameMode; level.givecustomloadout =&giveCustomLoadout; level.prematchPeriodSpawnFunc =&prematchPeriodSpawn; level.playerAvoidDamageGameMode =&playerAvoidDamageGameMode; level.customDamageShellshockAndRumble =&customDamageShellshockAndRumble; level.determineWinner =&determineWinner; level.disableWeaponAccuracyTracking = true; gameobjects::register_allowed_gameobject( level.gameType ); // Sets the scoreboard columns and determines with data is sent across the network globallogic::setvisiblescoreboardcolumns( "score", "objtime", "kills", "deaths", "assists" ); level.propList = []; level.propIndex = []; level.spawnPropList = []; level.abilities = Array( "FLASH", "CLONE" ); populatePropList(); level.gracePeriod = Int( level.phSettings.propHideTime + 0.5 ); level thread onPlayerConnect(); level thread delaySet(); level thread monitorHostmigrationTime(); if ( level.phSettings.nuketownVariant ) level thread manageMannequins(); ShatterAllGlass(); util::set_dvar_int_if_unset( "scr_prop_minigame", 1 ); /# level.alwaysAllowSpawn = false; thread prop_dev::propDevGui(); #/ } function getMapMaxWhistleTime() { if ( level.script == "mp_chinatown" ) { return 18; } else if ( level.script == "mp_redwood" ) { return 20; } else if ( level.script == "mp_nuketown_x" ) { return 0; } return 30; } function delaySet() { {wait(.05);}; level.playStartConversation = false; level.allowSpecialistDialog = false; } function onEndGame( winningTeam ) { if ( isdefined( winningTeam ) && isdefined( level.teams[winningTeam] ) ) globallogic_score::giveTeamScoreForObjective( winningTeam, 1 ); } function onRoundSwitch() { game["switchedsides"] = !game["switchedsides"]; if ( level.scoreRoundWinBased ) { foreach( team in level.teams ) { [[level._setTeamScore]]( team, game["roundswon"][team] ); } } } function onRoundEndGame( finalRoundWinner ) { gameWinner = finalRoundWinner; // need to evaluate winner here for round based game modes if ( level.gameEnded ) { gameWinner = determineWinnerInternal( gameWinner, true ); } if ( ( gameWinner == "allies" ) || ( gameWinner == "axis" ) ) ph_setFinalKillCamWinner( gameWinner ); return gameWinner; } function determineWinner( roundWinner ) { return determineWinnerInternal( roundWinner, false ); } function determineWinnerInternal( roundWinner, adjustTieScore ) { gameWinner = roundWinner; winEvent = "roundswon"; level.propTieBreaker = "none"; if ( game[winEvent]["allies"] == game[winEvent]["axis"] ) { level.propTieBreaker = "kills"; if ( game["propScore"]["axis"] == game["propScore"]["allies"] ) { level.propTieBreaker = "time"; if ( game["hunterKillTime"]["axis"] == game["hunterKillTime"]["allies"] ) { level.propTieBreaker = "tie"; gameWinner = "tie"; } else if ( game["hunterKillTime"]["axis"] < game["hunterKillTime"]["allies"] ) { gameWinner = "axis"; } else { gameWinner = "allies"; } } else if ( game["propScore"]["axis"] > game["propScore"]["allies"] ) { gameWinner = "axis"; } else { gameWinner = "allies"; } // Give the tie-breaker winner one extra round win to showcase in the AAR (so it doesn't look like a tie) if ( gameWinner != "tie" && adjustTieScore ) level thread givePHTeamScore( gameWinner ); } else if ( game[winEvent]["axis"] > game[winEvent]["allies"] ) { gameWinner = "axis"; } else { gameWinner = "allies"; } return gameWinner; } function onScoreCloseMusic() { teamScores = []; while( !level.gameEnded ) { scoreLimit = level.scoreLimit; scoreThreshold = scoreLimit * .1; scoreThresholdStart = abs(scoreLimit - scoreThreshold); scoreLimitCheck = scoreLimit - 10; topScore = 0; runnerUpScore = 0; foreach( team in level.teams ) { score = [[level._getTeamScore]]( team ); if ( score > topScore ) { runnerUpScore = topScore; topScore = score; } else if ( score > runnerUpScore ) { runnerUpScore = score; } } scoreDif = (topScore - runnerUpScore); if( topScore >= scoreLimit*.5) { level notify( "sndMusicHalfway" ); return; } wait(1); } } function onPlayerConnect() { while ( true ) { level waittill( "connected", player ); player.noDrowning = true; if ( isdefined( level.allow_teamchange ) && level.allow_teamchange == "0" ) player.hasDoneCombat = true; if ( !isdefined( player.pers["objtime"] ) ) // prop alive time player.pers["objtime"] = 0; } } function hideHUDIntermission() { level waittill( "game_ended" ); if ( prop::usePropHudServer() ) { level.elim_hud.alpha = 0; if ( level.phSettings.propWhistleTime > 0 ) { level.phWhistleTimer.alpha = 0; level.whistling.alpha = 0; } } foreach( player in level.players ) { player prop_controls::propAbilityKeysVisible( false ); } } function onStartGameType() { setClientNameMode( "manual_change" ); if ( !isdefined( game["switchedsides"] ) ) game["switchedsides"] = false; if ( game["switchedsides"] ) { oldAttackers = game["attackers"]; oldDefenders = game["defenders"]; game["attackers"] = oldDefenders; game["defenders"] = oldAttackers; } level.displayRoundEndText = false; // now that the game objects have been deleted place the influencers spawning::create_map_placed_influencers(); level.spawnMins = ( 0, 0, 0 ); level.spawnMaxs = ( 0, 0, 0 ); util::setObjectiveText( game["attackers"], &"OBJECTIVES_PH_ATTACKER" ); util::setObjectiveText( game["defenders"], &"OBJECTIVES_PH_DEFENDER" ); util::setObjectiveHintText( game["attackers"], &"OBJECTIVES_PH_ATTACKER_HINT" ); util::setObjectiveHintText( game["defenders"], &"OBJECTIVES_PH_DEFENDER_HINT" ); if ( level.splitscreen ) { util::setObjectiveScoreText( game["attackers"], &"OBJECTIVES_PH_ATTACKER" ); util::setObjectiveScoreText( game["defenders"], &"OBJECTIVES_PH_DEFENDER" ); } else { util::setObjectiveScoreText( game["attackers"], &"OBJECTIVES_PH_ATTACKER_SCORE" ); util::setObjectiveScoreText( game["defenders"], &"OBJECTIVES_PH_DEFENDER_SCORE" ); } foreach( team in level.teams ) { spawnlogic::add_spawn_points( team, "mp_tdm_spawn" ); spawnlogic::place_spawn_points( spawning::getTDMStartSpawnName(team) ); } spawning::updateAllSpawnPoints(); //Swap spawns so props start on the other side of the map after halftime (aka if 2 or 3 rounds have been played) swapSpawnSides = (game["roundsplayed"]%4 == 2) || (game["roundsplayed"]%4 == 3); if ( swapSpawnSides ) game["switchedsides"] = !game["switchedsides"]; level.spawn_start = []; foreach( team in level.teams ) { level.spawn_start[ team ] = spawnlogic::get_spawnpoint_array( spawning::getTDMStartSpawnName(team) ); } if ( swapSpawnSides ) game["switchedsides"] = !game["switchedsides"]; level.mapCenter = math::find_box_center( level.spawnMins, level.spawnMaxs ); setMapCenter( level.mapCenter ); spawnpoint = spawnlogic::get_random_intermission_point(); setDemoIntermissionPoint( spawnpoint.origin, spawnpoint.angles ); //removed action loop -CDC level thread onScoreCloseMusic(); if ( !util::isOneRound() ) { level.displayRoundEndText = true; if( level.scoreRoundWinBased ) { globallogic_score::resetTeamScores(); } } if( ( isdefined( level.droppedTagRespawn ) && level.droppedTagRespawn ) ) level.numLives = 1; level._effect[ "propFlash" ] = "explosions/fx_exp_grenade_concussion"; level._effect[ "propDeathFX" ] = "explosions/fx_prop_exp"; if ( !IsDefined( game["propScore"] ) ) { game["propScore"] = []; game["propScore"]["allies"] = 0; game["propScore"]["axis"] = 0; } if ( !IsDefined( game["propSurvivalTime"] ) ) { game["propSurvivalTime"] = []; game["propSurvivalTime"]["allies"] = 0; game["propSurvivalTime"]["axis"] = 0; } if ( !IsDefined( game["hunterKillTime"] ) ) { game["hunterKillTime"] = []; game["hunterKillTime"]["allies"] = 0; game["hunterKillTime"]["axis"] = 0; } level flag::init( "props_hide_over", false ); level thread setupRoundStartHUD(); if ( level.phSettings.propWhistleTime > 0 ) level thread propWhistle(); level thread hideHUDIntermission(); level thread monitorTimers(); level thread setPHTeamScores(); level thread stillAliveXP(); level thread deleteLevelTurrets(); level thread trackTimeAlive(); } function givePrimaryWeapon( weapon ) { self.primaryWeapon = weapon; self GiveWeapon( weapon ); self switchToWeapon( weapon ); self setSpawnWeapon( weapon ); self.spawnWeapon = weapon; self SetBlockWeaponPickup( weapon, true ); } function giveSecondayWeapon( weapon ) { self.secondaryWeapon = weapon; self GiveWeapon( weapon ); self SetBlockWeaponPickup( weapon, true ); } function giveOffhandPrimary( primaryOffhand, primaryOffhandCount ) { self GiveWeapon( primaryOffhand ); self SetWeaponAmmoStock( primaryOffhand, primaryOffhandCount ); self SwitchToOffhand( primaryOffhand ); self.grenadeTypePrimary = primaryOffhand; self.grenadeTypePrimaryCount = primaryOffhandCount; } function giveOffhandSecondary( secondaryOffhand, secondaryOffhandCount ) { self GiveWeapon( secondaryOffhand ); self SetWeaponAmmoClip( secondaryOffhand, secondaryOffhandCount ); self SwitchToOffhand( secondaryOffhand ); self.grenadeTypeSecondary = secondaryOffhand; self.grenadeTypeSecondaryCount = secondaryOffhandCount; } function givePerk( perkName ) { if ( !self HasPerk( perkName ) ) { self SetPerk( perkName ); } } function giveCustomLoadout() { loadout::giveLoadout_init( true ); loadout::setClassNum( self.curClass ); self ClearPerks(); weapon = undefined; if ( self util::isProp() ) { weapon = GetWeapon( "pistol_standard" ); self givePrimaryWeapon( weapon ); self giveOffhandPrimary( GetWeapon( "null_offhand_primary" ), 0 ); self giveOffhandSecondary( GetWeapon( "null_offhand_secondary" ), 0 ); self givePerk( "specialty_quieter" ); self givePerk( "specialty_fastladderclimb" ); self givePerk( "specialty_fastmantle" ); self givePerk( "specialty_jetcharger" ); } else { weapon = GetWeapon( "pistol_standard" ); self giveSecondayWeapon( weapon ); attachments = Array(); attachments[attachments.size] = "extclip"; attachments[attachments.size] = "steadyaim"; weapon = GetWeapon( "smg_standard", attachments ); self givePrimaryWeapon( weapon ); if ( !propMiniGameActive() ) self giveOffhandPrimary( GetWeapon( "concussion_grenade" ), 2 ); else self giveOffhandPrimary( GetWeapon( "null_offhand_primary" ), 0 ); self giveOffhandSecondary( GetWeapon( "null_offhand_secondary" ), 0 ); self attackerInitAmmo(); self givePerk( "specialty_twogrenades" ); self givePerk( "specialty_fastladderclimb" ); self givePerk( "specialty_fastmantle" ); self givePerk( "specialty_longersprint" ); self givePerk( "specialty_sprintfire" ); self givePerk( "specialty_jetcharger" ); } self GiveWeapon( level.weaponBaseMelee ); self notify( "applyLoadout" ); return weapon; } function is_player_gamepad_enabled() { return self GamepadUsedLast(); } function whistleStartTimer( duration ) { level notify( "whistle_start_timer_beginning" ); countTime = int( duration ); if ( countTime >= 0 ) { thread whistleStartTimer_Internal( countTime ); } } function whistleStartTimer_Internal( countTime ) { level endon( "whistle_start_timer_beginning" ); // stop any existing whistle timer threads waittillframeend; // ensure we don't start in the middle of a frame while( ( countTime > 0 ) && !level.gameEnded ) { countTime--; wait( 1 ); } } function usePropHudServer() { /# if ( GetDvarInt( "scr_ph_usePropHudServer", 0 ) != 0 ) return true; #/ return true; } function setupRoundStartHUD() { //Initial Countdown Timer level.phCountdownTimer = hud::createServerTimer( "default", 1.5 ); level.phCountdownTimer hud::setPoint( "CENTER", undefined, 0, 50 ); level.phCountdownTimer.label = &"MP_PH_STARTS_IN"; level.phCountdownTimer.alpha = 0; level.phCountdownTimer.archived = false; level.phCountdownTimer.hideWhenInMenu = true; level.phCountdownTimer.sort = 1; if ( usePropHudServer() ) { yUpperLeft = 110; ySpace = 20; if ( !level.console ) { yUpperLeft = 125; ySpace = 15; } //Props Alive Counter level.elim_hud = hud::createServerFontString( "default", 1.5 ); level.elim_hud.label = &"MP_PH_ALIVE"; level.elim_hud SetValue( 0 ); level.elim_hud.x = 5; level.elim_hud.y = yUpperLeft; level.elim_hud.alignX = "left"; level.elim_hud.alignY = "top"; level.elim_hud.horzAlign = "left"; level.elim_hud.vertAlign = "top"; level.elim_hud.archived = true; level.elim_hud.alpha = 0; level.elim_hud.glowAlpha = 0; level.elim_hud.hidewheninmenu = false; level thread eliminatedHUDMonitor(); if ( level.phSettings.propWhistleTime > 0 ) { //Whistle Timer level.phWhistleTimer = hud::createServerTimer( "default", 1.5 ); level.phWhistleTimer.x = 5; level.phWhistleTimer.y = yUpperLeft + ySpace; level.phWhistleTimer.alignX = "left"; level.phWhistleTimer.alignY = "top"; level.phWhistleTimer.horzAlign = "left"; level.phWhistleTimer.vertAlign = "top"; level.phWhistleTimer.label = &"MP_PH_WHISTLE_IN"; level.phWhistleTimer.alpha = 0; level.phWhistleTimer.archived = true; level.phWhistleTimer.hideWhenInMenu = false; level.phWhistleTimer setTimer( 120 ); //Whistling level.whistling = hud::createServerFontString( "default", 1.5 ); level.whistling.label = &"MP_PH_WHISTLING"; level.whistling.x = 5; level.whistling.y = yUpperLeft + ySpace; level.whistling.alignX = "left"; level.whistling.alignY = "top"; level.whistling.horzAlign = "left"; level.whistling.vertAlign = "top"; level.whistling.archived = true; level.whistling.alpha = 0; level.whistling.glowAlpha = 0.2; level.whistling.hidewheninmenu = false; } } } function eliminatedHUDMonitor() { level endon ( "game_ended" ); while(1) { props = get_alive_nonspecating_players( game["defenders"] ); level.elim_hud SetValue( props.size ); level util::waittill_any( "player_spawned", "player_killed", "player_eliminated", "playerCountChanged", "propCountChanged", "playerDisconnected" ); } } function get_alive_nonspecating_players( team ) { alive_nonspec_players = []; foreach ( player in level.players ) { if ( IsDefined( player ) && isalive( player ) && (!IsDefined( player.sessionstate ) || player.sessionstate == "playing" ) ) { if ( !IsDefined( team ) || player.team == team ) alive_nonspec_players[alive_nonspec_players.size] = player; } } return alive_nonspec_players; } function onPlayerDisconnect() { level notify( "playerDisconnected" ); if ( propMiniGameActive() ) thread propMiniGameUpdateScoreboard( 0.05 ); } function waittillPrematchDone() { while ( !( isdefined( level.prematch_over ) && level.prematch_over ) ) {wait(.05);}; } function onSpawnPlayer(predictedSpawn) { self.breathingStopTime = 0; if ( self util::isProp() ) { self.pingsLeft = undefined; if ( !isdefined( self.abilityLeft ) ) self.abilityLeft = 0; if ( !isdefined( self.clonesLeft ) ) self.clonesLeft = 0; //remember ability selection between rounds if( !isDefined( self.pers["ability"] ) ) self.pers["ability"] = 0; self.currentAbility = level.abilities[ self.pers["ability"] ]; if ( usePropHudServer() ) self thread prop_controls::propControlsHUD(); self.isAngleOffset = false; changeCount = Int( level.phSettings.propChangeCount ); abilityCount = undefined; cloneCount = undefined; if ( isdefined( self.spawnedOnce ) && isdefined( self.changesLeft ) ) { changeCount = self.changesLeft; abilityCount = self.abilityLeft; cloneCount = self.clonesLeft; } self prop_controls::propSetChangesLeft( changeCount ); self prop_controls::setNewAbilityCount( self.currentAbility, abilityCount ); self prop_controls::setNewAbilityCount( "CLONE", cloneCount ); self thread prop_controls::cleanupPropControlsHUDOnDeath(); self thread handleProp(); } else { self.abilityLeft = undefined; self.clonesLeft = undefined; if ( !isdefined( self.pingsLeft ) ) self.pingsLeft = 0; if ( !IsDefined( self.thrownSpecialCount ) ) self.thrownSpecialCount = 0; if ( usePropHudServer() ) self thread prop_controls::hunterControlsHUD(); pingsLeft = level.phSettings.hunterNumPings; if ( isdefined( self.spawnedOnce ) && isdefined( self.pingsLeft ) ) { pingsLeft = self.pingsLeft; } self prop_controls::hunterSetPingsLeft( pingsLeft ); self thread prop_controls::cleanupHunterControlsHUDOnDeath(); self thread handleNonProp(); } self thread attackersWaitTime(); if ( level.useStartSpawns && !level.inGracePeriod && !level.playerQueuedRespawn ) { level.useStartSpawns = false; } self.spawnedOnce = true; spawning::onSpawnPlayer( predictedSpawn ); } function monitorTimers() { level endon( "game_ended" ); waittillPrematchDone(); level.allow_teamchange = "0"; foreach ( player in level.players ) player.hasDoneCombat = true; level thread pausePHTimerForMigration(); if ( level.phSettings.propHideTime > 0 ) { level.phCountdownTimer setTimer( level.phSettings.propHideTime ); level.phCountdownTimer.alpha = 1; } if ( usePropHudServer() && level.phSettings.propWhistleTime > 0 ) { level.phWhistleTimer setTimer( level.phsettings.propWhistleTime + level.phSettings.propHideTime ); } if ( level.phSettings.propHideTime > 0 || level.phSettings.propWhistleTime > 0 ) whistleStartTimer( level.phsettings.propWhistleTime + level.phSettings.propHideTime ); if ( level.phSettings.propHideTime > 0 ) waittillCountdownComplete( level.phSettings.propHideTime ); level flag::set( "props_hide_over" ); if ( usePropHudServer() ) { if ( level.phSettings.propWhistleTime > 0 ) level.phWhistleTimer.alpha = 1; level.elim_hud.alpha = 1; } level.phCountdownTimer.alpha = 0; } function pausePHTimerForMigration() { level endon( "game_ended" ); level endon( "props_hide_over" ); countdownEndTime = int( ( level.phSettings.propHideTime ) + ( GetTime() / 1000 ) ); totalTimePassed = 0; while( 1 ) { level waittill( "host_migration_begin" ); level.phCountdownTimer.alpha = 0; if ( usePropHudServer() && level.phSettings.propWhistleTime > 0 ) level.phWhistleTimer.alpha = 0; timePassed = int( hostmigration::waitTillHostMigrationDone() / 1000 ); totalTimePassed += timePassed; timePassed = totalTimePassed; intendedEnd = countdownEndTime + timePassed - int( gettime() / 1000 ); level.phCountdownTimer setTimer( intendedEnd ); if ( usePropHudServer() && level.phSettings.propWhistleTime > 0 ) level.phWhistleTimer setTimer( level.phsettings.propWhistleTime + intendedEnd ); whistleStartTimer( level.phsettings.propWhistleTime + intendedEnd ); level.phCountdownTimer.alpha = 1; if ( usePropHudServer() && level.phSettings.propWhistleTime > 0 ) level.phWhistleTimer.alpha = 1; } } function handleProp() { level endon( "game_ended" ); self endon( "disconnect" ); self endon( "death" ); self waittill( "applyLoadout" ); self AllowProne( false ); self AllowCrouch( false ); self AllowSprint( false ); self AllowSlide( false ); self SetMoveSpeedScale( level.phSettings.propSpeedScale ); self PlayerKnockback( false ); self TakeAllWeapons(); self AllowSpectateTeam( game["attackers"], true ); self Ghost(); self SetClientUIVisibilityFlag( "weapon_hud_visible", 0 ); self.healthRegenDisabled = true; self.concussionImmune = undefined; Assert( !IsDefined( self.prop ) ); self thread setupProp(); self thread prop_controls::setupKeyBindings(); self thread setupDamage(); self thread prop_controls::propInputWatch(); self thread propWatchDeath(); self thread propWatchCleanupOnDisconnect(); self thread propWatchCleanupOnRoundEnd(); self thread propWatchPreMatchSettings(); } function getThirdPersonRangeForSize( propSize ) { switch ( propSize ) { case 50: return 120; case 100: return 150; case 250: return 180; case 450: return 260; case 550: return 320; default: AssertMsg( "Unknown size: " + propSize ); break; } return 120; } function getThirdPersonHeightOffsetForSize( propSize ) { switch ( propSize ) { case 50: return -30; case 100: return -20; case 250: return 0; case 450: return 20; case 550: return 40; default: AssertMsg( "Unknown size: " + propSize ); break; } return 0; } function applyXYZOffset() { if( !isDefined( self.prop.xyzOffset ) ) return; self.prop.angles = self.angles; forward = AnglesToForward( self.prop.angles ) * self.prop.xyzOffset[0]; right = AnglesToRight( self.prop.angles ) * self.prop.xyzOffset[1]; up = AnglesToUp( self.prop.angles ) * self.prop.xyzOffset[2]; self.prop.origin += forward; self.prop.origin += right; self.prop.origin += up; } function applyAnglesOffset() { if( !isDefined( self.prop.anglesOffset ) ) return; self.prop.angles = self.angles; self.prop.angles += self.prop.anglesOffset; self.isAngleOffset = true; } function propWhistle() { level endon( "game_ended" ); waittillPrematchDone(); time = GetTime(); whistleTime = level.phSettings.propWhistleTime*1000; whistleTimeMinTime = 20000; whistleTimeFinal = whistleTimeMinTime; whistleTimeDifference = 500; // start the whistling a bit earlier so that it matches with how we want to show the UI //time in miliseconds when the game will not allow whistles endGameGracePeriod = 5000; whistleCount = 0; whistleSortOrigin = ( 0, 0, 0 ); minimapCorners = getentarray("minimap_corner", "targetname"); if ( minimapCorners.size > 0 ) whistleSortOrigin = minimapCorners[0].origin; hostmigration::waitLongDurationWithHostMigrationPause( level.phSettings.propHideTime+level.phSettings.propWhistleTime ); while( 1 ) { if( ( time + whistleTime - whistleTimeDifference ) < GetTime() ) { whistleCount++; if ( usePropHudServer() ) { level.phWhistleTimer.alpha = 0; level.whistling.alpha = 1; level.whistling fadeOverTime( 0.75 ); level.whistling.alpha = .6; } sortedPlayers = ArraySortClosest(level.players, whistleSortOrigin); foreach( player in sortedPlayers ) { if( !IsDefined( player ) ) continue; if ( player util::isProp() && isAlive( player ) ) { PlaySoundAtPosition( "mpl_phunt_char_whistle", player.origin + ( 0, 0, 60 ) ); //due to this wait each player takes 1.5 seconds to whistle hostmigration::waitLongDurationWithHostMigrationPause( 1.5 ); } } time = GetTime(); if( whistleCount % 2 == 0 ) whistleTime = max( whistleTime - 5000, whistleTimeMinTime ); if( ( WhistleTimeFinal ) >= ( globallogic_utils::getTimeRemaining() - endGameGracePeriod ) ) { if ( usePropHudServer() ) level.whistling.alpha = 0; return; } else { if( ( WhistleTimeFinal * 2 ) + ( getTeamPlayersAlive( game["defenders"] ) * 2500 ) >= ( globallogic_utils::getTimeRemaining() - endGameGracePeriod ) ) { if ( usePropHudServer() ) level.phWhistleTimer.label = &"MP_PH_FINAL_WHISTLE"; whistleTimeFinal += getTeamPlayersAlive( game["defenders"] ) * 2500; } if ( usePropHudServer() ) level.phWhistleTimer setTimer( int( whistleTime/1000 ) ); whistleStartTimer( int( whistleTime/1000 ) ); if ( usePropHudServer() ) { level.whistling.alpha = 0; level.phWhistleTimer.alpha = 1; } } } hostmigration::waitLongDurationWithHostMigrationPause( 0.5 ); } } function getLivingPlayersOnTeam( team ) { players = []; foreach( player in level.players ) { if ( !IsDefined( player.team ) ) continue; if ( IsAlive(player) && player.team == team ) { players[players.size] = player; } } return players; } function setupDamage() { level endon( "game_ended" ); self endon( "death" ); self endon( "disconnect" ); hostmigration::waitLongDurationWithHostMigrationPause( 0.5 ); self.prop.health = 99999; self.prop.maxhealth = 99999; self.prop thread entityDamageWatcher( &damageWatch ); } function entityDamageWatcher( damageCallback ) { level endon( "game_ended" ); self endon( "death" ); while ( true ) { self waittill ( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, weapon, iDFlags ); self thread [[ damageCallback ]]( damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, weapon, iDFlags ); } } function damageWatch( damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, weapon, iDFlags ) { if ( !IsDefined( attacker ) ) return; if ( !IsDefined( self.owner ) ) return; if ( isPlayer( attacker ) ) { if ( attacker.pers["team"] == self.owner.pers["team"] ) return; attacker thread damagefeedback::update(); if ( isdefined( weapon ) && weapon.rootWeapon.name == "concussion_grenade" && isdefined( meansOfDeath ) && meansOfDeath != "MOD_IMPACT" ) prop_controls::registerConcussionEvent( attacker, undefined, meansOfDeath, damage, point, weapon ); } self.owner DoDamage( damage, point, attacker, attacker, "none", meansOfDeath, iDFlags, weapon ); self.health = 99999; self.maxhealth = 99999; } function propCleanup() // self == player { array = Array( self.prop, self.propAnchor, self.propEnt ); self thread propCleanupDelayed( array ); } function propCleanupDelayed( propEnts ) { foreach( prop in propEnts ) { if ( IsDefined( prop ) ) prop Unlink(); } {wait(.05);}; foreach( prop in propEnts ) { if ( IsDefined( prop ) ) prop Delete(); } } function propWatchDeath() // self == player { level endon( "game_ended" ); self endon("disconnect"); self waittill( "death" ); corpse = self.body; PlaySoundAtPosition( "wpn_flash_grenade_explode", self.prop.origin + ( 0, 0, 4 ) ); PlayFX( fx::get( "propDeathFX" ), ( self.prop.origin + ( 0, 0, 4 ) ) ); if ( IsDefined( corpse ) ) corpse Delete(); self propCleanup(); } function propWatchCleanupOnDisconnect() // self == player { self notify( "propWatchCleanupOnDisconnect" ); self endon( "propWatchCleanupOnDisconnect" ); level endon( "game_ended" ); self waittill( "disconnect" ); self propCleanup(); self propCloneCleanup(); } function propWatchCleanupOnRoundEnd() // self == player { self notify( "propWatchDeleteRoundEnd" ); self endon( "propWatchDeleteRoundEnd" ); self endon( "disconnect" ); level waittill( "round_end_done" ); self propCleanup(); self propCloneCleanup(); } function propCloneCleanup() { if ( IsDefined( self.propClones ) ) { foreach( clone in self.propClones ) { if ( isdefined( clone ) ) clone Delete(); } } } function propWatchPreMatchSettings() { self endon( "death" ); self endon( "disconnect" ); self endon( "joined_team" ); self endon( "joined_spectators" ); waittillPrematchDone(); // Any other settings that can be overwritten by the prematch freeze controls self AllowProne( false ); self AllowCrouch( false ); self AllowSprint( false ); } function organizePropList(inArray) { return array::randomize(inArray); } function randGetPropSizeToAllocate() { WEIGHT_XSMALL = 10 * IsDefined(level.propList[50]); WEIGHT_SMALL = 30 * IsDefined(level.propList[100]); WEIGHT_MEDIUM = 40 * IsDefined(level.propList[250]); WEIGHT_LARGE = 20 * IsDefined(level.propList[450]); WEIGHT_XLARGE = 10 * IsDefined(level.propList[550]); randomRange = WEIGHT_XSMALL + WEIGHT_SMALL + WEIGHT_MEDIUM + WEIGHT_LARGE + WEIGHT_XLARGE; randomVal = RandomInt(randomRange); if ( randomVal < WEIGHT_XSMALL ) return 50; randomVal -= WEIGHT_XSMALL; if ( randomVal < WEIGHT_SMALL ) return 100; randomVal -= WEIGHT_SMALL; if ( randomVal < WEIGHT_MEDIUM ) return 250; randomVal -= WEIGHT_MEDIUM; if ( randomVal < WEIGHT_LARGE ) return 450; randomVal -= WEIGHT_LARGE; return 550; } function getNextProp(inPlayer) { firstChoicePropSize = randGetPropSizeToAllocate(); allPropSizes = GetArrayKeys(level.propList); allPropSizes = array::randomize(allPropSizes); propSizes = Array( firstChoicePropSize ); foreach(size in allPropSizes) { if( size != firstChoicePropSize ) propSizes[propSizes.size] = size; } prop = undefined; for(i=0; i= time ) break; } return; } function giveLastOnTeamWarning() { self endon("death"); self endon("disconnect"); level endon( "game_ended" ); self waitTillRecoveredHealth( 3 ); level thread playerCardSplash( &"SCORE_LAST_ALIVE", self ); if ( self util::isProp() ) { level notify ( "noPropsToSpectate" ); level.noPropsSpectate = true; } level notify ( "last_alive", self ); } function playerCardSplash( calloutMessage, calloutPlayer ) { LUINotifyEvent( &"player_callout", 2, calloutMessage, calloutPlayer.entnum ); } function onTimeLimit() { if ( !( isdefined( level.gameEnding ) && level.gameEnding ) ) { storeTimePassed(); chooseFinalKillCam(); ph_endGame( game["defenders"], game["strings"]["time_limit_reached"] ); } } function storeTimePassed() { timeLimitInMilliseconds = ( globallogic_defaults::default_getTimeLimit() * 60 * 1000 ); timePassed = globallogic_utils::getTimePassed(); timePassedClamped = Int( min( timeLimitInMilliseconds, timePassed ) ); game["propSurvivalTime"][ game["defenders"] ] += timePassedClamped; game["hunterKillTime"][ game["attackers"] ] += timePassedClamped; } function chooseFinalKillCam() { livingProps = getLivingPlayersOnTeam( game["defenders"] ); if ( livingProps.size < 1 ) { return; } livingHunters = getLivingPlayersOnTeam( game["attackers"] ); if ( livingHunters.size < 1 ) { return; } killCamProp = chooseBestPropForKillcam( livingProps, livingHunters ); if ( IsPlayer( killCamProp ) ) { attackerNum = killCamProp getEntityNumber(); } else { attackerNum = -1; } victim = livingHunters[0]; victim.deathtime = getTime() - 1000; weap = GetWeapon( "none" ); killcam_entity_info = killcam::get_killcam_entity_info( killCamProp, killCamProp, weap ); level thread killcam::record_settings( attackerNum, victim getEntityNumber(), weap, "MOD_UNKNOWN", victim.deathtime, 0, 0, killcam_entity_info, [], [], killCamProp ); } function chooseBestPropForKillcam( livingProps, livingHunters ) { bestProp = undefined; shortestPathDistOverall = ( 1 << 30 ); foreach ( prop in livingProps ) { Assert( isAlive( prop ) ); closestHunter = undefined; shortestPathDist = ( 1 << 30 ); foreach ( hunter in livingHunters ) { pathDist = PathDistance( prop.origin, hunter.origin ); if ( !isdefined( pathDist ) ) pathDist = Distance( prop.origin, hunter.origin ); if ( pathDist < shortestPathDist ) { shortestPathDist = pathDist; closestHunter = hunter; } } if ( shortestPathDist < shortestPathDistOverall ) { shortestPathDistOverall = shortestPathDist; bestProp = prop; } } if ( !IsDefined( bestProp ) ) { bestProp = array::random( livingProps ); } return bestProp; } function showPlayerToAll( setClientField ) { self Show(); self notify( "showPlayer" ); if ( setClientField ) self clientfield::set( "hideTeamPlayer", 0 ); } function hidePlayerFromTeam( team, setClientField ) { self Hide(); // turns off the rest: footstep/landing fx, gun tracers/whizbys, etc if ( setClientField ) self thread hidePlayerClientfield( team ); foreach ( player in level.players ) self thread hidePlayerFromTeamOnPlayerSpawned( player, team ); self thread hidePlayerFromTeamOnPlayerConnect( team ); } function hidePlayerClientfield( team ) { level endon( "game_ended" ); self endon( "disconnect" ); self endon( "showPlayer" ); {wait(.05);}; // need to give time for the player entity to be created before setting clientfields teamInt = 1; if ( team == "axis" ) teamInt = 2; self clientfield::set( "hideTeamPlayer", teamInt ); // makes the player invisible } function hidePlayerFromTeamOnPlayerConnect( team ) { level endon( "game_ended" ); self endon( "disconnect" ); self endon( "showPlayer" ); while ( true ) { level waittill( "connected", player ); self thread hidePlayerFromTeamOnPlayerSpawned( player, team ); } } function hidePlayerFromTeamOnPlayerSpawned( player, team ) { level endon( "game_ended" ); self endon( "disconnect" ); self endon( "showPlayer" ); player endon( "disconnect" ); while ( true ) { if ( ( isdefined( player.hasSpawned ) && player.hasSpawned ) && player.team != team ) { self ShowToPlayer( player ); // ShowToPlayer turns off the EF_NODRAW flag; turn it back on since props should never render the player body if ( self util::IsProp() ) self Ghost(); } player waittill( "spawned" ); } } function handleNonProp() { self.thirdPersonRange = undefined; self SetClientThirdPerson( false, 0 ); self AllowProne( true ); self AllowSprint( true ); self SetMoveSpeedScale( 1 ); self PlayerKnockback( true ); self Show(); self SetClientUIVisibilityFlag( "weapon_hud_visible", 1 ); if ( propMiniGameActive() ) self propMiniGameSetHunterVisibility( false ); self thread prop_controls::setupHunterKeyBindings(); self thread prop_controls::hunterInputWatch(); self.concussionImmune = true; self.healthRegenDisabled = false; self thread attackerRegenAmmo(); } function stillAliveXP() { level endon( "game_ended" ); level.xpEventInfo["kill"]["value"] = 300; level waittill( "props_hide_over" ); while(1) { hostmigration::waitLongDurationWithHostMigrationPause( 10 ); /# if ( GetGametypeSetting( "timelimit" ) == 0 ) continue; #/ foreach ( player in level.players ) { if ( !IsDefined(player.team) ) continue; if ( player.team == game[ "attackers" ] ) continue; if ( !isAlive( player ) ) continue; if ( !isdefined( player.prop ) ) continue; scoreevents::processScoreEvent( "still_alive", player ); switch( player.prop.info.propSize ) { case 250: { scoreevents::processScoreEvent( "still_alive_medium_bonus", player ); break; } case 450: { scoreevents::processScoreEvent( "still_alive_large_bonus", player ); break; } case 550: { scoreevents::processScoreEvent( "still_alive_extra_large_bonus", player ); break; } default: break; } } } } function trackTimeAlive() { level endon( "game_ended" ); waittillPrematchDone(); while(1) { foreach ( player in level.players ) { if ( !IsDefined(player.team) ) continue; if ( player.team == game[ "attackers" ] ) continue; if ( !IsAlive( player ) ) continue; player.pers["objtime"]++; // prop alive time on scoreboard player.objtime = player.pers["objtime"]; player AddPlayerStatWithGameType( "OBJECTIVE_TIME", 1 ); } hostmigration::waitLongDurationWithHostMigrationPause( 1 ); } } function gamemodeModifyPlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, boneIndex ) { victim = self; if ( IsDefined( eAttacker ) && IsPlayer( eAttacker ) && isAlive( eAttacker ) ) { if( !isDefined( eAttacker.hasHitPlayer ) ) eAttacker.hasHitPlayer = true; } return iDamage; } function playerAvoidDamageGameMode( iDFlags, sHitLoc, weapon, friendlyFire, attackerIsHittingSelf, sMeansOfDeath ) { if ( IsDefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_FALLING" ) { return true; } if ( self isHunter() ) { if ( weapon.name == "concussion_grenade" ) { return true; } if ( IsSubStr( weapon.name, "destructible" ) ) { return true; } } return false; } function attackersWaitTime() { level endon( "game_ended" ); self endon( "disconnect" ); waittillPrematchDone(); if ( self.team == game[ "defenders" ] ) { self notify( "cancelCountdown" ); return; } while ( !IsDefined( level.startTime ) ) {wait(.05);}; while ( IsDefined( self.controlsFrozen ) && self.controlsFrozen ) {wait(.05);}; gameSecondsElapsed = getGameSecondsElapsed(); remainingTime = level.phSettings.propHideTime - gameSecondsElapsed; result = false; if ( remainingTime > 0 ) { if ( !propMiniGameActive() ) result = attackersWaitTimeNormal( gameSecondsElapsed, remainingTime ); else result = attackersWaitTimeMinigame( gameSecondsElapsed, remainingTime ); } } function attackersWaitTimeNormal( gameSecondsElapsed, remainingTime ) { self freezeControls( true ); if ( Int( gameSecondsElapsed ) > 0 ) fadeInTime = 0.0; else fadeInTime = 1.0; fadeOutTime = 1.0; if ( fadeInTime + fadeOutTime > remainingTime ) { fadeInTime = 0.0; fadeOutTime = 0.0; } self thread prop_controls::countdownFadeToBlackForXSec( remainingTime, fadeInTime, fadeOutTime ); result = self waittillCountdownComplete( remainingTime ); self freezeControls( false ); return result; } function attackersWaitTimeMinigame( gameSecondsElapsed, remainingTime ) { CONST_MINIGAME_TIME_TO_END_SEC = 3; CONST_MINIGAME_FADE_TIME = 1; fadeInTime = 0.0; result = false; timeToMinigame = remainingTime - CONST_MINIGAME_TIME_TO_END_SEC; self thread propWaitMiniGameInit( timeToMinigame + CONST_MINIGAME_FADE_TIME ); waittillframeend; // make sure spawnPlayer() runs since it will unfreeze controls and make sure the player is not spectating before grabbing the origin self.phSpawnOrigin = undefined; self.phSpawnAngles = undefined; if ( remainingTime > 8 ) { self.phSpawnOrigin = self.origin; self.phSpawnAngles = self.angles; result = self waittillCountdownComplete( timeToMinigame ); fadeInTime = 1.0; } gameSecondsElapsed = getGameSecondsElapsed(); remainingTime = level.phSettings.propHideTime - gameSecondsElapsed; if ( remainingTime > 0 ) { self freezeControls( true ); fadeOutTime = 1.0; if ( fadeInTime + fadeOutTime > remainingTime ) { fadeInTime = 0.0; fadeOutTime = 0.0; } self thread prop_controls::countdownFadeToBlackForXSec( remainingTime, fadeInTime, fadeOutTime ); result = self waittillCountdownComplete( remainingTime ); self freezeControls( false ); } return result; } function getGameSecondsElapsed() { return ( ( GetTime() - level.startTime - level.totalHostMigrationTime ) / 1000.0 ); } function monitorHostmigrationTime() { level.totalHostMigrationTime = 0; while ( true ) { level waittill( "host_migration_begin" ); startTime = GetTime(); level waittill( "host_migration_end" ); passedTime = GetTime() - startTime; level.totalHostMigrationTime += passedTime; } } function waittillCountdownComplete( remainingTime ) { result = waittillCountdownCompleteInternal( remainingTime ); /# while ( GetDvarInt( "scr_ph_pauseCountdown", 0 ) != 0 ) {wait(.05);}; #/ return result; } function waittillCountdownCompleteInternal( remainingTime ) { self endon( "cancelCountdown" ); hostmigration::waitLongDurationWithHostMigrationPause( remainingTime ); return true; } function prematchPeriodSpawn() { if ( self.pers["team"] == game["attackers"] ) self util::freeze_player_controls( true ); else self thread propPrematchLock(); team = self.pers["team"]; if( isdefined( self.pers["music"].spawn ) && self.pers["music"].spawn == false ) { if (level.wagerMatch) { music = "SPAWN_WAGER"; } else { music = game["music"]["spawn_" + team]; } if( game[ "roundsplayed" ] == 0 ) { self thread globallogic_spawn::sndDelayedMusicStart("spawnFull"); } else { self thread globallogic_spawn::sndDelayedMusicStart("spawnShort"); } self.pers["music"].spawn = true; } if ( level.splitscreen ) { if ( isdefined( level.playedStartingMusic ) ) music = undefined; else level.playedStartingMusic = true; } self thread globallogic_spawn::doInitialSpawnMessaging(); } function propPrematchLock() { self endon( "disconnect" ); self FreezeControlsAllowLook( true ); waittillPrematchDone(); self FreezeControlsAllowLook( false ); } function attackerInitAmmo() { primaryWeapons = self GetWeaponsListPrimaries(); foreach ( weapon in primaryWeapons ) { self GiveMaxAmmo( weapon ); self SetWeaponAmmoClip( weapon, 999 ); } if ( !propMiniGameActive() ) { if ( !IsDefined( self.thrownSpecialCount ) ) self.thrownSpecialCount = 0; // Handle tactical grenades that may have already been used weapon = GetWeapon( "concussion_grenade" ); tacticalCount = self GetWeaponAmmoStock( weapon ); tacticalCount = tacticalCount - self.thrownSpecialCount; tacticalCount = Int( max( tacticalCount, 0 ) ); self SetWeaponAmmoStock( weapon, tacticalCount ); if ( tacticalCount > 0 ) self thread prop_controls::watchSpecialGrenadeThrow(); } } function attackerRegenAmmo() { self endon( "death" ); self endon( "disconnect" ); self notify( "attackerRegenAmmo" ); self endon( "attackerRegenAmmo" ); level endon( "game_ended" ); while ( true ) { self waittill( "reload" ); primaryWeapon = self GetCurrentWeapon(); self GiveMaxAmmo( primaryWeapon ); } } function checkKillRespawn() { self endon( "disconnect" ); level endon( "game_ended" ); hostmigration::waitLongDurationWithHostMigrationPause( 0.1 ); if( self.pers["lives"] == 1 ) { self.pers["lives"]--; level.livesCount[self.team]--; globallogic::updateGameEvents(); level notify( "propCountChanged" ); return; } } function gameModeAssistedSuicide( attacker, sMeansOfDeath, weapon ) { bestPlayer = undefined; bestPlayerMeansOfDeath = undefined; bestPlayerWeapon = undefined; if ( !level flag::get( "props_hide_over" ) ) return; if ( ( !isdefined( attacker ) || attacker.classname == "trigger_hurt" || attacker.classname == "worldspawn" || ( isdefined( attacker.isMagicBullet ) && attacker.isMagicBullet == true ) || attacker == self ) ) { for ( i = 0; i < self.attackers.size; i++ ) { player = self.attackers[i]; if ( !isdefined( player ) ) continue; if ( !isdefined( self.attackerDamage[ player.clientId ] ) || ! isdefined( self.attackerDamage[ player.clientId ].damage ) ) continue; if ( player == self || ( level.teamBased && player.team == self.team ) ) continue; if ( self.attackerDamage[ player.clientId ].damage > 1 && !isdefined( bestPlayer ) ) { bestPlayer = player; bestPlayerMeansOfDeath = self.attackerDamage[ player.clientId ].meansOfDeath; bestPlayerWeapon = self.attackerDamage[ player.clientId ].weapon; } else if ( isdefined( bestPlayer ) && self.attackerDamage[ player.clientId ].lasttimedamaged > self.attackerDamage[ bestPlayer.clientId ].lasttimedamaged ) { bestPlayer = player; bestPlayerMeansOfDeath = self.attackerDamage[ player.clientId ].meansOfDeath; bestPlayerWeapon = self.attackerDamage[ player.clientId ].weapon; } } if ( !isdefined( bestPlayer ) && self util::IsProp() ) { bestDistSq = undefined; foreach ( player in level.players ) { if ( IsAlive( player ) && player.team != self.team ) { distSq = DistanceSquared( player.origin, self.origin ); if ( !isdefined( bestDistSq ) || distSq < bestDistSq ) { bestPlayer = player; bestDistSq = distSq; } } } if ( isdefined( bestPlayer ) ) { bestPlayerMeansOfDeath = "MOD_MELEE"; bestPlayerWeapon = GetWeapon( "none" ); } } } result = undefined; if ( isdefined( bestPlayer ) ) { result = []; result["bestPlayer"] = bestPlayer; result["bestPlayerWeapon"] = bestPlayerWeapon; result["bestMeansOfDeath"] = bestPlayerMeansOfDeath; } return result; } function onPlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, lifeId ) { victim = self; killedByEnemy = false; level notify( "playerCountChanged" ); if ( victim.team == game[ "attackers" ] ) { self thread respawnPlayer(); } else { if ( !level flag::get( "props_hide_over" ) ) { self thread respawnPlayer(); return; } else { thread deathicons::add( victim.body, victim, victim.team, 5.0 ); } } if ( ( IsDefined( attacker ) && IsPlayer( attacker ) && attacker != victim && victim.team != attacker.team ) ) { killedByEnemy = true; } if ( killedByEnemy ) { scoreevents::processScoreEvent( "prop_finalblow", attacker, victim ); // Play local sound for players who did damage foreach( assailant in victim.attackers ) { if ( assailant == attacker ) { assailant PlayHitMarker( "mpl_hit_alert" ); } else { assailant PlayHitMarker( "mpl_hit_alert_escort" ); } } } foreach ( player in level.players ) { if ( player != attacker && player util::isProp() && isalive( player ) && victim util::isProp() ) { scoreevents::processScoreEvent( "prop_survived", player ); } else if ( player != attacker && player isHunter() && victim.team == game["defenders"] ) { scoreevents::processScoreEvent( "prop_killed", player, victim ); } } if ( victim util::isProp() ) { attackerTeam = util::getOtherTeam( victim.team ); game["propScore"][attackerTeam] += 1; } } function respawnPlayer() { self thread waitTillCanSpawnClient(); } // Adding the same fix below, that I did to Reinforce, to make sure this function ends correctly // instead of continously running while a person is stuck in an incrementing wait loop, due to spawning in the same frame as another player function waitTillCanSpawnClient() { self endon ( "started_spawnPlayer" ); self endon ( "disconnect" ); level endon ( "game_ended" ); for (;;) { wait ( .05 ); if ( isDefined( self ) && isdefined( self.curClass ) && ( self.sessionstate == "spectator" || !isAlive( self ) ) ) { self.pers["lives"] = 1; self globallogic_spawn::spawnClient(); //we need to continue here because spawn client can fail for up to 3 server frames in this instance continue; } //player either disconnected or has spawned return; } } function onDeadEvent( team ) { if ( team == game[ "defenders" ] ) { /# if ( isdefined( level.allow_teamchange ) && level.allow_teamchange == "1" ) return; #/ level thread propKilledEnd(); } } function propKilledEnd() { if ( ( isdefined( level.huntersWonEnding ) && level.huntersWonEnding ) ) return; if ( ( isdefined( level.gameEnding ) && level.gameEnding ) ) return; level.huntersWonEnding = true; storeTimePassed(); level.gameEnding = 1; hostmigration::waitLongDurationWithHostMigrationPause( 3 ); thread ph_endGame( game["attackers"], game["strings"][game["defenders"]+"_eliminated"] ); } function playDamageSoundPH( sMeansOfDeath ) { if ( self.team == game["attackers"] ) self battlechatter::pain_vox( sMeansOfDeath ); } function playDeathSoundPH( body, attacker, weapon, sMeansOfDeath ) { if ( self.team == game["attackers"] && IsDefined( body ) ) self battlechatter::play_death_vox( body, attacker, weapon, sMeansOfDeath ); } function round( value ) { value = int( value + 0.5 ); return value; } function gametypeRoundEndScoreHud( winner, endType, endReasonText, outcomeText, team, winnerEnum, notifyRoundEndToUI, matchBonus ) { if ( endType == "gameend" && IsDefined( level.propTieBreaker ) ) { if ( !isdefined( team ) || team == "spectator" ) { if ( isdefined( self.team ) && self.team != "spectator" && isdefined( game[ "propScore" ][ self.team ] ) ) team = self.team; else if ( isdefined( self.sessionteam ) && self.sessionteam != "spectator" && isdefined( game[ "propScore" ][ self.sessionteam ] ) ) team = self.sessionteam; if ( !isdefined( team ) ) return true; } otherTeam = util::getotherteam( team ); if( level.propTieBreaker == "kills" ) { winnerScore = game[ "propScore" ][ team ]; loserScore = game[ "propScore" ][ otherTeam ]; if ( winnerScore < loserScore ) { winnerScore = game[ "propScore" ][ otherTeam ]; loserScore = game[ "propScore" ][ team ]; } scorePacked = ( winnerScore << 8 ) + loserScore; self LUINotifyEvent( &"show_outcome", 6, outcomeText, &"MP_PH_TIEBREAKER_KILL", int( matchBonus ), winnerEnum, notifyRoundEndToUI, scorePacked ); return true; } else if ( level.propTieBreaker == "time" ) { teamSeconds = game[ "hunterKillTime" ][ team ] / 1000; otherTeam = util::getotherteam( team ); otherTeamSeconds = game[ "hunterKillTime" ][ otherTeam ] / 1000; teamSecondsRounded = round(teamSeconds); otherTeamSecondsRounded = round(otherTeamSeconds); if( teamSecondsRounded == otherTeamSecondsRounded ) { if( teamSeconds > otherTeamSeconds ) teamSecondsRounded++; else otherTeamSecondsRounded++; } winnerTime = teamSecondsRounded; loserTime = otherTeamSecondsRounded; if ( winnerTime < loserTime ) { winnerTime = otherTeamSecondsRounded; loserTime = teamSecondsRounded; } self LUINotifyEvent( &"show_outcome", 7, outcomeText, &"MP_PH_TIEBREAKER_TIME", int( matchBonus ), winnerEnum, notifyRoundEndToUI, winnerTime, loserTime ); return true; } } return false; } function finalizeStartSpawn( spawnPoint, predictedSpawn ) { if ( !predictedspawn ) { self.startSpawn = spawnPoint; } } function checkSpawnPoint( spawnPoint, predictedspawn ) { // Fail if any of the (start) spawns have been given to another player foreach ( player in level.players ) { if ( IsDefined( player.pers["team"] ) && player.pers["team"] != "spectator" ) { if ( IsDefined( player.startSpawn ) && player.startSpawn == spawnPoint ) return false; } } return true; } // from maySpawn() function gameHasStarted() { if ( level.teamBased ) return ( globallogic_spawn::allTeamsHaveExisted() ); else return (level.maxPlayerCount > 1) || ( !util::isOneRound() && !util::isFirstRound() ); } function maySpawnGameMode() { /# if ( level.alwaysAllowSpawn ) return true; #/ if ( level.inOvertime ) return false; if ( level.playerQueuedRespawn && !isdefined(self.allowQueueSpawn) && !level.inGracePeriod && !level.useStartSpawns ) return false; if ( level.numLives || level.numTeamLives ) { gameHasStarted = gameHasStarted(); if ( gameHasStarted && ( level.numLives && !self.pers["lives"] ) || ( level.numTeamLives && !game[self.team+"_lives"] ) ) { return false; } else if ( gameHasStarted ) { // disallow spawning for late comers if ( !level.inGracePeriod && !self.hasSpawned && !level.wagerMatch ) return false; } if ( self disableSpawningForPlayer() ) return false; } return true; } function disableSpawningForPlayer() { if ( !gameHasStarted() ) { return false; } if ( self isHunter() ) { return false; } else if ( self util::isProp() ) { return ( !level.inGracePeriod ); } return false; } function isHunter() { return ( isdefined( self.team ) && self.team == game["attackers"] ); } function deleteLevelTurrets() { turrets = getEntArray( "misc_turret", "classname" ); foreach ( turret in turrets ) turret Delete(); } function customDamageShellshockAndRumble( eAttacker, eInflictor, weapon, sMeansOfDeath, iDamage, vPoint ) { self thread weaponsOnDamage( eAttacker, eInflictor, weapon, sMeansOfDeath, iDamage, vPoint ); if ( !self util::isUsingRemote() ) { self PlayRumbleOnEntity( "damage_heavy" ); } } function weaponsOnDamage( eAttacker, eInflictor, weapon, meansOfDeath, damage, point ) { self endon ( "death" ); self endon ( "disconnect" ); if ( isdefined( level._custom_weapon_damage_func ) ) { is_weapon_registered = self [[level._custom_weapon_damage_func]]( eAttacker, eInflictor, weapon, meansOfDeath, damage ); if( is_weapon_registered ) { return; } } switch ( weapon.rootWeapon.name ) { case "concussion_grenade": if ( ( isdefined( self.concussionImmune ) && self.concussionImmune ) ) return; radius = weapon.explosionRadius; if (self == eAttacker) // TFLAME 8/1/12 - reduce effects on attacker radius *= 0.5; damageOrigin = eInflictor.origin; if ( isdefined( point ) ) damageOrigin = point; if ( self prop_controls::wasConcussedAtOrigin( damageOrigin ) ) return; scale = 1 - (distance( self.origin, damageOrigin ) / radius); if ( scale < 0 ) scale = 0; time = 0.25 + (4 * scale); {wait(.05);}; if ( meansOfDeath != "MOD_IMPACT" ) { if ( self HasPerk ( "specialty_stunprotection" ) ) { time *= 0.1; } else { if ( self util::mayApplyScreenEffect() ) { self shellShock( "concussion_grenade_mp", time, false ); } } self thread weapons::play_concussion_sound( time ); self.concussionEndTime = getTime() + (time * 1000); self.lastConcussedBy = eAttacker; if ( self util::IsProp() ) { if ( ( isdefined( self.lock ) && self.lock ) ) self prop_controls::unlockProp(); self prop_controls::registerConcussionEvent( einflictor, self, meansOfDeath, damage, damageOrigin, weapon ); } } break; default: // shellshock will only be done if meansofdeath is an appropriate type and if there is enough damage. if ( isdefined( level.shellshockOnPlayerDamage ) ) { [[level.shellshockOnPlayerDamage]]( meansOfDeath, damage, weapon ); } break; } } function manageMannequins() { level endon( "game_ended" ); {wait(.05);}; while ( !isdefined( level.mannequins ) ) {wait(.05);}; foreach ( mannequin in level.mannequins ) { mannequin NotSolid(); } level waittill( "props_hide_over" ); foreach ( mannequin in level.mannequins ) { mannequin Solid(); } } // ------------------------------------------------------------------------------------------------------------- function propWaitMiniGameInit( time ) { if ( !isdefined( level.ph_minigame ) ) level.ph_minigame = SpawnStruct(); if ( !( isdefined( level.ph_minigame.started ) && level.ph_minigame.started ) ) { level.ph_minigame.started = true; self thread propMiniGameGlobal( time ); } self.ph_miniGameScore = 0; self.ph_miniGameTime = 0; if ( level.ph_minigame.doChase && self IsHunter() && time > 8 ) { waittillframeend; if ( level.ph_minigame_clonerunners.size < 6 ) self propMiniGameSpawnCloneRunner(); } } function propMiniGameGlobal( time ) { if ( time <= 0 ) { level.ph_minigame.active = false; return; } thread propMiniGameStart(); waittillCountdownComplete( time ); level notify( "propMiniGameComplete" ); level.ph_minigame.active = false; foreach ( player in level.players ) { if ( isdefined( player.pers["team"] ) && player.pers["team"] == game["defenders"] ) player playerDefenderPropMiniGameComplete(); else if ( isdefined( player.pers["team"] ) && player.pers["team"] == game["attackers"] ) player playerAttackerPropMiniGameComplete(); } if ( isdefined( level.ph_minigame.hudPregame ) ) level.ph_minigame.hudPregame Destroy(); thread propMiniGameUpdateShowWinner( level.ph_minigame.hudPlaces[0], -80, 2.0 ); thread propMiniGameUpdateShowWinner( level.ph_minigame.hudPlaces[1], -50, 1.75 ); thread propMiniGameUpdateShowWinner( level.ph_minigame.hudPlaces[2], -20, 1.5 ); if ( isdefined( level.ph_minigame.targets ) ) { foreach ( target in level.ph_minigame.targets ) { if ( isdefined( target ) ) target Delete(); } } if ( isdefined( level.ph_minigame_clonerunners ) ) { foreach ( clone in level.ph_minigame_clonerunners ) { if ( isdefined( clone ) ) { if ( isdefined( clone.cloneTarget ) ) clone.cloneTarget Delete(); if ( isdefined( clone.targetObjectiveId ) ) gameobjects::release_obj_id( clone.targetObjectiveId ); clone Delete(); } } } } function propMiniGameUpdateShowWinner( hud, winYOffset, winFontScale ) { hud endon("death"); moveTime = 0.5; showTime = 2.5; fadeTime = 0.5; hud hud::setPoint( "BOTTOM", "CENTER", 0, winYOffset, moveTime ); hud changefontscaleovertime( moveTime ); hud.fontScale = winFontScale; wait moveTime+showTime; hud.alpha = 0; hud fadeovertime( fadeTime ); wait fadeTime; if ( isdefined( hud ) ) hud Destroy(); } function playerAttackerPropMiniGameComplete() { self propMiniGameSetHunterVisibility( true ); self TakeWeapon( GetWeapon( "null_offhand_primary" ) ); self giveOffhandPrimary( GetWeapon( "concussion_grenade" ), 2 ); self attackerInitAmmo(); } function playerDefenderPropMiniGameComplete() { self propMiniGameSetPropVisibility( true ); } function propMiniGameActive() { if ( !isdefined( level.ph_minigame ) ) level.ph_minigame = SpawnStruct(); if ( !isdefined( level.ph_minigame.active ) ) level.ph_minigame.active = GetDvarInt( "scr_prop_minigame", 0 ); return level.ph_minigame.active; } function propMiniGameSetHunterVisibility( isVisible ) { if ( isVisible ) { self Solid(); self SolidCapsule(); self showPlayerToAll( true ); if ( isdefined( self.phSpawnOrigin ) ) { self SetOrigin( self.phSpawnOrigin ); self SetPlayerAngles( self.phSpawnAngles ); } if ( isdefined( self.phClone ) ) self.phClone Delete(); } else { self NotSolid(); self NotSolidCapsule(); self thread hidePlayerFromTeam( game["defenders"], true ); self thread propMiniGameHunterClone( self ); } } function propMiniGameHunterClone( player ) { player endon( "disconnect" ); level endon( "game_ended" ); if ( !isdefined( player.phClone ) ) { waittillPrematchDone(); wait 0.1; clone = util::spawn_player_clone( player, "pb_stand_alert" ); weapon = player GetCurrentWeapon(); if( IsDefined( weapon.worldModel ) ) { clone Attach( weapon.worldModel, "tag_weapon_right" ); } clone NotSolid(); clone NotSolidCapsule(); clone HideFromTeam( player.pers["team"] ); player.phClone = clone; player thread propMiniGameCloneCleanup(player, player.phClone); } } function propMiniGameCloneCleanup( player, clone ) { clone endon( "entityshutdown" ); clone endon( "death" ); player waittill( "disconnect" ); if ( isdefined( clone ) ) clone Delete(); } function propMiniGameSetPropVisibility( isVisible ) { if ( isVisible ) { if ( isdefined( self.prop ) ) { self.prop Show(); self.prop Solid(); } self showPlayerToAll( false ); self Ghost(); if ( isdefined( self.propClones ) ) { foreach ( clone in self.propClones ) { clone Show(); clone Solid(); } } } else { if ( isdefined( self.prop ) ) { self.prop NotSolid(); self.prop HideFromTeam( game["attackers"] ); } self thread hidePlayerFromTeam( game["attackers"], false ); } } function propMiniGameStart() { level.ph_minigame.doChase = false; label = &"MP_PH_PREGAME_HUNT"; if ( RandomFloat( 1 ) < 0.5 ) { level.ph_minigame.doChase = true; label = &"MP_PH_PREGAME_CHASE"; } /# if ( GetDvarInt( "scr_ph_debugDoMinigameType", 0 ) == 2 && level.ph_minigame.doChase ) { level.ph_minigame.doChase = false; label = &"MP_PH_PREGAME_HUNT"; } else if ( GetDvarInt( "scr_ph_debugDoMinigameType", 0 ) == 1 && !level.ph_minigame.doChase ) { level.ph_minigame.doChase = true; label = &"MP_PH_PREGAME_CHASE"; } #/ thread propMiniGameHud( label ); level.ph_minigame.targetLocations = propMiniGameGetTargetLocations(); level.ph_minigame.targetLocations = array::randomize( level.ph_minigame.targetLocations ); level.ph_minigame.nextIndex = 0; if ( !level.ph_minigame.doChase ) thread propMiniGameSpawnTargets(); else level.ph_minigame_clonerunners = []; foreach ( player in level.players ) { if ( isdefined( player.pers["team"] ) && player.pers["team"] == game["attackers"] ) player thread playerAttackerPropMiniGameStart( &"MP_PH_EMPTY" ); } } function propMiniGameGetTargetLocations() { CONST_TARGET_DIST_SQ = 90000; targetLocations = []; allLocations = spawnlogic::get_spawnpoint_array( "mp_tdm_spawn" ); hunters = getLivingPlayersOnTeam( game["attackers"] ); hunter = hunters[0]; foreach ( location in allLocations ) { distSq = DistanceSquared( location.origin, hunter.origin ); if ( distSq > CONST_TARGET_DIST_SQ ) targetLocations[targetLocations.size] = location; } return targetLocations; } function propMiniGameGetTargetModel() { return "wpn_t7_uplink_ball_world"; } function propMiniGameSpawnTargets() { CONST_MAX_TARGETS = 40; CONST_SPAWNS_PER_FRAME = 4; model = propMiniGameGetTargetModel(); numTargets = min( level.ph_minigame.targetLocations.size, CONST_MAX_TARGETS ); level.ph_minigame.targets = []; num = 0; for ( i = 0; i < numTargets; i++ ) { origin = getNextTargetOrigin(); target = propMiniGameSpawnTarget( origin, model ); level.ph_minigame.targets[level.ph_minigame.targets.size] = target; num++; if ( num >= CONST_SPAWNS_PER_FRAME ) { {wait(.05);}; num = 0; } } } function propMiniGameTargetFx( targetEnt ) { {wait(.05);}; if ( isdefined( targetEnt ) ) PlayFxOnTag( "ui/fx_uplink_ball_vanish", targetEnt, "tag_origin" ); } function propMiniGameSpawnTarget( origin, model ) { target = Spawn( "script_model", origin ); target SetModel( model ); target.targetname = "propTarget"; target SetCanDamage( true ); target.fakeHealth = 50; target.health = 99999; target.maxhealth = 99999; target thread entityDamageWatcher( &propMiniGameDamageTargetWatch ); target SetPlayerCollision( false ); target MakeSentient(); target NotSolidCapsule(); target SetTeam( game["defenders"] ); target HideFromTeam( game["defenders"] ); target SetScale( 2, true ); thread propMiniGameTargetFx( target ); return target; } function propMiniGameDamageTargetWatch( damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, weapon, iDFlags ) { if ( !IsDefined( attacker ) ) return; if ( isPlayer( attacker ) ) { if ( ( isdefined( self.isDying ) && self.isDying ) ) return; attacker thread damagefeedback::update(); self.lastattacker = attacker; self.fakeHealth -= damage; if ( self.fakeHealth <= 0 ) { propMiniGamePlayerScored( attacker ); self thread moveTarget(); } } self.health += damage; } function moveTarget() { self.isDying = true; {wait(.05);}; self.fakeHealth = 50; fxEnt = PlayFx( fx::get( "propDeathFX" ), ( self.origin + ( 0, 0, 4 ) ) ); fxEnt Hide(); foreach ( player in level.players ) { if ( player IsHunter() ) fxEnt ShowToPlayer( player ); } fxEnt PlaySoundToTeam( "wpn_flash_grenade_explode", game["attackers"] ); self.origin = getNextTargetOrigin(); self DontInterpolate(); self.isDying = false; } function isLocationNearOtherTargets( location ) { CONST_CLOSEST_DIST_SQ = 90000; foreach ( target in level.ph_minigame.targets ) { distSq = DistanceSquared( target.origin, location ); if ( distSq < CONST_CLOSEST_DIST_SQ ) return true; } return false; } function getNextTargetOrigin() { if ( level.ph_minigame.nextIndex >= level.ph_minigame.targetLocations.size ) level.ph_minigame.nextIndex = 0; location = level.ph_minigame.targetLocations[level.ph_minigame.nextIndex]; if ( !isdefined( location.altOrigin ) ) { dir = ( level.mapCenter - location.origin ); dist = Distance( level.mapCenter, location.origin ); if ( dist > 0 ) dir = ( dir[0] / dist, dir[1] / dist, dir[2] / dist ); attempts = 9; newLocation = location.origin; rand = RandomFloat( 1 ); while ( attempts > 0 ) { randDist = dist * rand; newLocation = location.origin + ( dir * randDist ); if ( !isLocationNearOtherTargets( newLocation ) ) break; rand -= 0.1; if ( rand < 0 ) { newLocation = location.origin; break; } attempts--; } newLocation = GetClosestPointOnNavMesh( newLocation, 100 ); if ( !isdefined( newlocation ) ) newLocation = location.origin; location.altOrigin = newLocation; } origin = location.altOrigin + ( 0, 0, 40 ); level.ph_minigame.nextIndex++; return origin; } function propMiniGameHudCreatePlace( x, y, label, color ) { hudPlace = hud::createServerFontString( "default", 1.5, game["attackers"] ); hudPlace.label = label; hudPlace.x = x; hudPlace.y = y; hudPlace.alignX = "left"; hudPlace.alignY = "top"; hudPlace.horzAlign = "left"; hudPlace.vertAlign = "top"; hudPlace.color = color; hudPlace.archived = true; hudPlace.alpha = 0; hudPlace.glowAlpha = 0; hudPlace.hidewheninmenu = false; hudPlace.sort = 1001; return hudPlace; } function propMiniGameHud( titleLabel ) { level.ph_minigame.hudPlaces = []; yUpperLeft = 110; ySpace = 20; if ( !level.console ) { yUpperLeft = 125; ySpace = 15; } x = 5; y = yUpperLeft; level.ph_minigame.hudPlaces[level.ph_minigame.hudPlaces.size] = propMiniGameHudCreatePlace( x, y, &"MP_PH_MINIGAME_FIRST", (1.000, 0.843, 0.000) ); y += ySpace; level.ph_minigame.hudPlaces[level.ph_minigame.hudPlaces.size] = propMiniGameHudCreatePlace( x, y, &"MP_PH_MINIGAME_SECOND", (0.300, 0.300, 0.300) ); y += ySpace; level.ph_minigame.hudPlaces[level.ph_minigame.hudPlaces.size] = propMiniGameHudCreatePlace( x, y, &"MP_PH_MINIGAME_THIRD", (0.804, 0.498, 0.196) ); level.ph_minigame.hudPregame = hud::createServerFontString( "default", 2.5, game["attackers"] ); level.ph_minigame.hudPregame hud::setPoint( "CENTER", undefined, 0, -30 ); level.ph_minigame.hudPregame.label = titleLabel; level.ph_minigame.hudPregame.x = 0; level.ph_minigame.hudPregame.archived = true; level.ph_minigame.hudPregame.alpha = 1; level.ph_minigame.hudPregame.glowAlpha = 0; level.ph_minigame.hudPregame.hidewheninmenu = false; thread propMinigameDelayedHud(); } function propMiniGameDelayedHud() { level endon( "game_ended" ); wait 5.5; level.ph_minigame.hudPregame MoveOverTime( 1.0 ); level.ph_minigame.hudPregame hud::setPoint( "CENTER", undefined, 0, -100 ); wait 1.0; level.ph_minigame.hudPregame FadeOverTime( 1.0 ); level.ph_minigame.hudPregame.color = ( 0, 1, 0 ); wait 1.0; level.ph_minigame.hudPregame FadeOverTime( 1.0 ); level.ph_minigame.hudPregame.color = ( 1, 1, 1 ); } function propMiniGamePlayerScored( player ) { gameMSElapsed = ( GetTime() - level.startTime - level.totalHostMigrationTime ); player.ph_miniGameScore++; player.ph_miniGameTime += gameMSElapsed; player.phIndicator SetValue( player.ph_miniGameScore ); player thread propMiniGameFlashIndicator(); propMiniGameUpdateScoreboard(); } function propMiniGameUpdateScoreboard( delayTime ) { level endon( "game_ended" ); if ( isdefined( delayTime ) ) wait delayTime; hunters = getLivingPlayersOnTeam( game["attackers"] ); sortedHunters = array::quickSort( hunters, &propMiniGameScoreCompare ); for ( i = 0; i < 3; i++ ) { if ( isdefined( sortedHunters[i] ) && isdefined( sortedHunters[i].ph_miniGameScore ) && sortedHunters[i].ph_miniGameScore > 0 ) { level.ph_minigame.hudPlaces[i].alpha = 1; level.ph_minigame.hudPlaces[i] SetPlayerNameString( sortedHunters[i] ); } else if ( isdefined( level.ph_minigame.hudPlaces ) && isdefined( level.ph_minigame.hudPlaces[i] ) && level.ph_minigame.hudPlaces[i].alpha > 0 ) { level.ph_minigame.hudPlaces[i].alpha = 0; } } } function propMiniGameScoreCompare( p1, p2 ) { if ( !isdefined( p1 ) || !isdefined( p1.ph_miniGameScore ) ) return false; if ( !isdefined( p2 ) || !isdefined( p2.ph_miniGameScore ) ) return true; if ( p1.ph_miniGameScore > p2.ph_miniGameScore ) return true; return ( ( p1.ph_miniGameScore == p2.ph_miniGameScore ) && ( p1.ph_miniGameTime <= p2.ph_miniGameTime ) ); } function propMiniGameIndicator( label ) { self.phIndicator = hud::createFontString( "objective", 1 ); self.phIndicator.label = label; self.phIndicator.x = 0; self.phIndicator.y = 20; self.phIndicator.alignX = "center"; self.phIndicator.alignY = "middle"; self.phIndicator.horzAlign = "user_center"; self.phIndicator.vertAlign = "middle"; self.phIndicator.archived = true; self.phIndicator.fontscale = 1; self.phIndicator.alpha = 0; self.phIndicator.glowAlpha = 0.5; self.phIndicator.hidewheninmenu = false; } function playerAttackerPropMiniGameStart( label ) { self propMiniGameIndicator( label ); } function propMiniGameFlashIndicator() { self.phIndicator.alpha = 1; self.phIndicator FadeOverTime( 3 ); self.phIndicator.alpha = 0; } //function propMiniGameSpawnCloneRunners() //{ // CONST_NUM_RUNNERS = 6; // // level.ph_minigame_clonerunners = []; // // foreach ( player in level.players ) // { // propMiniGameSpawnCloneRunner(); // } //} function propMiniGameSpawnCloneRunner() { forward = anglestoforward( self getangles() ); origin = self.origin + VectorScale( forward, 100 ); origin = GetClosestPointOnNavMesh( origin, 600 ); clone = SpawnActor( "spawner_bo3_robot_grunt_assault_mp", origin, self.angles, "", true ); clone.cloneTarget = propMiniGameSpawnCloneTarget( origin + ( 0, 0, 40 ) ); clone.cloneTarget LinkTo( clone ); level.ph_minigame_clonerunners[level.ph_minigame_clonerunners.size] = clone; propMiniGameConfigureClone( clone, self, forward ); } function propMiniGameConfigureClone( clone, player, forward ) // self is player { clone.isAiClone = true; clone.properName = ""; clone.ignoreTriggerDamage = true; clone.minWalkDistance = 125; clone.overrideActorDamage = &cloneDamageOverride; clone.spawnTime = GetTime(); clone.skipGibs = true; clone setmaxhealth( 9999 ); clone PushActors( true ); // Don't collide with other actors. clone PushPlayer( true ); // Don't collide with players. clone SetContents( (1 << 13) ); // Collide with bullets. clone SetAvoidanceMask( "avoid none" ); // Disable all avoidance. clone.targetObjectiveId = gameobjects::get_next_obj_id(); Objective_Add( clone.targetObjectiveId, "active" ); Objective_Team( clone.targetObjectiveId, game["attackers"] ); Objective_Position( clone.targetObjectiveId, clone.origin ); Objective_Icon( clone.targetObjectiveId, "t7_hud_waypoints_safeguard_location" ); Objective_SetColor( clone.targetObjectiveId, &"FriendlyBlue" ); Objective_OnEntity( clone.targetObjectiveId, clone ); clone ASMSetAnimationRate( 1.20 ); clone setclone(); clone._goal_center_point = getNextCloneGoalOrigin(); queryResult = undefined; if ( IsDefined( clone._goal_center_point ) && clone FindPath( clone.origin, clone._goal_center_point, true, false ) ) { queryResult = PositionQuery_Source_Navigation( clone._goal_center_point, 0, 450, 450, 100, clone ); } else { queryResult = PositionQuery_Source_Navigation( clone.origin, 500, 750, 750, 50, clone ); } if( queryResult.data.size > 0 ) { clone setgoalpos( queryResult.data[0].origin, true ); clone._clone_goal = queryresult.data[0].origin; clone._clone_goal_max_dist = 450; } else { clone._goal_center_point = clone.origin; } clone thread _UpdateClonePathing(); clone HideFromTeam( game["defenders"] ); clone Ghost(); _ConfigureCloneTeam( clone, player ); } function cloneDamageOverride( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, timeOffset, boneIndex, modelIndex, surfaceType, surfaceNormal ) { return 0; } function _ConfigureCloneTeam( clone, player ) { team = util::getOtherTeam( player.team ); clone.ignoreall = true; clone SetTeam( team ); clone.team = team; } function _GetCloseClones() { CLONE_TOO_CLOSE_DIST_SQ = 10000; closeClones = []; foreach ( clone in level.ph_minigame_clonerunners ) { if ( self == clone ) continue; distSq = DistanceSquared( clone.origin, self.origin ); if ( distSq < CLONE_TOO_CLOSE_DIST_SQ ) closeClones[closeClones.size] = clone; } return closeClones; } function _UpdateClonePathing() { self endon( "death" ); CLONE_NOT_MOVING_DIST_SQ = 576; CLONE_NOT_MOVING_POLL_TIME = 2000; CLONE_TOO_CLOSE_POLL_TIME = 1500; if ( !isdefined( level.ph_minigame.lastPathFindTime ) ) level.ph_minigame.lastPathFindTime = 0; while( true ) { if ( !IsDefined( self.lastKnownPos ) ) { self.lastKnownPos = self.origin; self.lastKnownPosTime = GetTime(); } if ( !isdefined( self.lastTooCloseTime ) ) { self.lastTooCloseTime = GetTime(); } distance = 0; if( isDefined( self._clone_goal ) ) distance = DistanceSquared( self._clone_goal, self.origin ); findNewPath = false; if ( distance < 120 * 120 ) { findNewPath = true; } else if ( !self HasPath() ) { findNewPath = true; } else if ( ( self.lastKnownPosTime + CLONE_NOT_MOVING_POLL_TIME ) <= GetTime() ) { if ( DistanceSquared( self.lastKnownPos, self.origin ) < CLONE_NOT_MOVING_DIST_SQ ) findNewPath = true; self.lastKnownPos = self.origin; self.lastKnownPosTime = GetTime(); } else if ( ( self.lastTooCloseTime + CLONE_TOO_CLOSE_POLL_TIME ) <= GetTime() && level.ph_minigame.lastPathFindTime != GetTime() ) // one path find per frame { clones = _GetCloseClones(); if ( clones.size > 0 ) findNewPath = true; // give this clone a chance to separate for ( i = 0; i < clones.size; i++ ) { clones[i].lastTooCloseTime = GetTime(); } self.lastTooCloseTime = GetTime(); } if ( findNewPath ) { level.ph_minigame.lastPathFindTime = GetTime(); self._goal_center_point = getNextCloneGoalOrigin(); queryResult = PositionQuery_Source_Navigation( self._goal_center_point, 500, 750, 750, 100, self ); if( queryResult.data.size == 0 ) { queryResult = PositionQuery_Source_Navigation( self.origin, 500, 750, 750, 100, self ); } if( queryResult.data.size > 0 ) { randIndex = RandomIntRange( 0, queryResult.data.size ); self setgoalpos( queryResult.data[ randIndex ].origin, true ); self._clone_goal = queryresult.data[ randIndex ].origin; self._clone_goal_max_dist = 750; } } // util::drawcylinder( self._goal_center_point, self._clone_goal_max_dist, 10, .5, "stop_notify_asdf" ); // util::debug_sphere( self._clone_goal, 10, ( 1, 0, 1 ), 1, 1 ); wait( .5 ); } } function getNextCloneGoalOrigin() { if ( level.ph_minigame.nextIndex >= level.ph_minigame.targetLocations.size ) level.ph_minigame.nextIndex = 0; location = level.ph_minigame.targetLocations[level.ph_minigame.nextIndex]; level.ph_minigame.nextIndex++; return location.origin; } function propMiniGameSpawnCloneTarget( origin ) { model = propMiniGameGetTargetModel(); target = Spawn( "script_model", origin ); target SetModel( model ); target.targetname = "propTarget"; target SetCanDamage( true ); target.fakeHealth = 50; target.health = 99999; target.maxhealth = 99999; target thread entityDamageWatcher( &propMiniGameDamageCloneTargetWatch ); target SetPlayerCollision( false ); target MakeSentient(); target NotSolidCapsule(); target SetTeam( game["defenders"] ); target HideFromTeam( game["defenders"] ); target SetScale( 2, true ); thread propMiniGameTargetFx( target ); return target; } function propMiniGameDamageCloneTargetWatch( damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, weapon, iDFlags ) { if ( !IsDefined( attacker ) ) return; if ( isPlayer( attacker ) ) { attacker thread damagefeedback::update(); self.lastattacker = attacker; propMiniGamePlayerScored( attacker ); } self.health += damage; }