diff options
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/conversation')
5 files changed, 736 insertions, 0 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/conversation/_battle_chatter.gnut b/Northstar.CustomServers/scripts/vscripts/conversation/_battle_chatter.gnut new file mode 100644 index 00000000..961816c7 --- /dev/null +++ b/Northstar.CustomServers/scripts/vscripts/conversation/_battle_chatter.gnut @@ -0,0 +1,25 @@ +global function BattleChatter_Init +global function PlayBattleChatterLine +global function TryPlayWeaponBattleChatterLine + +void function BattleChatter_Init() +{ + //ShBattleChatter_Init() +} + +void function PlayBattleChatterLine( entity player, string conversationType ) +{ + foreach( entity otherPlayer in GetPlayerArray() ) + if ( ShouldPlayBattleChatter( conversationType, otherPlayer, player ) && player != otherPlayer ) + Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_PlayBattleChatter", GetConversationIndex( conversationType ), player.GetEncodedEHandle() ) +} + +void function TryPlayWeaponBattleChatterLine( entity player, entity weapon ) +{ + var chatterEvent = weapon.GetWeaponInfoFileKeyField( "battle_chatter_event" ) + if ( chatterEvent == null ) + return + + expect string( chatterEvent ) + PlayBattleChatterLine( player, chatterEvent ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/scripts/vscripts/conversation/_conversation_schedule.gnut b/Northstar.CustomServers/scripts/vscripts/conversation/_conversation_schedule.gnut new file mode 100644 index 00000000..089d4b71 --- /dev/null +++ b/Northstar.CustomServers/scripts/vscripts/conversation/_conversation_schedule.gnut @@ -0,0 +1,629 @@ +untyped + +global function DialogueScheduleServer_Init + +global function GetConversationIndex +global function PlaySquadConversationToPlayer +global function PlaySquadConversationToTeam +global function PlaySquadConversationToAll +global function PlaySpectreChatterToAll +global function PlaySpectreChatterToTeam +global function PlaySpectreChatterToPlayer +global function PlaySquadConversation +global function PlayConversationToPlayer +global function Delayed_PlayConversationToPlayer +global function PlayConversationToTeam +global function PlayConversationToAll +global function PlayConversationToAllExcept +global function PlayConversationToTeamExceptPlayer +global function ForcePlayConversationToPlayer +global function ForcePlayConversationToAll +global function ForcePlayConversationToTeam +global function SetGlobalForcedDialogueOnly +global function SetPlayerForcedDialogueOnly +global function CodeCallback_ScriptedDialogue +global function GetNearbyEnemyGrunts +global function GetNearbyFriendlyGrunts +global function CodeCallback_OnNPCLookAtHint + +global function ScriptDialog_PilotCloaked + +struct +{ + array< void functionref( entity ) > codeDialogueFunc + +} file + +void function DialogueScheduleServer_Init() +{ + #document( "PlayConversationToPlayer", " Play conversation passed in to player specified" ) + + // dialogue that comes from ai schedule notifies + + // must match order of enum eCodeDialogueID + file.codeDialogueFunc = [ + CodeDialogue_ManDown, + CodeDialogue_GruntSalute, + CodeDialogue_EnemyContact, //As per Conger's advice: Don't depend on this one. Use WaitSignal( guy, "OnFoundEnemy", "OnSeeEnemy", "OnLostEnemy" ) + CodeDialogue_RunFromEnemy, + CodeDialogue_Reload, + CodeDialogue_MoveToAssault, + CodeDialogue_MoveToSquadLeader, + CodeDialogue_FanOut, + CodeDialogue_TakeCoverFromEnemy, + CodeDialogue_ChaseEnemy, + CodeDialogue_GrenadeOut, + CodeDialogue_DangerousAreaDisplace, + CodeDialogue_ReactSurprised, + ] + + Assert( file.codeDialogueFunc.len() == eCodeDialogueID.DIALOGUE_COUNT ) +} + +void function ScriptDialog_PilotCloaked( entity guy, entity enemy ) +{ + Assert( IsPilot( enemy ), "These dialog lines assume enemy is a pilot" ) + + if ( NPC_GruntChatterSPEnabled( guy ) ) + { + #if GRUNTCHATTER_ENABLED + GruntChatter_TryCloakedPilotSpotted( guy, enemy ) + #endif + } + else + { + #if GRUNT_CHATTER_MP_ENABLED + PlayGruntChatterMPLine( guy, "bc_engageenemycloakedpilot" ) + #endif + } +} + +void function CodeDialogue_GruntSalute( entity guy ) +{ + //EmitSoundOnEntity( guy, "grunt_salute" ) + //PlaySquadConversationToAll( "grunt_salute" ) +} + +void function CodeDialogue_EnemyContact( entity guy ) //As per Conger's advice: Don't depend on this one. Use WaitSignal( guy, "OnFoundEnemy", "OnSeeEnemy", "OnLostEnemy" ) +{ +} + + +void function CodeDialogue_RunFromEnemy( entity guy ) +{ + //MP and SP use different systems. + #if GRUNT_CHATTER_MP_ENABLED + //MP, use PlayOneLinerConversationOnEntWithPriority() as base function + entity enemy = guy.GetEnemy() + if ( !IsAlive( enemy ) ) + return + + if ( enemy.IsTitan() ) + PlayGruntChatterMPLine( guy, "bc_fleePlayerTitanCall" ) + #else + //SP, use r1 style PlayConversation calls() + // only imc has these currently + if ( guy.GetTeam() != TEAM_IMC ) + return + + entity enemy = guy.GetEnemy() + if ( !IsAlive( enemy ) ) + return + + if ( enemy.IsTitan() ) + { + local squadName = guy.Get( "squadname" ) + + bool isSquad = false + + if ( squadName != "" ) + { + array<entity> squad = GetNPCArrayBySquad( squadName ) + isSquad = squad.len() > 1 + } + + if ( isSquad ) + { + // has a safe hint? running to building + if ( guy.GetSafeHint() ) + PlaySquadConversationToAll( "grunt_flees_titan_building", guy ) + else + PlaySquadConversationToAll( "grunt_group_flees_titan", guy ) + } + else + { + PlaySquadConversationToAll( "grunt_flees_titan", guy ) + } + } + #endif +} + +void function CodeDialogue_Reload( entity guy ) +{ + //PlaySquadConversationToAll( "aichat_reload", guy ) +} + +void function CodeDialogue_FanOut( entity guy ) +{ +} + +void function CodeDialogue_MoveToSquadLeader( entity guy ) +{ +} + +void function CodeDialogue_MoveToAssault( entity guy ) +{ +} + +void function CodeDialogue_TakeCoverFromEnemy( entity guy ) +{ + #if HAS_BOSS_AI + if ( guy.IsTitan() ) + BossTitanRetreat( guy ) + #endif +} + +void function CodeDialogue_ChaseEnemy( entity guy ) +{ + #if HAS_BOSS_AI + if ( guy.IsTitan() ) + BossTitanAdvance( guy ) + #endif +} + +void function CodeDialogue_GrenadeOut( entity guy ) +{ + if ( NPC_GruntChatterSPEnabled( guy ) ) + { + #if GRUNTCHATTER_ENABLED + // Ticks are actually thrown like grenades, but the callouts work differently because only Specialists use them + // TODO- move this info to the weapon data file + if ( guy.kv.grenadeWeaponName == "mp_weapon_frag_drone" ) + GruntChatter_TryFriendlyEquipmentDeployed( guy, "mp_weapon_frag_drone" ) + else + GruntChatter_TryThrowingGrenade( guy ) + #endif + } + else + { + if ( IsSpectre( guy ) ) + { + #if SPECTRE_CHATTER_MP_ENABLED + PlaySpectreChatterMPLine( guy, "diag_imc_spectre_gs_grenadeout_01_1" ) + #else + PlaySpectreChatterToAll( "spectre_gs_grenadeout_01_1", guy ) + + #endif + } + else if ( IsGrunt( guy ) ) + { + #if GRUNT_CHATTER_MP_ENABLED + PlayGruntChatterMPLine( guy, "bc_grenadeOutCall" ) + #endif + } + } +} + +void function CodeDialogue_DangerousAreaDisplace( entity guy ) +{ + #if GRUNT_CHATTER_MP_ENABLED + //MP ONly + string dangerousAreaWeaponName = guy.GetDangerousAreaWeapon() + //printt( "CodeDialogue_DangerousAreaDisplace, Dangerous weapon name: " + dangerousAreaWeaponName ) + string conversationName = "" + switch ( dangerousAreaWeaponName ) //String comparison, not great... + { + case "mp_weapon_frag_grenade": + conversationName = "bc_grenadecall" + break + + case "mp_weapon_thermite_grenade": + conversationName = "bc_reactGrenadeThermite" + break + + case "mp_weapon_grenade_gravity": //By the time this triggers it looks like they're already being sucked in. + conversationName = "bc_reactGrenadeGravity" + break + + case "mp_weapon_grenade_electric_smoke": + conversationName = "bc_reactGrenadeElecSmoke" + break + + //Arc grenades have their dialogue triggered by PlayGruntChatterMP_DamagedByEMP() since arc grenades don't create dangerous areas + } + + if( conversationName != "" ) + PlayGruntChatterMPLine( guy, conversationName ) + + #endif + #if GRUNTCHATTER_ENABLED + //SP Only + if ( NPC_GruntChatterSPEnabled( guy ) ) + GruntChatter_TryDisplacingFromDangerousArea( guy ) + #endif +} + +void function CodeDialogue_ReactSurprised( entity guy ) +{ + #if GRUNTCHATTER_ENABLED + if ( NPC_GruntChatterSPEnabled( guy ) ) + { + int aiSurprisedReactionType = guy.GetSurprisedReactionReason() + + switch ( aiSurprisedReactionType ) + { + case RSR_SIDE_FLANK: + case RSR_REAR_FLANK: + GruntChatter_TryGruntFlankedByPlayer( guy, aiSurprisedReactionType ) + break + } + } + #endif +} + +void function CodeDialogue_ManDown( entity guy ) +{ +} + +void function SetGlobalForcedDialogueOnly( bool value ) +{ + level.nv.forcedDialogueOnly = value +} + +void function SetPlayerForcedDialogueOnly( entity player, bool value ) +{ + player.SetForcedDialogueOnly( value ) +} + +void function Delayed_PlayConversationToPlayer( string conversation, entity player, float delay ) +{ + player.EndSignal( "OnDeath" ) + wait delay + PlayConversationToPlayer( conversation, player ) +} + +void function PlayConversationToPlayer( string conversationType, entity player ) +{ + if ( IsForcedDialogueOnly( player ) ) + { + printt( "ForcedDialogueOnly, not playing conversationType: " + conversationType ) + return + } + + PlayConversation_internal( conversationType, player ) +} + +void function PlayConversationToTeam( string conversationType, int team ) +{ + array<entity> playerArr = GetPlayerArrayOfTeam( team ) + foreach( player in playerArr ) + PlayConversationToPlayer( conversationType, player ) +} + +void function PlayConversationToTeamExceptPlayer( string conversationType, int team, entity excludePlayer ) +{ + array<entity> playerArr = GetPlayerArrayOfTeam( team ) + foreach( player in playerArr ) + { + if ( player == excludePlayer ) + continue + + PlayConversation_internal( conversationType, player ) + } +} + +void function PlayConversationToAll( string conversationType ) +{ + array<entity> playerArr = GetPlayerArray() + foreach( player in playerArr ) + PlayConversationToPlayer( conversationType, player ) +} + +void function PlayConversation_internal( string conversationType, entity player ) +{ + #if FACTION_DIALOGUE_ENABLED + return + #endif + + int conversationID = GetConversationIndex( conversationType ) + Remote_CallFunction_NonReplay( player, "ServerCallback_PlayConversation", conversationID ) +} + +void function ForcePlayConversationToAll( string conversationType ) +{ + array<entity> playerArr = GetPlayerArray() + foreach( player in playerArr ) + { + ForcePlayConversationToPlayer( conversationType, player ) + } +} + +void function ForcePlayConversationToTeam( string conversationType, team ) +{ + array<entity> playerArr = GetPlayerArrayOfTeam( team ) + foreach( player in playerArr ) + { + ForcePlayConversationToPlayer( conversationType, player ) + } +} + +//Like PlayConversation, but no checking for flags +void function ForcePlayConversationToPlayer( string conversationType, entity player ) +{ + PlayConversation_internal( conversationType, player ) +} + +array<entity> function GetNearbyFriendlyGrunts( vector origin, int team, range = null ) +{ + float searchRange = AI_CONVERSATION_RANGE + if ( range != null ) + searchRange = expect float( range ) + + array<entity> guys + array<entity> ai = GetNPCArrayEx( "npc_soldier", team, TEAM_ANY, origin, searchRange ) + foreach ( guy in ai ) + { + if ( IsAlive( guy ) ) + guys.append( guy ) + } + + return guys +} + +array<entity> function GetNearbyEnemyGrunts( vector origin, int team, range = null ) +{ + float searchRange = AI_CONVERSATION_RANGE + if ( range != null ) + searchRange = expect float( range ) + + array<entity> guys + array<entity> ai = GetNPCArrayEx( "npc_soldier", TEAM_ANY, team, origin, searchRange ) + foreach ( guy in ai ) + { + if ( IsAlive( guy ) ) + guys.append( guy ) + } + + return guys +} + +bool function SquadExistsForConversation( entity ai, string conversationType ) +{ + if ( !IsAlive( ai ) ) + return false + + // only soldiers play squad conversations + if ( !IsGrunt( ai ) ) + return false + + //Squadless AI don't play squad conversations + local squadName = ai.Get( "squadname" ) + if ( squadName == "" ) + return false + + // only all-soldier squads can use squad conversations + array<entity> squad = GetNPCArrayBySquad( squadName ) + if ( !squad.len() ) + return false + + bool foundNonSoldier = false + foreach ( guy in squad ) + { + if ( !IsGrunt( guy ) ) + { + foundNonSoldier = true + break + } + } + + if ( !(DoesConversationExist( conversationType ) )) + { + printt( "*****CONVERSATION WARNING***** Conversation " + conversationType + " does not exist! Returning" ) + return false + } + + return true +} + +function GetSquadEHandles( ai ) +{ + expect entity( ai ) + + local aiHandles = [ null, null, null, null ] + + string squadName = expect string( ai.Get( "squadname" ) ) + + if ( squadName == "" ) + return aiHandles + + array<entity> squad = GetNPCArrayBySquad( squadName ) + squad.fastremovebyvalue( ai ) + aiHandles[0] = ai.GetEncodedEHandle() + + int nextIdx = 1 + + foreach ( guy in squad ) + { + if ( !IsValid( guy ) ) + continue + + switch ( guy.GetClassName() ) + { + case "npc_soldier": + aiHandles[ nextIdx ] = guy.GetEncodedEHandle() + ++nextIdx + break + } + + if ( nextIdx >= aiHandles.len() ) + break + } + + return aiHandles +} + +void function PlaySquadConversationToPlayer( string conversationType, entity player, entity ai, float rangeSqr = AI_CONVERSATION_RANGE_SQR ) +{ + if ( SquadExistsForConversation( ai, conversationType ) ) + { + local aiHandles = GetSquadEHandles( ai ) + PlaySquadConversationToPlayer_Internal( conversationType, player, ai, rangeSqr, aiHandles ) + } +} + +// All PlaySquadConversation functions eventually funnel down to this. +// Funciton is broken apart from PlaySquadConversationToPlayer since PlaySquadConversationToPlayer has +// a few expensive checks that only need to be run once for every conversation we're trying to play, +// as opposed to for every player we're trying to play a conversation to. +void function PlaySquadConversationToPlayer_Internal( string conversationType, entity player, entity ai, float rangeSqr, aiHandles ) +{ + #if GRUNT_CHATTER_MP_ENABLED + return + #endif + + Assert( IsAlive( ai ), ai + " is dead." ) + Assert( aiHandles.len() == 4 ) + vector org = ai.GetOrigin() + float debounceTime = GetConversationDebounce( conversationType ) + float allowedTime = Time() - debounceTime + + // tell client to play conversation + int conversationID = GetConversationIndex( conversationType ) + if ( !ShouldPlaySquadConversation( player, conversationType, allowedTime, org, rangeSqr ) ) + return + + UpdateConversationTracking( player, conversationType, Time() ) + Remote_CallFunction_Replay( player, "ServerCallback_PlaySquadConversation", conversationID, aiHandles[0], aiHandles[1], aiHandles[2], aiHandles[3] ) +} + +void function PlaySquadConversation( string conversationType, entity ai ) +{ + PlaySquadConversationToAll( conversationType, ai ) +} + +void function PlaySquadConversationToAll( string conversationType, entity ai, float rangeSqr = AI_CONVERSATION_RANGE_SQR ) +{ + if ( !SquadExistsForConversation( ai, conversationType ) ) + return + + local aiHandles = GetSquadEHandles( ai ) + + array<entity> players = GetPlayerArray() + foreach ( player in players ) + { + PlaySquadConversationToPlayer_Internal( conversationType, player, ai, rangeSqr, aiHandles ) + } +} + +void function PlaySquadConversationToTeam( string conversationType, int team, entity ai, float rangeSqr = AI_CONVERSATION_RANGE_SQR ) +{ + if ( !SquadExistsForConversation( ai, conversationType ) ) + return + + local aiHandles = GetSquadEHandles( ai ) + + array<entity> players = GetPlayerArrayOfTeam( team ) + foreach ( player in players ) + { + PlaySquadConversationToPlayer_Internal( conversationType, player, ai, rangeSqr, aiHandles ) + } +} + +void function PlaySpectreChatterToAll( string conversationType, entity spectre, float rangeSqr = AI_CONVERSATION_RANGE_SQR ) +{ + PlaySpectreChatterToTeam( conversationType, TEAM_IMC, spectre, rangeSqr ) + PlaySpectreChatterToTeam( conversationType, TEAM_MILITIA, spectre, rangeSqr ) +} + +void function PlaySpectreChatterToTeam( string conversationType, team, entity spectre, float rangeSqr = AI_CONVERSATION_RANGE_SQR ) +{ + array<entity> players = GetPlayerArrayOfTeam( team ) + foreach ( player in players ) + { + PlaySpectreChatterToPlayer( conversationType, player, spectre, rangeSqr ) + } +} + +void function PlaySpectreChatterToPlayer( string conversationType, entity player, entity spectre, float rangeSqr = AI_CONVERSATION_RANGE_SQR ) +{ + //PrintFunc() + vector spectreOrigin = spectre.GetOrigin() + float debounceTime = DEFAULT_CONVERSATION_DEBOUNCE_TIME // Spectre conversations aren't as real as the Grunt ones- they don't get registered bc they just EmitSound + float allowedTime = Time() - debounceTime + + string teamSpecificSoundAlias = GetSpectreTeamSpecificSoundAlias( spectre, conversationType ) + + if ( teamSpecificSoundAlias == "" ) + // neutral AI don't have dialog + return + + Assert( DoesAliasExist( teamSpecificSoundAlias ) ) + + //printt( "Trying to play spectre chatter: " + teamSpecificSoundAlias + " to player: " + player) + if ( !ShouldPlaySquadConversation( player, teamSpecificSoundAlias, allowedTime, spectreOrigin, rangeSqr ) ) + return + + UpdateConversationTracking( player, teamSpecificSoundAlias, Time() ) + + EmitSoundOnEntityOnlyToPlayer( spectre, player, teamSpecificSoundAlias ) +} + +string function GetSpectreTeamSpecificSoundAlias( entity spectre, string partialConversationAlias ) +{ + int spectreTeam = spectre.GetTeam() + + if ( spectreTeam == TEAM_IMC ) + return "diag_imc_" + partialConversationAlias + else if ( spectreTeam == TEAM_MILITIA ) + return "diag_militia_" + partialConversationAlias + + return "" +} + +void function PlayConversationToAllExcept( string conversationType, array<entity> exceptions ) +{ + array<entity> playerArr = GetPlayerArray() + + table<entity, int> exceptionsTable + foreach( exceptionPlayer in exceptions ) + { + exceptionsTable[ exceptionPlayer ] <- 1 + } + + foreach ( player in playerArr ) + { + if ( player in exceptionsTable ) + continue + + PlayConversationToPlayer( conversationType, player ) + } +} + +void function CodeCallback_ScriptedDialogue( entity guy, int dialogueID ) +{ + Assert( dialogueID < file.codeDialogueFunc.len() ) + + if ( dialogueID in file.codeDialogueFunc ) + { + file.codeDialogueFunc[ dialogueID ]( guy ) + } +} + +function UpdateConversationTracking( player, conversationType, time ) +{ + if ( !(conversationType in player.s.lastAIConversationTime) ) + player.s.lastAIConversationTime[ conversationType ] <- time + else + player.s.lastAIConversationTime[ conversationType ] = time +} + +int function GetConversationIndex( string conversation ) +{ + Assert( conversation != "", "No conversation specified." ) + Assert( typeof(conversation) == "string" ) + return GetConversationToIndexTable()[ conversation ] +} + +void function CodeCallback_OnNPCLookAtHint( entity npc, entity hint ) +{ +} diff --git a/Northstar.CustomServers/scripts/vscripts/conversation/_faction_dialogue.gnut b/Northstar.CustomServers/scripts/vscripts/conversation/_faction_dialogue.gnut new file mode 100644 index 00000000..ccb5cd6e --- /dev/null +++ b/Northstar.CustomServers/scripts/vscripts/conversation/_faction_dialogue.gnut @@ -0,0 +1,46 @@ +global function FactionDialogue_Init +global function InitFactionDialoguePersistence +global function PlayFactionDialogueToPlayer +global function PlayFactionDialogueToTeam +global function PlayFactionDialogueToTeamExceptPlayer + +void function FactionDialogue_Init() +{ + AddCallback_OnClientConnected( AssignEnemyFactionToPlayer ) +} + +void function InitFactionDialoguePersistence( entity player ) +{ + // doesn't seem to be used? required to compile tho +} + +void function PlayFactionDialogueToPlayer( string conversationType, entity player ) +{ + #if !FACTION_DIALOGUE_ENABLED + return + #endif + + if ( !ShouldPlayFactionDialogue( conversationType, player ) ) + return + + int conversationIndex = GetConversationIndex( conversationType ) + Remote_CallFunction_NonReplay( player, "ServerCallback_PlayFactionDialogue", conversationIndex ) +} + +void function PlayFactionDialogueToTeam( string conversationType, int team ) +{ + foreach ( entity player in GetPlayerArrayOfTeam( team ) ) + PlayFactionDialogueToPlayer( conversationType, player ) +} + +void function PlayFactionDialogueToTeamExceptPlayer( string conversationType, int team, entity except ) +{ + foreach ( entity player in GetPlayerArrayOfTeam( team ) ) + if ( player != except ) + PlayFactionDialogueToPlayer( conversationType, player ) +} + +void function AssignEnemyFactionToPlayer( entity player ) +{ + AssignEnemyFaction( player, expect string( player.GetPersistentVar( "factionChoice" ) ) ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/scripts/vscripts/conversation/_grunt_chatter_mp.gnut b/Northstar.CustomServers/scripts/vscripts/conversation/_grunt_chatter_mp.gnut new file mode 100644 index 00000000..b638e92b --- /dev/null +++ b/Northstar.CustomServers/scripts/vscripts/conversation/_grunt_chatter_mp.gnut @@ -0,0 +1,18 @@ +global function GruntChatter_MP_Init +global function PlayGruntChatterMPLine + +void function GruntChatter_MP_Init() +{ + //ShGruntChatter_MP_Init() +} + +void function PlayGruntChatterMPLine( entity grunt, string conversationType ) +{ + #if !GRUNT_CHATTER_MP_ENABLED + return + #endif + + foreach ( entity player in GetPlayerArray() ) + if ( ShouldPlayGruntChatterMPLine( conversationType, player, grunt ) ) + Remote_CallFunction_NonReplay( player, "ServerCallback_PlayGruntChatterMP", GetConversationIndex( conversationType ), grunt.GetEncodedEHandle() ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/scripts/vscripts/conversation/_spectre_chatter_mp.gnut b/Northstar.CustomServers/scripts/vscripts/conversation/_spectre_chatter_mp.gnut new file mode 100644 index 00000000..2f9e0f84 --- /dev/null +++ b/Northstar.CustomServers/scripts/vscripts/conversation/_spectre_chatter_mp.gnut @@ -0,0 +1,18 @@ +global function SpectreChatter_MP_Init +global function PlaySpectreChatterMPLine + +void function SpectreChatter_MP_Init() +{ + //ShSpectreChatter_MP_Init() +} + +void function PlaySpectreChatterMPLine( entity spectre, string conversationType ) +{ + #if !SPECTRE_CHATTER_MP_ENABLED + return + #endif + + foreach ( entity player in GetPlayerArray() ) + if ( ShouldPlaySpectreChatterMPLine( conversationType, player, spectre ) ) + Remote_CallFunction_NonReplay( player, "ServerCallback_PlaySpectreChatterMP", GetConversationIndex( conversationType ), spectre.GetEncodedEHandle() ) +}
\ No newline at end of file |