Browse Source

Add gem spawning on death (#299)

* Fix player teleports by using the correct physics bus.

Signed-off-by: Mike Balfour <[email protected]>

* Add RPCs for spawning gems

Signed-off-by: Mike Balfour <[email protected]>

* Add logic to the match component to spawn gems on player death.
Also clears out gems on round end as a minor polish item.

Signed-off-by: Mike Balfour <[email protected]>

* Configured gem spawn on death

Signed-off-by: Mike Balfour <[email protected]>

* Addressed PR feedback

Signed-off-by: Mike Balfour <[email protected]>

---------

Signed-off-by: Mike Balfour <[email protected]>
Mike Balfour 2 năm trước cách đây
mục cha
commit
74b98f8be8

+ 16 - 0
Gem/Code/Source/AutoGen/GemSpawnerComponent.AutoComponent.xml

@@ -24,4 +24,20 @@
 
 
     <ArchetypeProperty Type="AZStd::string" Name="GemSpawnTag"
     <ArchetypeProperty Type="AZStd::string" Name="GemSpawnTag"
                        ExposeToEditor="true" Description="A tag value for entities the describe the location of gems spawn points." />
                        ExposeToEditor="true" Description="A tag value for entities the describe the location of gems spawn points." />
+
+    <RemoteProcedure Name="RPC_SpawnGem" InvokeFrom="Server" HandleOn="Authority" 
+                     IsPublic="true" IsReliable="true" GenerateEventBindings="true" Description="Spawn a gem at a specific location">
+      <Param Type="Multiplayer::NetEntityId" Name="PlayerEntity"/>
+      <Param Type="AZ::Vector3" Name="SpawnLocation"/>
+      <Param Type="AZStd::string" Name="GemTag"/>
+    </RemoteProcedure>
+
+  <RemoteProcedure Name="RPC_SpawnGemWithValue" InvokeFrom="Server" HandleOn="Authority"
+                   IsPublic="true" IsReliable="true" GenerateEventBindings="true" Description="Spawn a gem at a specific location with the given value">
+    <Param Type="Multiplayer::NetEntityId" Name="PlayerEntity"/>
+    <Param Type="AZ::Vector3" Name="SpawnLocation"/>
+    <Param Type="AZStd::string" Name="GemTag"/>
+    <Param Type="uint16_t" Name="GemValue"/>
+  </RemoteProcedure>
+
 </Component>
 </Component>

+ 1 - 0
Gem/Code/Source/AutoGen/NetworkMatchComponent.AutoComponent.xml

@@ -29,6 +29,7 @@
     <ArchetypeProperty Type="float"  Name="RoundDuration"  Init="120.f" ExposeToEditor="true" Description="Total time of a round in seconds" />
     <ArchetypeProperty Type="float"  Name="RoundDuration"  Init="120.f" ExposeToEditor="true" Description="Total time of a round in seconds" />
     <ArchetypeProperty Type="uint16_t"  Name="TotalRounds"  Init="3" ExposeToEditor="true" Description="Total number of rounds" />
     <ArchetypeProperty Type="uint16_t"  Name="TotalRounds"  Init="3" ExposeToEditor="true" Description="Total number of rounds" />
     <ArchetypeProperty Type="uint16_t"  Name="RespawnPenaltyPercent" Init="50" ExposeToEditor="true" Description="Percent of score to deduct on armor depletion"/>
     <ArchetypeProperty Type="uint16_t"  Name="RespawnPenaltyPercent" Init="50" ExposeToEditor="true" Description="Percent of score to deduct on armor depletion"/>
+    <ArchetypeProperty Type="AZStd::string"  Name="RespawnGemTag" ExposeToEditor="true" Description="The type of gem to spawn on armor depletion" />
     <ArchetypeProperty Type="float"  Name="RestDurationBetweenRounds"  Init="10.0f" ExposeToEditor="true" Description="The time between rounds to rest and look at the score." />
     <ArchetypeProperty Type="float"  Name="RestDurationBetweenRounds"  Init="10.0f" ExposeToEditor="true" Description="The time between rounds to rest and look at the score." />
 
 
     <RemoteProcedure Name="RPC_EndMatch" InvokeFrom="Authority" HandleOn="Client" IsPublic="false" IsReliable="true"
     <RemoteProcedure Name="RPC_EndMatch" InvokeFrom="Authority" HandleOn="Client" IsPublic="false" IsReliable="true"

+ 42 - 12
Gem/Code/Source/Components/Multiplayer/GemSpawnerComponent.cpp

@@ -162,28 +162,58 @@ namespace MultiplayerSample
         }
         }
     }
     }
 
 
