EditorSpawnerComponent.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorSpawnerComponent.h"
  9. #include "SpawnerComponent.h"
  10. #include <QMessageBox>
  11. #include <QApplication>
  12. #include <AzCore/Asset/AssetSerializer.h>
  13. #include <AzCore/Serialization/EditContext.h>
  14. #include <AzCore/Slice/SliceComponent.h>
  15. #include <AzFramework/Entity/EntityContextBus.h>
  16. #include <AzFramework/Slice/SliceEntityBus.h>
  17. namespace LmbrCentral
  18. {
  19. void EditorSpawnerComponent::Reflect(AZ::ReflectContext* context)
  20. {
  21. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  22. if (serializeContext)
  23. {
  24. serializeContext->Class<EditorSpawnerComponent, AZ::Component>()
  25. ->Version(1)
  26. ->Field("Slice", &EditorSpawnerComponent::m_sliceAsset)
  27. ->Field("SpawnOnActivate", &EditorSpawnerComponent::m_spawnOnActivate)
  28. ->Field("DestroyOnDeactivate", &EditorSpawnerComponent::m_destroyOnDeactivate)
  29. ;
  30. AZ::EditContext* editContext = serializeContext->GetEditContext();
  31. if (editContext)
  32. {
  33. editContext->Class<EditorSpawnerComponent>("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")
  34. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  35. ->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
  36. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Spawner.svg")
  37. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Spawner.svg")
  38. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  39. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  40. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorSpawnerComponent::m_sliceAsset, "Dynamic slice", "The slice to spawn")
  41. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSpawnerComponent::SliceAssetChanged)
  42. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorSpawnerComponent::m_spawnOnActivate, "Spawn on activate",
  43. "Should the component spawn the selected slice upon activation?")
  44. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSpawnerComponent::SpawnOnActivateChanged)
  45. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorSpawnerComponent::m_destroyOnDeactivate, "Destroy on deactivate",
  46. "Upon deactivation, should the component destroy any slices it spawned?")
  47. ;
  48. }
  49. }
  50. }
  51. void EditorSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  52. {
  53. SpawnerComponent::GetProvidedServices(services);
  54. }
  55. void EditorSpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  56. {
  57. SpawnerComponent::GetRequiredServices(services);
  58. }
  59. void EditorSpawnerComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  60. {
  61. SpawnerComponent::GetDependentServices(services);
  62. }
  63. bool EditorSpawnerComponent::HasInfiniteLoop()
  64. {
  65. // 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
  66. AZ::SliceComponent::SliceInstanceAddress sliceInstanceAddress;
  67. AzFramework::SliceEntityRequestBus::EventResult(sliceInstanceAddress, GetEntityId(),
  68. &AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
  69. if (m_spawnOnActivate && sliceInstanceAddress.GetReference())
  70. {
  71. // Compare the ids because one is source and the other is going to be the dynamic slice
  72. return m_sliceAsset.GetId().m_guid == sliceInstanceAddress.GetReference()->GetSliceAsset().GetId().m_guid;
  73. }
  74. return false;
  75. }
  76. AZ::u32 EditorSpawnerComponent::SliceAssetChanged()
  77. {
  78. if (HasInfiniteLoop())
  79. {
  80. QMessageBox(QMessageBox::Warning,
  81. "Input Error",
  82. "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!",
  83. QMessageBox::Ok, QApplication::activeWindow()).exec();
  84. m_sliceAsset = AZ::Data::Asset<AZ::DynamicSliceAsset>();
  85. // 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.
  86. // Once LY-71192 (and the other variants) are fixed, this can be changed to ::ValuesOnly
  87. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  88. }
  89. return AZ::Edit::PropertyRefreshLevels::None;
  90. }
  91. AZ::u32 EditorSpawnerComponent::SpawnOnActivateChanged()
  92. {
  93. if (HasInfiniteLoop())
  94. {
  95. QMessageBox(QMessageBox::Warning,
  96. "Input Error",
  97. "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!",
  98. QMessageBox::Ok, QApplication::activeWindow()).exec();
  99. m_spawnOnActivate = false;
  100. return AZ::Edit::PropertyRefreshLevels::ValuesOnly;
  101. }
  102. return AZ::Edit::PropertyRefreshLevels::None;
  103. }
  104. bool EditorSpawnerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  105. {
  106. if (auto config = azrtti_cast<const SpawnerConfig*>(baseConfig))
  107. {
  108. m_sliceAsset = config->m_sliceAsset;
  109. m_spawnOnActivate = config->m_spawnOnActivate;
  110. m_destroyOnDeactivate = config->m_destroyOnDeactivate;
  111. return true;
  112. }
  113. return false;
  114. }
  115. bool EditorSpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  116. {
  117. if (auto config = azrtti_cast<SpawnerConfig*>(outBaseConfig))
  118. {
  119. config->m_sliceAsset = m_sliceAsset;
  120. config->m_spawnOnActivate = m_spawnOnActivate;
  121. config->m_destroyOnDeactivate = m_destroyOnDeactivate;
  122. return true;
  123. }
  124. return false;
  125. }
  126. void EditorSpawnerComponent::BuildGameEntity(AZ::Entity* gameEntity)
  127. {
  128. // Add corresponding gameComponent to gameEntity.
  129. auto gameComponent = gameEntity->CreateComponent<SpawnerComponent>();
  130. SpawnerConfig config;
  131. config.m_sliceAsset = m_sliceAsset;
  132. config.m_spawnOnActivate = m_spawnOnActivate;
  133. config.m_destroyOnDeactivate = m_destroyOnDeactivate;
  134. gameComponent->SetConfiguration(config);
  135. }
  136. } // namespace LmbrCentral