global function GamemodeInfection_Init struct { bool hasHadFirstInfection = false bool hasHadLastInfection = false array playersToNotifyOfInfection } file void function GamemodeInfection_Init() { SetSpawnpointGamemodeOverride( FFA ) SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period SetWeaponDropsEnabled( false ) SetShouldUseRoundWinningKillReplay( true ) Riff_ForceTitanAvailability( eTitanAvailability.Never ) Riff_ForceBoostAvailability( eBoostAvailability.Disabled ) ClassicMP_ForceDisableEpilogue( true ) SetShouldPlayerStartBleedoutFunc( InfectionShouldPlayerStartBleedout ) AddCallback_OnClientConnected( InfectionInitPlayer ) AddCallback_OnPlayerKilled( InfectionOnPlayerKilled ) AddCallback_OnPlayerRespawned( RespawnInfected ) AddCallback_GameStateEnter( eGameState.Playing, SelectFirstInfected ) SetTimeoutWinnerDecisionFunc( TimeoutCheckSurvivors ) AddCallback_GameStateEnter( eGameState.WinnerDetermined, OnWinnerDetermined ) } void function InfectionInitPlayer( entity player ) { if ( GetGameState() < eGameState.Playing || !file.hasHadFirstInfection ) // per Gecko's suggestion, make anyone joining before first infected to stay as survivor instead SetTeam( player, INFECTION_TEAM_SURVIVOR ) else InfectPlayer( player ) } void function SelectFirstInfected() { thread SelectFirstInfectedDelayed() } void function SelectFirstInfectedDelayed() { wait 10.0 + RandomFloat( 5.0 ) array players = GetPlayerArray() entity infected = players[ RandomInt( players.len() ) ] InfectPlayer( infected ) RespawnInfected( infected ) } void function InfectionOnPlayerKilled( entity victim, entity attacker, var damageInfo ) { if ( !victim.IsPlayer() || GetGameState() != eGameState.Playing ) return if ( victim.GetTeam() == INFECTION_TEAM_SURVIVOR ) InfectPlayer( victim ) if ( attacker.IsPlayer() ) { attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 ) SetRoundWinningKillReplayAttacker(attacker) } } void function InfectPlayer( entity player ) { SetTeam( player, INFECTION_TEAM_INFECTED ) player.SetPlayerGameStat( PGS_ASSAULT_SCORE, 0 ) // reset kills file.playersToNotifyOfInfection.append( player ) // check how many survivors there are array survivors = GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ) if ( survivors.len() == 0 ) SetWinner( INFECTION_TEAM_INFECTED ) else if ( survivors.len() == 1 && !file.hasHadLastInfection ) SetLastSurvivor( survivors[ 0 ] ) if ( !file.hasHadFirstInfection ) { file.hasHadFirstInfection = true foreach ( entity otherPlayer in GetPlayerArray() ) if ( player != otherPlayer ) Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_AnnounceFirstInfected", player.GetEncodedEHandle() ) PlayMusicToAll( eMusicPieceID.GAMEMODE_1 ) } } void function RespawnInfected( entity player ) { if ( player.GetTeam() != INFECTION_TEAM_INFECTED ) return // notify newly infected players of infection if ( file.playersToNotifyOfInfection.contains( player ) ) { Remote_CallFunction_NonReplay( player, "ServerCallback_YouAreInfected" ) file.playersToNotifyOfInfection.remove( file.playersToNotifyOfInfection.find( player ) ) } // set camo to pond scum player.SetSkin( 1 ) player.SetCamo( 110 ) // if human, remove helmet bodygroup, human models have some weird bloody white thing underneath their helmet that works well for this, imo if ( !player.IsMechanical() ) player.SetBodygroup( player.FindBodyGroup( "head" ), 1 ) // stats for infected StimPlayer( player, 9999.9 ) // can't do endless since we don't get the visual effect in endless // scale health with num of infected, with 50 as base health player.SetMaxHealth( ( GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ).len() + 1 ) * 10 ) // set loadout foreach ( entity weapon in player.GetMainWeapons() ) player.TakeWeaponNow( weapon.GetWeaponClassName() ) foreach ( entity weapon in player.GetOffhandWeapons() ) player.TakeWeaponNow( weapon.GetWeaponClassName() ) // TEMP: give archer so player so player has a weapon which lets them use offhands // need to replace this with a custom empty weapon at some point //player.GiveWeapon( "mp_weapon_rocket_launcher" ) player.GiveWeapon( "mp_weapon_mgl" ) player.GiveOffhandWeapon( "melee_pilot_emptyhanded", OFFHAND_MELEE ) thread GiveAirAccel(player) thread PlayInfectedSounds( player ) } void function GiveAirAccel(entity player) { WaitFrame() if (GetCurrentPlaylistVarInt( "custom_air_accel_pilot", int( player.GetPlayerSettingsField( "airAcceleration" ) ) ) < 2500) player.kv.airAcceleration = 2500 // prevents sh_custom_air_accel from not giving infected air acceleration. else player.kv.airAcceleration = GetCurrentPlaylistVarInt( "custom_air_accel_pilot", int( player.GetPlayerSettingsField( "airAcceleration" ) ) ) } void function PlayInfectedSounds( entity player ) { player.EndSignal( "OnDeath" ) player.EndSignal( "OnDestroy" ) float nextRandomSound while ( true ) { WaitFrame() int meleeState = player.PlayerMelee_GetState() if ( nextRandomSound < Time() || meleeState != 0 ) { string selectedSound if ( CoinFlip() ) selectedSound = "prowler_vocal_attack" else selectedSound = "prowler_vocal_attackmiss" bool canSeeSurvivor foreach ( entity survivor in GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ) ) if ( TraceLineSimple( player.GetOrigin(), survivor.GetOrigin(), survivor ) == 1.0 ) canSeeSurvivor = true // _int sounds are less agressive so only play them if we aren't in some sorta fight if ( player.GetHealth() == player.GetMaxHealth() || !canSeeSurvivor || meleeState != 0 ) selectedSound += "_int" EmitSoundOnEntity( player, selectedSound ) nextRandomSound = Time() + max( 2.5, RandomFloat( 12.0 ) ) while ( player.PlayerMelee_GetState() != 0 ) // need to ensure this is updated WaitFrame() } } } void function SetLastSurvivor( entity player ) { foreach ( entity otherPlayer in GetPlayerArray() ) Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_AnnounceLastSurvivor", player.GetEncodedEHandle() ) Highlight_SetEnemyHighlight( player, "enemy_sonar" ) StatusEffect_AddEndless( player, eStatusEffect.sonar_detected, 1.0 ) // sonar is better here so the player themselves see the SONAR DETECTED warning. if ( SpawnPoints_GetTitan().len() > 0 ) thread CreateTitanForPlayerAndHotdrop( player, GetTitanReplacementPoint( player, false ) ) if ( GameTime_TimeLeftSeconds() > 45 ) SetServerVar( "gameEndTime", Time() + 45.0 ) file.hasHadLastInfection = true } int function TimeoutCheckSurvivors() { if ( GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ).len() > 0 ) return INFECTION_TEAM_SURVIVOR return INFECTION_TEAM_INFECTED } bool function InfectionShouldPlayerStartBleedout( entity player, var damageInfo ) { return player.GetTeam() != INFECTION_TEAM_INFECTED } void function OnWinnerDetermined() { SetRespawnsEnabled( false ) SetKillcamsEnabled( false ) }