-    void GemSpawnerComponentController::SpawnGem(const AZ::Vector3& location, const AZ::Crc32& type)
+    void GemSpawnerComponentController::HandleRPC_SpawnGem(
+        [[maybe_unused]] AzNetworking::IConnection* invokingConnection, 
+        [[maybe_unused]] const Multiplayer::NetEntityId& playerEntity, const AZ::Vector3& spawnLocation, const AZStd::string& gemTag)
     {
     {
-        if (GetParent().GetGemSpawnables().empty())
-        {
-            return;
+        SpawnGem(spawnLocation, AZ::Crc32(gemTag));
+    }
+
+    void GemSpawnerComponentController::HandleRPC_SpawnGemWithValue(
+        [[maybe_unused]] AzNetworking::IConnection* invokingConnection,
+        [[maybe_unused]] const Multiplayer::NetEntityId& playerEntity, 
+        const AZ::Vector3& spawnLocation, const AZStd::string& gemTag, const uint16_t& gemValue)
+    {
+        
+        if (auto gemEntry = GetGemSpawnable(AZ::Crc32(gemTag)); gemEntry)
+        { 
+            // Spawn the gem with the max value between what's requested and what's in the gem table.
+            uint16_t value = AZStd::max(gemEntry->m_scoreValue, gemValue);
+            SpawnGem(spawnLocation, gemEntry->m_gemAsset, value);
         }
         }
+    }
 
 
-        const GemSpawnable* spawnable = nullptr;
-        for (const GemSpawnable& gemType : GetParent().GetGemSpawnables())
+    AZStd::optional<const GemSpawnable> GemSpawnerComponentController::GetGemSpawnable(AZ::Crc32 gemTag) const
+    {
+        for (const GemSpawnable gemType : GetParent().GetGemSpawnables())
         {
         {
-            if (type == AZ::Crc32(gemType.m_tag.c_str()))
+            if (gemTag == AZ::Crc32(gemType.m_tag.c_str()))
             {
             {
-                spawnable = &gemType;
+                return gemType;
             }
             }
         }
         }
-        if (!spawnable)
+
+        return {};
+    }
+
+    void GemSpawnerComponentController::SpawnGem(const AZ::Vector3& location, const AZ::Crc32& type)
+    {
+        if (auto gemEntry = GetGemSpawnable(type); gemEntry)
+        {
+            SpawnGem(location, gemEntry->m_gemAsset, gemEntry->m_scoreValue);
+        }
+    }
+
+    void GemSpawnerComponentController::SpawnGem(const AZ::Vector3& location, const AzFramework::SpawnableAsset& gemAsset, uint16_t gemValue)
+    {
+        // Don't spawn gems with 0 value.
+        if (gemValue == 0)
         {
         {
             return;
             return;
         }
         }
 
 
         PrefabCallbacks callbacks;
         PrefabCallbacks callbacks;
-        callbacks.m_onActivateCallback = [this, spawnable](AZStd::shared_ptr<AzFramework::EntitySpawnTicket> ticket,
+        callbacks.m_onActivateCallback = [this, gemValue](AZStd::shared_ptr<AzFramework::EntitySpawnTicket> ticket,
             AzFramework::SpawnableConstEntityContainerView view)
             AzFramework::SpawnableConstEntityContainerView view)
         {
         {
             if (view.empty())
             if (view.empty())
@@ -200,7 +230,7 @@ namespace MultiplayerSample
                     if (GemComponentController* gemController = static_cast<GemComponentController*>(gem->GetController()))
                     if (GemComponentController* gemController = static_cast<GemComponentController*>(gem->GetController()))
                     {
                     {
                         gemController->SetRandomPeriodOffset(GetNetworkRandomComponentController()->GetRandomInt() % 1000);
                         gemController->SetRandomPeriodOffset(GetNetworkRandomComponentController()->GetRandomInt() % 1000);
-                        gemController->SetGemScoreValue(spawnable->m_scoreValue);
+                        gemController->SetGemScoreValue(gemValue);
                         gemController->SetGemSpawnerController(this);
                         gemController->SetGemSpawnerController(this);
                     }
                     }
                 }
                 }
@@ -213,7 +243,7 @@ namespace MultiplayerSample
 
 
         GetParent().GetNetworkPrefabSpawnerComponent()->SpawnPrefabAsset(
         GetParent().GetNetworkPrefabSpawnerComponent()->SpawnPrefabAsset(
             AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), location),
             AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateIdentity(), location),
-            spawnable->m_gemAsset, AZStd::move(callbacks));
+            gemAsset, AZStd::move(callbacks));
     }
     }
 
 
     void GemSpawnerComponentController::RemoveGems()
     void GemSpawnerComponentController::RemoveGems()

