/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "LmbrCentral_precompiled.h" #include "EditorSpawnerComponent.h" #include "SpawnerComponent.h" #include #include #include namespace LmbrCentral { void EditorSpawnerComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class() ->Version(1) ->Field("Slice", &EditorSpawnerComponent::m_sliceAsset) ->Field("SpawnOnActivate", &EditorSpawnerComponent::m_spawnOnActivate) ->Field("DestroyOnDeactivate", &EditorSpawnerComponent::m_destroyOnDeactivate) ; AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { editContext->Class("Spawner", "The Spawner component allows an entity to spawn a design-time or run-time dynamic slice (*.dynamicslice) at the entity's location with an optional offset") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Gameplay") ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/Spawner.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/Spawner.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-spawner.html") ->DataElement(0, &EditorSpawnerComponent::m_sliceAsset, "Dynamic slice", "The slice to spawn") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSpawnerComponent::SliceAssetChanged) ->DataElement(0, &EditorSpawnerComponent::m_spawnOnActivate, "Spawn on activate", "Should the component spawn the selected slice upon activation?") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSpawnerComponent::SpawnOnActivateChanged) ->DataElement(0, &EditorSpawnerComponent::m_destroyOnDeactivate, "Destroy on deactivate", "Upon deactivation, should the component destroy any slices it spawned?") ; } } } void EditorSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) { SpawnerComponent::GetProvidedServices(services); } void EditorSpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) { SpawnerComponent::GetRequiredServices(services); } void EditorSpawnerComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services) { SpawnerComponent::GetDependentServices(services); } bool EditorSpawnerComponent::HasInfiniteLoop() { // If we are set to spawn on activate, then we need to make sure we don't point to ourself or we create an infinite spawn loop AZ::SliceComponent::SliceInstanceAddress sliceInstanceAddress; AzFramework::EntityIdContextQueryBus::EventResult(sliceInstanceAddress, GetEntityId(), &AzFramework::EntityIdContextQueryBus::Events::GetOwningSlice); if (m_spawnOnActivate && sliceInstanceAddress.GetReference()) { // Compare the ids because one is source and the other is going to be the dynamic slice return m_sliceAsset.GetId().m_guid == sliceInstanceAddress.GetReference()->GetSliceAsset().GetId().m_guid; } return false; } AZ::u32 EditorSpawnerComponent::SliceAssetChanged() { if (HasInfiniteLoop()) { QMessageBox(QMessageBox::Warning, "Input Error", "Your spawner is set to Spawn on Activate. You cannot set the spawner to spawn a dynamic slice that contains this entity or it will spawn infinitely!", QMessageBox::Ok, QApplication::activeWindow()).exec(); m_sliceAsset = AZ::Data::Asset(); // We have to refresh entire tree to update the asset control until the bug is fixed. Just refreshing values does not properly update the UI. // Once LY-71192 (and the other variants) are fixed, this can be changed to ::ValuesOnly return AZ::Edit::PropertyRefreshLevels::EntireTree; } return AZ::Edit::PropertyRefreshLevels::None; } AZ::u32 EditorSpawnerComponent::SpawnOnActivateChanged() { if (HasInfiniteLoop()) { QMessageBox(QMessageBox::Warning, "Input Error", "Your spawner is set to spawn a dynamic slice that contains this entity. You cannot set the spawner to be Spawn on Activate or it will spawn infinitely!", QMessageBox::Ok, QApplication::activeWindow()).exec(); m_spawnOnActivate = false; return AZ::Edit::PropertyRefreshLevels::ValuesOnly; } return AZ::Edit::PropertyRefreshLevels::None; } bool EditorSpawnerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) { if (auto config = azrtti_cast(baseConfig)) { m_sliceAsset = config->m_sliceAsset; m_spawnOnActivate = config->m_spawnOnActivate; m_destroyOnDeactivate = config->m_destroyOnDeactivate; return true; } return false; } bool EditorSpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const { if (auto config = azrtti_cast(outBaseConfig)) { config->m_sliceAsset = m_sliceAsset; config->m_spawnOnActivate = m_spawnOnActivate; config->m_destroyOnDeactivate = m_destroyOnDeactivate; return true; } return false; } void EditorSpawnerComponent::BuildGameEntity(AZ::Entity* gameEntity) { // Add corresponding gameComponent to gameEntity. auto gameComponent = gameEntity->CreateComponent(); SpawnerConfig config; config.m_sliceAsset = m_sliceAsset; config.m_spawnOnActivate = m_spawnOnActivate; config.m_destroyOnDeactivate = m_destroyOnDeactivate; gameComponent->SetConfiguration(config); } } // namespace LmbrCentral