// note: this is technically vanilla content, since bleedout was shipped with retail, but it needs custom remote functions which would break vanilla compatiblity, so it's not in Northstar.CustomServers // idk why bleedout was even shipped in retail lmao global function BleedoutDamage_PreInit global function BleedoutDamage_Init global function SetShouldPlayerStartBleedoutFunc struct { array bleedingPlayers // this is in _bleedout already, but it doesn't expose a way to track it, so we have to track it ourselves bool functionref( entity, var ) shouldPlayerStartBleedoutFunc = null } file void function BleedoutDamage_PreInit() { AddCallback_OnRegisteringCustomNetworkVars( Bleedout_RegisterRemoteFunctions ) } void function Bleedout_RegisterRemoteFunctions() { Remote_RegisterFunction( "ServerCallback_BLEEDOUT_StartFirstAidProgressBar" ) Remote_RegisterFunction( "ServerCallback_BLEEDOUT_StopFirstAidProgressBar" ) Remote_RegisterFunction( "ServerCallback_BLEEDOUT_ShowWoundedMarker" ) Remote_RegisterFunction( "ServerCallback_BLEEDOUT_HideWoundedMarker" ) } // copied from sh_bleedout const float DEFAULT_BLEEDOUT_TIME = 30.0 const float DEFAULT_FIRSTAID_TIME = 3.0 const float DEFAULT_FIRSTAID_TIME_SELF = -1.0 const float DEFAULT_FIRSTAID_HEAL_PERCENT = 1.0 const float DEFAULT_AI_BLEEDING_PLAYER_MISS_CHANCE = 0.0 const bool DEFAULT_FORCE_WEAPON_HOLSTER = false const bool DEFAULT_DEATH_ON_TEAM_BLEEDOUT = false void function BleedoutDamage_Init() { AddPrivateMatchModeSettingEnum( "#MODE_SETTING_CATEGORY_BLEEDOUT", "riff_player_bleedout", [ "Default", "Disabled", "Enabled" ], "0" ) AddPrivateMatchModeSettingEnum( "#MODE_SETTING_CATEGORY_BLEEDOUT", "player_bleedout_forceHolster", [ "Disabled", "Enabled" ], DEFAULT_FORCE_WEAPON_HOLSTER.tostring() ) AddPrivateMatchModeSettingEnum( "#MODE_SETTING_CATEGORY_BLEEDOUT", "player_bleedout_forceDeathOnTeamBleedout", [ "Disabled", "Enabled" ], DEFAULT_DEATH_ON_TEAM_BLEEDOUT.tostring() ) AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_BLEEDOUT", "player_bleedout_bleedoutTime", DEFAULT_BLEEDOUT_TIME.tostring() ) AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_BLEEDOUT", "player_bleedout_firstAidTime", DEFAULT_FIRSTAID_TIME.tostring() ) AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_BLEEDOUT", "player_bleedout_firstAidTimeSelf", DEFAULT_FIRSTAID_TIME_SELF.tostring() ) AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_BLEEDOUT", "player_bleedout_firstAidHealPercent", DEFAULT_FIRSTAID_HEAL_PERCENT.tostring() ) AddPrivateMatchModeSettingArbitrary( "#MODE_SETTING_CATEGORY_BLEEDOUT", "player_bleedout_aiBleedingPlayerMissChance", DEFAULT_AI_BLEEDING_PLAYER_MISS_CHANCE.tostring() ) #if CLIENT // because playlist var overrides fucking suck, they aren't actually updated by this point // client bleedout can be inited late enough that we can just init it on local player spawn AddCallback_LocalClientPlayerSpawned( InitClientBleedoutForLocalPlayer ) #elseif SERVER // sh_riff_settings should set this correctly on server if ( !Riff_PlayerBleedout() ) return AddDamageCallback( "player", HandleDamageForBleedout ) // do this irregardless of whether scripts inited, given it should always be used for bleedout Bleedout_SetCallback_OnPlayerStartBleedout( OnPlayerBleedoutBegin ) // kinda sucks we have to use this callback since game scripts could be using it // dont init if scripts already inited it manually if ( !Bleedout_IsBleedoutLogicActive() ) { InitSharedBleedoutWithPlaylistVars() Bleedout_Init() } #endif } void function SetShouldPlayerStartBleedoutFunc( bool functionref( entity, var ) func ) { file.shouldPlayerStartBleedoutFunc = func } void function InitSharedBleedoutWithPlaylistVars() { BleedoutShared_Init( GetCurrentPlaylistVarFloat( "player_bleedout_bleedoutTime", DEFAULT_BLEEDOUT_TIME ), GetCurrentPlaylistVarFloat( "player_bleedout_firstAidTime", DEFAULT_FIRSTAID_TIME ), GetCurrentPlaylistVarFloat( "player_bleedout_firstAidTimeSelf", DEFAULT_FIRSTAID_TIME_SELF ), GetCurrentPlaylistVarFloat( "player_bleedout_firstAidHealPercent", DEFAULT_FIRSTAID_HEAL_PERCENT ), GetCurrentPlaylistVarFloat( "player_bleedout_aiBleedingPlayerMissChance", DEFAULT_AI_BLEEDING_PLAYER_MISS_CHANCE ), GetCurrentPlaylistVarInt( "player_bleedout_forceHolster", int( DEFAULT_FORCE_WEAPON_HOLSTER ) ) == 1, GetCurrentPlaylistVarInt( "player_bleedout_forceDeathOnTeamBleedout", int( DEFAULT_DEATH_ON_TEAM_BLEEDOUT ) ) == 1 ) } #if CLIENT void function InitClientBleedoutForLocalPlayer( entity player ) { // dont init if bleedout is disabled or scripts already inited it if ( !Riff_PlayerBleedout() || Bleedout_IsBleedoutLogicActive() ) return InitSharedBleedoutWithPlaylistVars() BleedoutClient_Init() } #endif #if SERVER void function HandleDamageForBleedout( entity player, var damageInfo ) { if ( IsInstantDeath( damageInfo ) || DamageInfo_GetForceKill( damageInfo ) || player.IsTitan() || file.bleedingPlayers.contains( player ) ) return if ( file.shouldPlayerStartBleedoutFunc != null ) if ( !file.shouldPlayerStartBleedoutFunc( player, damageInfo ) ) return // check if damage would kill player if ( player.GetHealth() - DamageInfo_GetDamage( damageInfo ) <= 0 ) { Bleedout_StartPlayerBleedout( player, DamageInfo_GetAttacker( damageInfo ) ) DamageInfo_SetDamage( damageInfo, 1 ) // prevent player from dying, but if we set it to 0, player won't receive any knockback from damage source } } void function OnPlayerBleedoutBegin( entity player ) { file.bleedingPlayers.append( player ) // would prefer to use Bleedout_SetCallback_OnPlayerGiveFirstAid for this, but it doesn't expose the player that's receiving first aid for some reason thread TrackPlayerBleedout( player ) } void function TrackPlayerBleedout( entity player ) { player.EndSignal( "OnDeath" ) player.EndSignal( "OnDestroy" ) OnThreadEnd( function() : ( player ) { file.bleedingPlayers.remove( file.bleedingPlayers.find( player ) ) }) WaitFrame() // wait a frame, since this gets called before this status effect is added while ( StatusEffect_Get( player, eStatusEffect.bleedoutDOF ) != 0 ) WaitFrame() } #endif