/* * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include AZ_CVAR(bool, mps_enableMissingAudioTriggerWarnings, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Reports warnings whenever a game effect is missing an audio trigger defined in Game Playe Effects component."); namespace MultiplayerSample { void GameplayEffectsComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class() ->Version(1); } GameplayEffectsComponentBase::Reflect(context); } void GameplayEffectsComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { if (IsNetEntityRoleClient()) { LocalOnlyGameplayEffectsNotificationBus::Handler::BusConnect(); } m_soundTriggerNames.clear(); m_soundTriggerNames.resize(SoundEffectNamespace::SoundEffectCount); m_soundTriggerNames[aznumeric_cast(SoundEffect::PlayerFootSteps)] = GetPlayerFootSteps(); m_soundTriggerNames[aznumeric_cast(SoundEffect::PlayerExertion)] = GetPlayerExertion(); m_soundTriggerNames[aznumeric_cast(SoundEffect::PlayerKnockedDown)] = GetPlayerKnockedDown(); m_soundTriggerNames[aznumeric_cast(SoundEffect::ArmorBreaking)] = GetArmorBreaking(); m_soundTriggerNames[aznumeric_cast(SoundEffect::ArmorMend)] = GetArmorMend(); m_soundTriggerNames[aznumeric_cast(SoundEffect::PlayerOuch)] = GetPlayerOuch(); m_soundTriggerNames[aznumeric_cast(SoundEffect::LadderClimb)] = GetLadderClimb(); m_soundTriggerNames[aznumeric_cast(SoundEffect::ShutDown)] = GetShutDown(); m_soundTriggerNames[aznumeric_cast(SoundEffect::CountDown)] = GetCountDown(); m_soundTriggerNames[aznumeric_cast(SoundEffect::GemPickup)] = GetGemPickup(); m_soundTriggerNames[aznumeric_cast(SoundEffect::VictoryFanfare)] = GetVictoryFanfare(); m_soundTriggerNames[aznumeric_cast(SoundEffect::LosingFanfare)] = GetLosingFanfare(); m_soundTriggerNames[aznumeric_cast(SoundEffect::RoundStart)] = GetRoundStart(); m_soundTriggerNames[aznumeric_cast(SoundEffect::RoundEnd)] = GetRoundEnd(); m_soundTriggerNames[aznumeric_cast(SoundEffect::GameEnd)] = GetGameEnd(); m_soundTriggerNames[aznumeric_cast(SoundEffect::LaserPistolMuzzleFlash)] = GetLaserPistolMuzzleFlash(); m_soundTriggerNames[aznumeric_cast(SoundEffect::LaserPistolImpact)] = GetLaserPistolImpact(); m_soundTriggerNames[aznumeric_cast(SoundEffect::BubbleGunBuildup)] = GetBubbleGunBuildup(); m_soundTriggerNames[aznumeric_cast(SoundEffect::BubbleGunMuzzleFlash)] = GetBubbleGunMuzzleFlash(); m_soundTriggerNames[aznumeric_cast(SoundEffect::BubbleGunProjectile)] = GetBubbleGunProjectile(); m_soundTriggerNames[aznumeric_cast(SoundEffect::BubbleGunImpact)] = GetBubbleGunImpact(); m_soundTriggerNames[aznumeric_cast(SoundEffect::JumpPadLaunch)] = GetJumpPadLaunch(); m_soundTriggerNames[aznumeric_cast(SoundEffect::TeleporterUse)] = GetTeleporterUse(); m_soundTriggerNames[aznumeric_cast(SoundEffect::EnergyBallTrapRisingOutOfTheGround)] = GetEnergyBallTrapRisingOutOfTheGround(); m_soundTriggerNames[aznumeric_cast(SoundEffect::EnergyBallTrapBuildup)] = GetEnergyBallTrapBuildup(); m_soundTriggerNames[aznumeric_cast(SoundEffect::EnergyBallTrapProjectile)] = GetEnergyBallTrapProjectile(); m_soundTriggerNames[aznumeric_cast(SoundEffect::EnergyBallTrapImpact)] = GetEnergyBallTrapImpact(); m_soundTriggerNames[aznumeric_cast(SoundEffect::EnergyBallTrapOnCooldown)] = GetEnergyBallTrapOnCooldown(); } void GameplayEffectsComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { Audio::AudioTriggerNotificationBus::MultiHandler::BusDisconnect(); LocalOnlyGameplayEffectsNotificationBus::Handler::BusDisconnect(); } #if AZ_TRAIT_CLIENT void GameplayEffectsComponent::HandleRPC_OnEffect([[maybe_unused]] AzNetworking::IConnection* invokingConnection, const SoundEffect& effect) { // Spawn at the local camera position AZ::EntityId camera; Camera::CameraSystemRequestBus::BroadcastResult(camera, &Camera::CameraSystemRequestBus::Events::GetActiveCamera); if (camera.IsValid()) { AZ::Vector3 cameraPosition = AZ::Vector3::CreateZero(); AZ::TransformBus::EventResult(cameraPosition, camera, &AZ::TransformBus::Events::GetWorldTranslation); SpawnEffect(effect, cameraPosition); } } void GameplayEffectsComponent::HandleRPC_OnPositionalEffect([[maybe_unused]] AzNetworking::IConnection* invokingConnection, const SoundEffect& effect, const AZ::Vector3& soundLocation) { SpawnEffect(effect, soundLocation); } #endif void GameplayEffectsComponent::ReportTriggerFinished([[maybe_unused]] Audio::TAudioControlID triggerId) { const Audio::TriggerNotificationIdType busId = *Audio::AudioTriggerNotificationBus::GetCurrentBusId(); const AZ::EntityId entityId = aznumeric_cast(busId.m_owner); Audio::AudioTriggerNotificationBus::MultiHandler::BusDisconnect(busId); // Remove the prefab that played the sound trigger const auto iterator = m_spawnedEffects.find(entityId); if (iterator != m_spawnedEffects.end()) { m_spawnedEffects.erase(iterator); } } #if AZ_TRAIT_CLIENT void GameplayEffectsComponent::OnPositionalEffect(SoundEffect effect, const AZ::Vector3& position) { HandleRPC_OnPositionalEffect(nullptr, effect, position); } void GameplayEffectsComponent::OnEffect(SoundEffect effect) { HandleRPC_OnEffect(nullptr, effect); } #endif void GameplayEffectsComponent::SpawnEffect(SoundEffect effect, const AZ::Vector3& position) { if (effect == SoundEffect::Unused) { return; } const char* triggerName = nullptr; const AZStd::size_t effectId = aznumeric_cast(effect); if (effectId < m_soundTriggerNames.size()) { triggerName = m_soundTriggerNames[effectId].c_str(); } if (!triggerName || strlen(triggerName) == 0) { if (mps_enableMissingAudioTriggerWarnings) { const AZStd::string eventName(SoundEffectNamespace::ToString(effect)); AZ_Warning("MultiplayerSample", false, "Audio trigger wasn't specified for effect [%s] on GameplayEffectsComponent", eventName.c_str()); } return; } PrefabCallbacks callbacks; callbacks.m_onActivateCallback = [this, triggerName]( AZStd::shared_ptr&& ticket, [[maybe_unused]] AzFramework::SpawnableConstEntityContainerView view) { for (const AZ::Entity* entity : view) { LmbrCentral::AudioTriggerComponentRequests* audioTrigger = LmbrCentral::AudioTriggerComponentRequestBus::FindFirstHandler(entity->GetId()); if (audioTrigger) { Audio::AudioTriggerNotificationBus::MultiHandler::BusConnect(Audio::TriggerNotificationIdType{ entity->GetId() }); m_spawnedEffects.emplace(entity->GetId(), move(ticket)); audioTrigger->ExecuteTrigger(triggerName); break; } } }; // Spawn a prefab with an audio trigger, play the effect and clean it up when it finishes. const AZ::Transform t = AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), position); GetNetworkPrefabSpawnerComponent()->SpawnDefaultPrefab(t, callbacks); } GameplayEffectsComponentController::GameplayEffectsComponentController(GameplayEffectsComponent& parent) : GameplayEffectsComponentControllerBase(parent) { } void GameplayEffectsComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { GameplayEffectsNotificationBus::Handler::BusConnect(); } void GameplayEffectsComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { GameplayEffectsNotificationBus::Handler::BusDisconnect(); } #if AZ_TRAIT_SERVER void GameplayEffectsComponentController::OnEffect(SoundEffect effect) { RPC_OnEffect(effect); } void GameplayEffectsComponentController::OnPositionalEffect(SoundEffect effect, const AZ::Vector3& position) { RPC_OnPositionalEffect(effect, position); } #endif }