+ 11 - 3
Gem/Code/Source/Components/Multiplayer/GemSpawnerComponent.h

@@ -38,15 +38,23 @@ namespace MultiplayerSample
 
 
 #if AZ_TRAIT_SERVER
 #if AZ_TRAIT_SERVER
         void SpawnGems();
         void SpawnGems();
+        void SpawnGem(const AZ::Vector3& location, const AZ::Crc32& type);
         void RemoveGem(AzFramework::EntitySpawnTicket::Id gemTicketId);
         void RemoveGem(AzFramework::EntitySpawnTicket::Id gemTicketId);
+        void RemoveGems();
+
+        void HandleRPC_SpawnGem(
+            AzNetworking::IConnection* invokingConnection, const Multiplayer::NetEntityId& playerEntity, 
+            const AZ::Vector3& spawnLocation, const AZStd::string& gemTag) override;
+        void HandleRPC_SpawnGemWithValue(
+            AzNetworking::IConnection* invokingConnection, const Multiplayer::NetEntityId& playerEntity, 
+            const AZ::Vector3& spawnLocation, const AZStd::string& gemTag, const uint16_t& gemValue) override;
 #endif
 #endif
 
 
     private:
     private:
 #if AZ_TRAIT_SERVER
 #if AZ_TRAIT_SERVER
-        void SpawnGem(const AZ::Vector3& location, const AZ::Crc32& type);
-        void RemoveGems();
+        AZStd::optional<const GemSpawnable> GetGemSpawnable(AZ::Crc32 gemTag) const;
+        void SpawnGem(const AZ::Vector3& location, const AzFramework::SpawnableAsset& gemAsset, uint16_t gemValue);
 #endif
 #endif
-        
         AZStd::unordered_map<AzFramework::EntitySpawnTicket::Id, AZStd::shared_ptr<AzFramework::EntitySpawnTicket>> m_spawnedGems;
         AZStd::unordered_map<AzFramework::EntitySpawnTicket::Id, AZStd::shared_ptr<AzFramework::EntitySpawnTicket>> m_spawnedGems;
 
 
         struct GemSpawnEntry
         struct GemSpawnEntry

+ 38 - 0
Gem/Code/Source/Components/NetworkMatchComponent.cpp

@@ -5,6 +5,7 @@
  *
  *
  */
  */
 
 
+#include <AzCore/Component/TransformBus.h>
 #include <AzCore/Preprocessor/EnumReflectUtils.h>
 #include <AzCore/Preprocessor/EnumReflectUtils.h>
 
 
 #include <GameplayEffectsNotificationBus.h>
 #include <GameplayEffectsNotificationBus.h>
@@ -14,6 +15,7 @@
 #include <GameState/GameStateMatchEnded.h>
 #include <GameState/GameStateMatchEnded.h>
 #include <GameState/GameStateMatchInProgress.h>
 #include <GameState/GameStateMatchInProgress.h>
 #include <GameState/GameStatePreparingMatch.h>
 #include <GameState/GameStatePreparingMatch.h>
+#include <Source/Components/Multiplayer/GemSpawnerComponent.h>
 #include <Source/Components/Multiplayer/MatchPlayerCoinsComponent.h>
 #include <Source/Components/Multiplayer/MatchPlayerCoinsComponent.h>
 #include <Source/Components/Multiplayer/PlayerIdentityComponent.h>
 #include <Source/Components/Multiplayer/PlayerIdentityComponent.h>
 #include <Source/Components/NetworkTeleportCompatibleComponent.h>
 #include <Source/Components/NetworkTeleportCompatibleComponent.h>
@@ -437,6 +439,9 @@ namespace MultiplayerSample
 
 
     void NetworkMatchComponentController::EndRound()
     void NetworkMatchComponentController::EndRound()
     {
     {
+        // As soon as a round ends, remove all the gems until the next round begins.
+        GetGemSpawnerComponentController()->RemoveGems();
+
         // Check if we're in-between rounds, or if this is the end of the match...
         // Check if we're in-between rounds, or if this is the end of the match...
         if (GetRoundNumber() < GetTotalRounds()) // In-between
         if (GetRoundNumber() < GetTotalRounds()) // In-between
         {
         {
@@ -504,7 +509,40 @@ namespace MultiplayerSample
         {
         {
             if (Multiplayer::ConstNetworkEntityHandle playerHandle = Multiplayer::GetNetworkEntityManager()->GetEntity(playerEntity))
             if (Multiplayer::ConstNetworkEntityHandle playerHandle = Multiplayer::GetNetworkEntityManager()->GetEntity(playerEntity))
             {
             {
+                AZ::Vector3 playerTranslation = playerHandle.Exists() 
+                    ? playerHandle.GetEntity()->GetTransform()->GetWorldTranslation() 
+                    : AZ::Vector3::CreateZero();
                 RespawnPlayer(playerEntity, PlayerResetOptions{ true, GetRespawnPenaltyPercent() });
                 RespawnPlayer(playerEntity, PlayerResetOptions{ true, GetRespawnPenaltyPercent() });
+                if (playerHandle.Exists())
+                {
+                    MultiplayerSample::GemSpawnerComponent* gemSpawnerComponent = GetParent().GetGemSpawnerComponent();
+
+                    if (gemSpawnerComponent)
+                    {
+                        const AZStd::vector<PlayerCoinState>& coinStates = GetMatchPlayerCoinsComponentController()->GetParent().
+                            GetPlayerCoinCounts();
+
+                        const auto coinStateIterator = AZStd::find_if(
+                            coinStates.begin(), coinStates.end(), [playerEntity](const PlayerCoinState& state)
+                            {
+                                return state.m_playerId == playerEntity;
+                            });
+
+                        if (coinStateIterator != coinStates.end())
+                        {
+                            float coinsDropped = coinStateIterator->m_coins * (GetRespawnPenaltyPercent() * 0.01f);
+
+                            gemSpawnerComponent->RPC_SpawnGemWithValue(
+                                playerEntity, playerTranslation, GetRespawnGemTag(), static_cast<uint16_t>(coinsDropped));
+                        }
+                        else
+                        {
+                            gemSpawnerComponent->RPC_SpawnGem(
+                                playerEntity, playerTranslation, GetRespawnGemTag());
+                        }
+                            
+                    }
+                }
             }
             }
         }
         }
         else
         else

+ 3 - 3
Gem/Code/Source/Components/NetworkTeleportCompatibleComponent.cpp

@@ -10,7 +10,7 @@
 #include <Multiplayer/Components/NetworkTransformComponent.h>
 #include <Multiplayer/Components/NetworkTransformComponent.h>
 #include <AzCore/Component/TransformBus.h>
 #include <AzCore/Component/TransformBus.h>
 #include <AzCore/Serialization/SerializeContext.h>
 #include <AzCore/Serialization/SerializeContext.h>
-#include <AzFramework/Physics/RigidBodyBus.h>
+#include <AzFramework/Physics/Components/SimulatedBodyComponentBus.h>
 
 
 namespace MultiplayerSample
 namespace MultiplayerSample
 {
 {
@@ -63,7 +63,7 @@ namespace MultiplayerSample
         
         
         // disable physics (needed to move rigid bodies)
         // disable physics (needed to move rigid bodies)
         // see: https://github.com/o3de/o3de/issues/2541
         // see: https://github.com/o3de/o3de/issues/2541
-        Physics::RigidBodyRequestBus::Event(selfId, &Physics::RigidBodyRequestBus::Events::DisablePhysics);
+        AzPhysics::SimulatedBodyComponentRequestsBus::Event(selfId, &AzPhysics::SimulatedBodyComponentRequestsBus::Events::DisablePhysics);
 
 
         // move self and increment resetCount to prevent transform interpolation
         // move self and increment resetCount to prevent transform interpolation
         AZ::TransformBus::Event(selfId,
         AZ::TransformBus::Event(selfId,
@@ -73,7 +73,7 @@ namespace MultiplayerSample
         netTransform->SetResetCount(netTransform->GetResetCount() + 1);
         netTransform->SetResetCount(netTransform->GetResetCount() + 1);
 
 
         // re-enable physics
         // re-enable physics
-        Physics::RigidBodyRequestBus::Event(selfId, &Physics::RigidBodyRequestBus::Events::EnablePhysics);
+        AzPhysics::SimulatedBodyComponentRequestsBus::Event(selfId, &AzPhysics::SimulatedBodyComponentRequestsBus::Events::EnablePhysics);
 
 
         NotifyTeleport(teleportedLocation);
         NotifyTeleport(teleportedLocation);
     }
     }

+ 21 - 9
Levels/NewStarbase/NewStarbase.prefab

@@ -5538,7 +5538,8 @@
                     "Id": 110022581788315572,
                     "Id": 110022581788315572,
                     "m_template": {
                     "m_template": {
                         "$type": "MultiplayerSample::NetworkMatchComponent",
                         "$type": "MultiplayerSample::NetworkMatchComponent",
-                        "RespawnPenaltyPercent": 20
+                        "RespawnPenaltyPercent": 20,
+                        "RespawnGemTag": "Respawn Gem"
                     }
                     }
                 },
                 },
                 "Component_[11474776527615145837]": {
                 "Component_[11474776527615145837]": {
@@ -5690,6 +5691,17 @@
                                     "assetHint": "prefabs/diamond_gem.spawnable"
                                     "assetHint": "prefabs/diamond_gem.spawnable"
                                 },
                                 },
                                 "Score": 20
                                 "Score": 20
+                            },
+                            {
+                                "Tag": "Respawn Gem",
+                                "Asset": {
+                                    "assetId": {
+                                        "guid": "{0A633BA0-57EE-51B7-8722-1EBC551740F2}",
+                                        "subId": 3148544445
+                                    },
+                                    "assetHint": "prefabs/player_drop_gem.spawnable"
+                                },
+                                "Score": 0
                             }
                             }
                         ],
                         ],
                         "SpawnTablesPerRound": [
                         "SpawnTablesPerRound": [
@@ -30467,7 +30479,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",
@@ -30507,7 +30519,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",
@@ -30547,7 +30559,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",
@@ -30587,7 +30599,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",
@@ -30627,7 +30639,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",
@@ -30667,7 +30679,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",
@@ -30707,7 +30719,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",
@@ -30747,7 +30759,7 @@
                 {
                 {
                     "op": "replace",
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
                     "path": "/ContainerEntity/Components/Component_[2563122736071885678]/Transform Data/Rotate/1",
-                    "value": 1.273830758756478e-12
+                    "value": 1.2738307587564779e-12
                 },
                 },
                 {
                 {
                     "op": "replace",
                     "op": "replace",