diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index 60b6550..7f9d732 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -105,10 +105,17 @@ ManualIPAddress= +Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.") +Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.") +Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") -+Profiles=(Name="CogSampleProfile",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Sample cog profile") -+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="CogSampleChannel") -+EditProfiles=(Name="Pawn",CustomResponses=((Channel="Camera",Response=ECR_Ignore))) -+EditProfiles=(Name="CharacterMesh",CustomResponses=((Channel="Camera",Response=ECR_Ignore))) ++Profiles=(Name="ProjectileCollision",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Projectile",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Needs description") ++Profiles=(Name="ProjectileAssistance",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Projectile",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Needs description") ++DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,DefaultResponse=ECR_Ignore,bTraceType=False,bStaticObject=False,Name="Projectile") ++EditProfiles=(Name="Pawn",CustomResponses=((Channel="Camera",Response=ECR_Ignore),(Channel="Projectile",Response=ECR_Ignore))) ++EditProfiles=(Name="CharacterMesh",CustomResponses=((Channel="Camera",Response=ECR_Ignore),(Channel="Projectile",Response=ECR_Overlap))) ++EditProfiles=(Name="BlockAll",CustomResponses=((Channel="Projectile"))) ++EditProfiles=(Name="OverlapAll",CustomResponses=((Channel="Projectile",Response=ECR_Overlap))) ++EditProfiles=(Name="BlockAllDynamic",CustomResponses=((Channel="Projectile"))) ++EditProfiles=(Name="OverlapAllDynamic",CustomResponses=((Channel="Projectile",Response=ECR_Overlap))) ++EditProfiles=(Name="IgnoreOnlyPawn",CustomResponses=((Channel="Projectile"))) ++EditProfiles=(Name="OverlapOnlyPawn",CustomResponses=((Channel="Projectile"))) -ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") -ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") -ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") diff --git a/Content/Characters/Hero1/Abilities/Area/AM_Hero1_Area.uasset b/Content/Characters/Hero1/Abilities/Area/AM_Hero1_Area.uasset new file mode 100644 index 0000000..d0b76f5 Binary files /dev/null and b/Content/Characters/Hero1/Abilities/Area/AM_Hero1_Area.uasset differ diff --git a/Content/Characters/Hero1/Abilities/Area/AN_Hero1_Area.uasset b/Content/Characters/Hero1/Abilities/Area/AN_Hero1_Area.uasset new file mode 100644 index 0000000..f67b6f2 Binary files /dev/null and b/Content/Characters/Hero1/Abilities/Area/AN_Hero1_Area.uasset differ diff --git a/Content/Characters/Hero1/Abilities/Area/BP_Hero1_Area.uasset b/Content/Characters/Hero1/Abilities/Area/BP_Hero1_Area.uasset new file mode 100644 index 0000000..527f6af Binary files /dev/null and b/Content/Characters/Hero1/Abilities/Area/BP_Hero1_Area.uasset differ diff --git a/Content/Characters/Hero1/Abilities/Area/GA_Hero1_Area.uasset b/Content/Characters/Hero1/Abilities/Area/GA_Hero1_Area.uasset new file mode 100644 index 0000000..f07bcfb Binary files /dev/null and b/Content/Characters/Hero1/Abilities/Area/GA_Hero1_Area.uasset differ diff --git a/Content/Characters/Hero1/Abilities/Area/GE_Hero1_Area.uasset b/Content/Characters/Hero1/Abilities/Area/GE_Hero1_Area.uasset new file mode 100644 index 0000000..2909d0f Binary files /dev/null and b/Content/Characters/Hero1/Abilities/Area/GE_Hero1_Area.uasset differ diff --git a/Content/Characters/Hero1/Abilities/Area/VFX_Hero1_Area.uasset b/Content/Characters/Hero1/Abilities/Area/VFX_Hero1_Area.uasset new file mode 100644 index 0000000..77ecd12 Binary files /dev/null and b/Content/Characters/Hero1/Abilities/Area/VFX_Hero1_Area.uasset differ diff --git a/Content/Characters/Hero1/BP_Hero1.uasset b/Content/Characters/Hero1/BP_Hero1.uasset index 1e2d76b..813fba6 100644 Binary files a/Content/Characters/Hero1/BP_Hero1.uasset and b/Content/Characters/Hero1/BP_Hero1.uasset differ diff --git a/Content/Characters/_Shared_/Abilities/GA_Base.uasset b/Content/Characters/_Shared_/Abilities/GA_Base.uasset index ecbeea5..744138f 100644 Binary files a/Content/Characters/_Shared_/Abilities/GA_Base.uasset and b/Content/Characters/_Shared_/Abilities/GA_Base.uasset differ diff --git a/Content/Characters/_Shared_/BP_Character.uasset b/Content/Characters/_Shared_/BP_Character.uasset index fda4e76..c856025 100644 Binary files a/Content/Characters/_Shared_/BP_Character.uasset and b/Content/Characters/_Shared_/BP_Character.uasset differ diff --git a/Content/Core/Actors/BP_Base_Area.uasset b/Content/Core/Actors/BP_Base_Area.uasset new file mode 100644 index 0000000..bfb7739 Binary files /dev/null and b/Content/Core/Actors/BP_Base_Area.uasset differ diff --git a/Content/Core/Actors/BP_Base_Projectile.uasset b/Content/Core/Actors/BP_Base_Projectile.uasset new file mode 100644 index 0000000..6523c0d Binary files /dev/null and b/Content/Core/Actors/BP_Base_Projectile.uasset differ diff --git a/Content/Core/Actors/BP_Base_ProjectileLauncher.uasset b/Content/Core/Actors/BP_Base_ProjectileLauncher.uasset new file mode 100644 index 0000000..8d4f1de Binary files /dev/null and b/Content/Core/Actors/BP_Base_ProjectileLauncher.uasset differ diff --git a/Content/Core/Debug/Cheats/AM_Cheat.uasset b/Content/Core/Debug/Cheats/AM_Cheat.uasset index fa13ce3..df15630 100644 Binary files a/Content/Core/Debug/Cheats/AM_Cheat.uasset and b/Content/Core/Debug/Cheats/AM_Cheat.uasset differ diff --git a/Content/Core/Debug/Cheats/GA_Cheat_Debug.uasset b/Content/Core/Debug/Cheats/GA_Cheat_Debug.uasset index 8cb1baf..274a586 100644 Binary files a/Content/Core/Debug/Cheats/GA_Cheat_Debug.uasset and b/Content/Core/Debug/Cheats/GA_Cheat_Debug.uasset differ diff --git a/Content/Core/Debug/Cheats/GE_Cheat_Easy.uasset b/Content/Core/Debug/Cheats/GE_Cheat_Easy.uasset index 5ccefa1..137b5fd 100644 Binary files a/Content/Core/Debug/Cheats/GE_Cheat_Easy.uasset and b/Content/Core/Debug/Cheats/GE_Cheat_Easy.uasset differ diff --git a/Content/Core/Debug/DA_Debug_Engine.uasset b/Content/Core/Debug/DA_Debug_Engine.uasset index 13b7002..4b7b5a4 100644 Binary files a/Content/Core/Debug/DA_Debug_Engine.uasset and b/Content/Core/Debug/DA_Debug_Engine.uasset differ diff --git a/Content/Ingredients/DamageArea/BP_DamageArea.uasset b/Content/Ingredients/DamageArea/BP_DamageArea.uasset index 2ea0be4..14daf0a 100644 Binary files a/Content/Ingredients/DamageArea/BP_DamageArea.uasset and b/Content/Ingredients/DamageArea/BP_DamageArea.uasset differ diff --git a/Content/Ingredients/DamageArea/VFX_DamageArea.uasset b/Content/Ingredients/DamageArea/VFX_DamageArea.uasset index 9d7c1d5..89c1854 100644 Binary files a/Content/Ingredients/DamageArea/VFX_DamageArea.uasset and b/Content/Ingredients/DamageArea/VFX_DamageArea.uasset differ diff --git a/Content/Ingredients/DamageProjectile/BP_DamageProjectileLauncher.uasset b/Content/Ingredients/DamageProjectile/BP_DamageProjectileLauncher.uasset new file mode 100644 index 0000000..4461a06 Binary files /dev/null and b/Content/Ingredients/DamageProjectile/BP_DamageProjectileLauncher.uasset differ diff --git a/Content/Ingredients/DamageProjectile/BP_Trap_Dart.uasset b/Content/Ingredients/DamageProjectile/BP_Trap_Dart.uasset new file mode 100644 index 0000000..f31d2f0 Binary files /dev/null and b/Content/Ingredients/DamageProjectile/BP_Trap_Dart.uasset differ diff --git a/Content/Ingredients/DamageProjectile/BP_Trap_Dart_Projectile.uasset b/Content/Ingredients/DamageProjectile/BP_Trap_Dart_Projectile.uasset new file mode 100644 index 0000000..626f27b Binary files /dev/null and b/Content/Ingredients/DamageProjectile/BP_Trap_Dart_Projectile.uasset differ diff --git a/Content/Ingredients/DamageProjectile/GE_Trap_Dart_Damage.uasset b/Content/Ingredients/DamageProjectile/GE_Trap_Dart_Damage.uasset new file mode 100644 index 0000000..79af13f Binary files /dev/null and b/Content/Ingredients/DamageProjectile/GE_Trap_Dart_Damage.uasset differ diff --git a/Content/__ExternalActors__/Maps/L_Default/2/ZP/ZFTVD1X629VL0RB90NJ1OK.uasset b/Content/__ExternalActors__/Maps/L_Default/2/ZP/ZFTVD1X629VL0RB90NJ1OK.uasset index 359c4a9..13667d2 100644 Binary files a/Content/__ExternalActors__/Maps/L_Default/2/ZP/ZFTVD1X629VL0RB90NJ1OK.uasset and b/Content/__ExternalActors__/Maps/L_Default/2/ZP/ZFTVD1X629VL0RB90NJ1OK.uasset differ diff --git a/Content/__ExternalActors__/Maps/L_Default/7/Z0/GPP630NNTJ7N1E8B7TBGS5.uasset b/Content/__ExternalActors__/Maps/L_Default/7/Z0/GPP630NNTJ7N1E8B7TBGS5.uasset new file mode 100644 index 0000000..9c40f1b Binary files /dev/null and b/Content/__ExternalActors__/Maps/L_Default/7/Z0/GPP630NNTJ7N1E8B7TBGS5.uasset differ diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_NetEmulation.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_NetEmulation.cpp index f5d2b76..b63e7ca 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_NetEmulation.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_NetEmulation.cpp @@ -1,6 +1,7 @@ #include "CogEngineWindow_NetEmulation.h" #include "CogEngineWindow_Stats.h" +#include "CogWindowWidgets.h" #include "Engine/Engine.h" #include "Engine/NetDriver.h" #include "Engine/NetConnection.h" @@ -111,7 +112,7 @@ void UCogEngineWindow_NetEmulation::DrawControls() //------------------------------------------------------------------------------------------- FCogWindowWidgets::SetNextItemToShortWidth(); - if (ImGui::DragInt("Lag Min", &Settings.PktLagMin, 1.0f, 0, INT_MAX, "%d ms")) + if (ImGui::DragInt("Lag Min", &Settings.PktLagMin, 5.0f, 0, INT_MAX, "%d ms")) { SelectedNetDriver->NetDriver->SetPacketSimulationSettings(Settings); } @@ -123,7 +124,7 @@ void UCogEngineWindow_NetEmulation::DrawControls() //------------------------------------------------------------------------------------------- FCogWindowWidgets::SetNextItemToShortWidth(); - if (ImGui::DragInt("Lag Max", &Settings.PktLagMax, 1.0f, 0, INT_MAX, "%d ms")) + if (ImGui::DragInt("Lag Max", &Settings.PktLagMax, 5.0f, 0, INT_MAX, "%d ms")) { SelectedNetDriver->NetDriver->SetPacketSimulationSettings(Settings); } @@ -184,7 +185,7 @@ void UCogEngineWindow_NetEmulation::DrawControls() //------------------------------------------------------------------------------------------- FCogWindowWidgets::SetNextItemToShortWidth(); - if (ImGui::DragInt("Incoming Lag Min", &Settings.PktIncomingLagMin, 1.0f, 0, INT_MAX, "%d ms")) + if (ImGui::DragInt("Incoming Lag Min", &Settings.PktIncomingLagMin, 5.0f, 0, INT_MAX, "%d ms")) { SelectedNetDriver->NetDriver->SetPacketSimulationSettings(Settings); } @@ -196,7 +197,7 @@ void UCogEngineWindow_NetEmulation::DrawControls() //------------------------------------------------------------------------------------------- FCogWindowWidgets::SetNextItemToShortWidth(); - if (ImGui::DragInt("Incoming Lag Max", &Settings.PktIncomingLagMax, 1.0f, 0, INT_MAX, "%d ms")) + if (ImGui::DragInt("Incoming Lag Max", &Settings.PktIncomingLagMax, 5.0f, 0, INT_MAX, "%d ms")) { SelectedNetDriver->NetDriver->SetPacketSimulationSettings(Settings); } diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Selection.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Selection.cpp index a9a67a3..8b203b5 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Selection.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Selection.cpp @@ -558,40 +558,32 @@ bool UCogEngineWindow_Selection::ComputeBoundingBoxScreenPosition(const APlayerC } //-------------------------------------------------------------------------------------------------------------------------- -void UCogEngineWindow_Selection::RenderMainMenuWidget(bool Draw, float& Width) +float UCogEngineWindow_Selection::GetMainMenuWidgetWidth(int32 SubWidgetIndex, float MaxWidth) { - const float PickButtonWidth = FCogWindowWidgets::GetFontWidth() * 6; - const float SelectionButtonWidth = FCogWindowWidgets::GetFontWidth() * 30; - const float ResetButtonWidth = FCogWindowWidgets::GetFontWidth() * 3; - Width = PickButtonWidth + SelectionButtonWidth + ResetButtonWidth; - - if (Draw == false) + switch (SubWidgetIndex) { - return; + case 0: return FCogWindowWidgets::GetFontWidth() * 6; + case 1: return FMath::Min(FMath::Max(MaxWidth, FCogWindowWidgets::GetFontWidth() * 10), FCogWindowWidgets::GetFontWidth() * 30); + case 2: return FCogWindowWidgets::GetFontWidth() * 3; } - if (ImGui::BeginPopup("SelectionPopup")) - { - ImGui::BeginChild("Popup", ImVec2(Width, FCogWindowWidgets::GetFontWidth() * 40), false); + return -1.0f; +} - if (DrawSelectionCombo()) - { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndChild(); - ImGui::EndPopup(); - } +//-------------------------------------------------------------------------------------------------------------------------- +void UCogEngineWindow_Selection::RenderMainMenuWidget(int32 SubWidgetIndex, float Width) +{ //----------------------------------- // Pick Button //----------------------------------- + if (SubWidgetIndex == 0) { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); - if (ImGui::Button("Pick", ImVec2(PickButtonWidth, 0))) + if (ImGui::Button("Pick", ImVec2(Width, 0))) { ActivateSelectionMode(); HackWaitInputRelease(); @@ -601,52 +593,67 @@ void UCogEngineWindow_Selection::RenderMainMenuWidget(bool Draw, float& Width) ImGui::PopStyleColor(1); ImGui::PopStyleVar(2); } - - AActor* GlobalSelection = FCogDebugSettings::GetSelection(); - - //----------------------------------- - // Selection - //----------------------------------- + else if (SubWidgetIndex == 1) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); - ImGui::SameLine(); - FString CurrentSelectionName = GetActorName(GlobalSelection); + if (ImGui::BeginPopup("SelectionPopup")) + { + ImGui::BeginChild("Popup", ImVec2(Width, FCogWindowWidgets::GetFontWidth() * 40), false); - if (ImGui::Button(TCHAR_TO_ANSI(*CurrentSelectionName), ImVec2(SelectionButtonWidth, 0))) - { - ImGui::OpenPopup("SelectionPopup"); - } - if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Current Selection: %s", TCHAR_TO_ANSI(*CurrentSelectionName)); + if (DrawSelectionCombo()) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndChild(); + ImGui::EndPopup(); } - ImGui::PopStyleColor(1); - ImGui::PopStyleVar(2); + AActor* GlobalSelection = FCogDebugSettings::GetSelection(); - DrawActorContextMenu(GlobalSelection); + //----------------------------------- + // Selection + //----------------------------------- + { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); + FString CurrentSelectionName = GetActorName(GlobalSelection); + + if (ImGui::Button(TCHAR_TO_ANSI(*CurrentSelectionName), ImVec2(Width, 0.0f))) + { + ImGui::OpenPopup("SelectionPopup"); + } + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Current Selection: %s", TCHAR_TO_ANSI(*CurrentSelectionName)); + } + + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(2); + + DrawActorContextMenu(GlobalSelection); + } } - - //----------------------------------- - // Reset Button - //----------------------------------- + else if (SubWidgetIndex == 2) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); - ImGui::SameLine(); - if (ImGui::Button("X", ImVec2(ResetButtonWidth, 0))) + //----------------------------------- + // Reset Button + //----------------------------------- { - SetGlobalSelection(nullptr); - ImGui::CloseCurrentPopup(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); + if (ImGui::Button("X", ImVec2(Width, 0))) + { + SetGlobalSelection(nullptr); + ImGui::CloseCurrentPopup(); + } + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Reset the selection to the controlled actor."); + } + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(1); } - if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Reset the selection to the controlled actor."); - } - ImGui::PopStyleColor(1); - ImGui::PopStyleVar(1); } } diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Stats.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Stats.cpp index d92c40d..1386c81 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Stats.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Stats.cpp @@ -2,8 +2,10 @@ #include "CogWindowWidgets.h" #include "Engine/Engine.h" +#include "Engine/NetDriver.h" #include "Engine/NetConnection.h" #include "GameFramework/PlayerState.h" +#include "Net/NetPing.h" ImVec4 StatRedColor(1.0f, 0.4f, 0.3f, 1.0f); ImVec4 StatOrangeColor(1.0f, 0.7f, 0.4f, 1.0f); @@ -53,39 +55,170 @@ void UCogEngineWindow_Stats::RenderContent() } //-------------------------------------------------------------------------------------------------------------------------- -void UCogEngineWindow_Stats::RenderMainMenuWidget(bool Draw, float& Width) +float UCogEngineWindow_Stats::GetMainMenuWidgetWidth(int32 WidgetIndex, float MaxWidth) { - Width = FCogWindowWidgets::GetFontWidth() * 25; + const APlayerController* PlayerController = GetLocalPlayerController(); + const UNetConnection* Connection = PlayerController != nullptr ? PlayerController->GetNetConnection() : nullptr; - if (Draw == false) + switch (WidgetIndex) + { + case 0: return FCogWindowWidgets::GetFontWidth() * 8; + case 1: return Connection != nullptr ? FCogWindowWidgets::GetFontWidth() * 7 : 0.0f; + case 2: return Connection != nullptr ? FCogWindowWidgets::GetFontWidth() * 7 : 0.0f; + } + + return -1; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogEngineWindow_Stats::RenderMainMenuWidget(int32 WidgetIndex, float Width) +{ + switch (WidgetIndex) + { + case 0: RenderMainMenuWidgetFramerate(Width); break; + case 1: RenderMainMenuWidgetPing(Width); break; + case 2: RenderMainMenuWidgetPacketLoss(Width); break; + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogEngineWindow_Stats::RenderMainMenuWidgetFramerate(float Width) +{ + extern ENGINE_API float GAverageFPS; + int32 Fps = (int32)GAverageFPS; + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); + ImGui::PushStyleColor(ImGuiCol_Text, GetFpsColor(Fps)); + + if (ImGui::Button(TCHAR_TO_ANSI(*FString::Printf(TEXT("%3dfps###FramerateButton"), Fps)), ImVec2(Width, 0.0f))) + { + ImGui::OpenPopup("FrameratePopup"); + } + + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(2); + + ImGui::SetItemTooltip("Framerate"); + + if (ImGui::BeginPopup("FrameratePopup")) + { + ImGui::Text("Fps"); + ImGui::SameLine(); + + int32 MaxFps = GEngine->GetMaxFPS(); + TArray Values{ 0, 10, 20, 30, 60, 120 }; + if (FCogWindowWidgets::MultiChoiceButtonsInt(Values, MaxFps, ImVec2(3.5f * FCogWindowWidgets::GetFontWidth(), 0))) + { + GEngine->SetMaxFPS(MaxFps); + } + + ImGui::EndPopup(); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogEngineWindow_Stats::RenderMainMenuWidgetPing(float Width) +{ + const APlayerController* PlayerController = GetLocalPlayerController(); + const APlayerState* PlayerState = PlayerController != nullptr ? PlayerController->GetPlayerState() : nullptr; + if (PlayerState == nullptr) { return; } - extern ENGINE_API float GAverageFPS; - ImGui::TextColored(GetFpsColor(GAverageFPS), "%3dfps ", (int32)GAverageFPS); - ImGui::SetItemTooltip("Frame Per Second"); + const float Ping = PlayerState->GetPingInMilliseconds(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); + ImGui::PushStyleColor(ImGuiCol_Text, GetPingColor(Ping)); - if (const APlayerController* PlayerController = GetLocalPlayerController()) + if (ImGui::Button(TCHAR_TO_ANSI(*FString::Printf(TEXT("%3dms###PingButton"), (int32)Ping)), ImVec2(Width, 0.0f))) { - if (const APlayerState* PlayerState = PlayerController->GetPlayerState()) - { - const float Ping = PlayerState->GetPingInMilliseconds(); - ImGui::SameLine(); - ImGui::TextColored(GetPingColor(Ping), "%3dms ", (int32)Ping); - ImGui::SetItemTooltip("Ping"); - } - - if (UNetConnection* Connection = PlayerController->GetNetConnection()) - { - const float OutPacketLost = Connection->GetOutLossPercentage().GetAvgLossPercentage() * 100.0f; - const float InPacketLost = Connection->GetInLossPercentage().GetAvgLossPercentage() * 100.0f; - const float TotalPacketLost = OutPacketLost + InPacketLost; - ImGui::SameLine(); - ImGui::TextColored(GetPacketLossColor(TotalPacketLost), "%2d%% ", (int32)TotalPacketLost); - ImGui::SetItemTooltip("Packet Loss"); - } + ImGui::OpenPopup("PingPopup"); } + + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(2); + + ImGui::SetItemTooltip("Ping"); + +#if DO_ENABLE_NET_TEST + if (ImGui::BeginPopup("PingPopup")) + { + + FWorldContext& WorldContext = GEngine->GetWorldContextFromWorldChecked(GetWorld()); + if (WorldContext.ActiveNetDrivers.Num() > 0) + { + ImGui::Text("Ping"); + ImGui::SameLine(); + + FNamedNetDriver* SelectedNetDriver = &WorldContext.ActiveNetDrivers[0]; + FPacketSimulationSettings Settings = SelectedNetDriver->NetDriver->PacketSimulationSettings; + TArray Values{ 0, 50, 100, 200, 500, 1000 }; + if (FCogWindowWidgets::MultiChoiceButtonsInt(Values, Settings.PktIncomingLagMin, ImVec2(4.5f * FCogWindowWidgets::GetFontWidth(), 0))) + { + SelectedNetDriver->NetDriver->SetPacketSimulationSettings(Settings); + } + } + ImGui::EndPopup(); + } +#endif //DO_ENABLE_NET_TEST +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogEngineWindow_Stats::RenderMainMenuWidgetPacketLoss(float Width) +{ + const APlayerController* PlayerController = GetLocalPlayerController(); + UNetConnection* Connection = PlayerController != nullptr ? PlayerController->GetNetConnection() : nullptr; + if (Connection == nullptr) + { + return; + } + + const float OutPacketLost = Connection->GetOutLossPercentage().GetAvgLossPercentage() * 100.0f; + const float InPacketLost = Connection->GetInLossPercentage().GetAvgLossPercentage() * 100.0f; + const float TotalPacketLost = (OutPacketLost + InPacketLost) / 2; + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); + ImGui::PushStyleColor(ImGuiCol_Text, GetPacketLossColor(TotalPacketLost)); + + if (ImGui::Button(TCHAR_TO_ANSI(*FString::Printf(TEXT("%2d%%###PacketLossButton"), (int32)TotalPacketLost)), ImVec2(Width, 0.0f))) + { + ImGui::OpenPopup("PacketLossPopup"); + } + + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(2); + + ImGui::SetItemTooltip("Packet Loss"); + +#if DO_ENABLE_NET_TEST + if (ImGui::BeginPopup("PacketLossPopup")) + { + + FWorldContext& WorldContext = GEngine->GetWorldContextFromWorldChecked(GetWorld()); + if (WorldContext.ActiveNetDrivers.Num() > 0) + { + ImGui::Text("Packet Loss"); + ImGui::SameLine(); + + FNamedNetDriver* SelectedNetDriver = &WorldContext.ActiveNetDrivers[0]; + FPacketSimulationSettings Settings = SelectedNetDriver->NetDriver->PacketSimulationSettings; + + TArray Values{ 0, 5, 10, 20, 30, 40, 50 }; + if (FCogWindowWidgets::MultiChoiceButtonsInt(Values, Settings.PktIncomingLoss, ImVec2(3.5f * FCogWindowWidgets::GetFontWidth(), 0))) + { + Settings.PktLoss = Settings.PktIncomingLoss; + SelectedNetDriver->NetDriver->SetPacketSimulationSettings(Settings); + } + } + ImGui::EndPopup(); + } +#endif //DO_ENABLE_NET_TEST } //-------------------------------------------------------------------------------------------------------------------------- diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_TimeScale.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_TimeScale.cpp index cb53072..a484e60 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_TimeScale.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_TimeScale.cpp @@ -1,6 +1,7 @@ #include "CogEngineWindow_TimeScale.h" #include "CogEngineReplicator.h" +#include "CogWindowWidgets.h" #include "Engine/Engine.h" #include "Engine/World.h" @@ -43,49 +44,11 @@ void UCogEngineWindow_TimeScale::RenderContent() return; } - ImGuiStyle& Style = ImGui::GetStyle(); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(Style.WindowPadding.x * 0.40f, (float)(int)(Style.WindowPadding.y * 0.60f))); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(Style.FramePadding.x * 0.40f, (float)(int)(Style.FramePadding.y * 0.60f))); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(Style.ItemSpacing.x * 0.30f, (float)(int)(Style.ItemSpacing.y * 0.60f))); - ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(255, 255, 255, 180)); - - for (float TimeScale : TimingScales) - { - DrawTimeButton(Replicator, TimeScale); - ImGui::SameLine(); - } - - ImGui::PopStyleColor(1); - ImGui::PopStyleVar(3); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void UCogEngineWindow_TimeScale::DrawTimeButton(ACogEngineReplicator* Replicator, float Value) -{ - const bool IsSelected = FMath::IsNearlyEqual(Replicator->GetTimeDilation(), Value, 0.0001f); - if (IsSelected) - { - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - } - else - { - ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(128, 128, 128, 50)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(128, 128, 128, 100)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(128, 128, 128, 150)); - } - - const char* Text = TCHAR_TO_ANSI(*FString::Printf(TEXT("%g"), Value).Replace(TEXT("0."), TEXT("."))); - if (ImGui::Button(Text, ImVec2(3.5f * FCogWindowWidgets::GetFontWidth(), 0))) + + float Value = Replicator->GetTimeDilation(); + if (FCogWindowWidgets::MultiChoiceButtonsFloat(TimingScales, Value, ImVec2(3.5f * FCogWindowWidgets::GetFontWidth(), 0))) { Replicator->Server_SetTimeDilation(Value); } - if (IsSelected) - { - ImGui::PopStyleVar(); - } - else - { - ImGui::PopStyleColor(3); - } } diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Selection.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Selection.h index 6ab4345..f15beb0 100644 --- a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Selection.h +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Selection.h @@ -50,7 +50,9 @@ protected: virtual void RenderContent() override; - virtual void RenderMainMenuWidget(bool Draw, float& Width) override; + virtual float GetMainMenuWidgetWidth(int32 SubWidgetIndex, float MaxWidth) override; + + virtual void RenderMainMenuWidget(int32 SubWidgetIndex, float Width) override; virtual bool DrawSelectionCombo(); diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Stats.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Stats.h index 66cf4b5..c29b4fe 100644 --- a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Stats.h +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Stats.h @@ -23,5 +23,10 @@ protected: virtual void RenderContent() override; - virtual void RenderMainMenuWidget(bool Draw, float& Width) override; + virtual float GetMainMenuWidgetWidth(int32 SubWidgetIndex, float MaxWidth) override; + + virtual void RenderMainMenuWidget(int32 SubWidgetIndex, float Width) override; + void RenderMainMenuWidgetPacketLoss(float Width); + void RenderMainMenuWidgetPing(float Width); + void RenderMainMenuWidgetFramerate(float Width); }; diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_TimeScale.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_TimeScale.h index c7629a8..e76362a 100644 --- a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_TimeScale.h +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_TimeScale.h @@ -21,8 +21,6 @@ protected: virtual void RenderContent() override; - virtual void DrawTimeButton(ACogEngineReplicator* Replicator, float Value); - TArray TimingScales; private: diff --git a/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp b/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp index 86a8f2e..25271f8 100644 --- a/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp +++ b/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp @@ -384,21 +384,55 @@ void UCogWindowManager::RenderMainMenu() for (UCogWindow* Window : MainMenuWidgets) { - float Width = 0.0f; - Window->RenderMainMenuWidget(false, Width); + TArray SubWidgetsWidths; + float SimCursorX = CursorX; + for (int32 SubWidgetIndex = 0; ; ++SubWidgetIndex) + { + const float MaxWidth = SimCursorX - MinCursorX; + float SubWidgetWidth = Window->GetMainMenuWidgetWidth(SubWidgetIndex, MaxWidth); + if (SubWidgetWidth == -1) + { + break; + } - //------------------------------------------- - // Stop drawing if there is not enough room - //------------------------------------------- - if (CursorX - Width < MinCursorX) + SimCursorX -= SubWidgetWidth; + SubWidgetsWidths.Add(SubWidgetWidth); + } + + bool Stop = false; + for (int32 SubWidgetIndex = SubWidgetsWidths.Num() - 1; SubWidgetIndex >= 0; SubWidgetIndex--) + { + const float SubWidgetWidth = SubWidgetsWidths[SubWidgetIndex]; + const float MaxWidth = CursorX - MinCursorX; + + //------------------------------------------- + // Bypass this subwidget if its width is 0 + //------------------------------------------- + if (SubWidgetWidth == 0) + { + continue; + } + + //------------------------------------------- + // Stop drawing if there is not enough room + //------------------------------------------- + if (SubWidgetWidth > MaxWidth) + { + Stop = true; + break; + } + + CursorX -= SubWidgetWidth; + ImGui::SetCursorPosX(CursorX); + + Window->RenderMainMenuWidget(SubWidgetIndex, SubWidgetWidth); + } + + if (Stop) { break; } - CursorX -= Width; - ImGui::SetCursorPosX(CursorX); - Window->RenderMainMenuWidget(true, Width); - CursorX -= ImGui::GetStyle().ItemSpacing.x; } diff --git a/Plugins/Cog/Source/CogWindow/Private/CogWindowWidgets.cpp b/Plugins/Cog/Source/CogWindow/Private/CogWindowWidgets.cpp index ff84e2f..d3fd229 100644 --- a/Plugins/Cog/Source/CogWindow/Private/CogWindowWidgets.cpp +++ b/Plugins/Cog/Source/CogWindow/Private/CogWindowWidgets.cpp @@ -474,5 +474,99 @@ bool FCogWindowWidgets::DeleteArrayItemButton() ImGui::SetTooltip("Delete Item"); } + return IsPressed; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogWindowWidgets::MultiChoiceButton(const char* Label, bool IsSelected, const ImVec2& Size) +{ + if (IsSelected) + { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + } + else + { + ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(128, 128, 128, 50)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(128, 128, 128, 100)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(128, 128, 128, 150)); + } + + const bool IsPressed = ImGui::Button(Label, Size); + + if (IsSelected) + { + ImGui::PopStyleVar(); + } + else + { + ImGui::PopStyleColor(3); + } + + return IsPressed; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogWindowWidgets::MultiChoiceButtonsInt(TArray& Values, int32& Value, const ImVec2& Size) +{ + ImGuiStyle& Style = ImGui::GetStyle(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(Style.WindowPadding.x * 0.40f, (float)(int)(Style.WindowPadding.y * 0.60f))); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(Style.FramePadding.x * 0.40f, (float)(int)(Style.FramePadding.y * 0.60f))); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(Style.ItemSpacing.x * 0.30f, (float)(int)(Style.ItemSpacing.y * 0.60f))); + ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(255, 255, 255, 180)); + + bool IsPressed = false; + for (int32 i = 0; i < Values.Num(); ++i) + { + int32 ButtonValue = Values[i]; + + const char* Text = TCHAR_TO_ANSI(*FString::Printf(TEXT("%d"), ButtonValue)); + if (MultiChoiceButton(Text, ButtonValue == Value, Size)) + { + IsPressed = true; + Value = ButtonValue; + } + + if (i < Values.Num() - 1) + { + ImGui::SameLine(); + } + } + + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(3); + + return IsPressed; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogWindowWidgets::MultiChoiceButtonsFloat(TArray& Values, float& Value, const ImVec2& Size) +{ + ImGuiStyle& Style = ImGui::GetStyle(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(Style.WindowPadding.x * 0.40f, (float)(int)(Style.WindowPadding.y * 0.60f))); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(Style.FramePadding.x * 0.40f, (float)(int)(Style.FramePadding.y * 0.60f))); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(Style.ItemSpacing.x * 0.30f, (float)(int)(Style.ItemSpacing.y * 0.60f))); + ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(255, 255, 255, 180)); + + bool IsPressed = false; + for (int32 i = 0; i < Values.Num(); ++i) + { + float ButtonValue = Values[i]; + + const char* Text = TCHAR_TO_ANSI(*FString::Printf(TEXT("%g"), ButtonValue).Replace(TEXT("0."), TEXT("."))); + if (MultiChoiceButton(Text, ButtonValue == Value, Size)) + { + IsPressed = true; + Value = ButtonValue; + } + + if (i < Values.Num() - 1) + { + ImGui::SameLine(); + } + } + + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(3); + return IsPressed; } \ No newline at end of file diff --git a/Plugins/Cog/Source/CogWindow/Public/CogWindow.h b/Plugins/Cog/Source/CogWindow/Public/CogWindow.h index 11e9c34..6248a35 100644 --- a/Plugins/Cog/Source/CogWindow/Public/CogWindow.h +++ b/Plugins/Cog/Source/CogWindow/Public/CogWindow.h @@ -31,7 +31,10 @@ public: virtual void GameTick(float DeltaTime); /** */ - virtual void RenderMainMenuWidget(bool Draw, float& Width) {} + virtual float GetMainMenuWidgetWidth(int32 SubWidgetIndex, float MaxWidth) { return -1.0f; } + + /** */ + virtual void RenderMainMenuWidget(int32 SubWidgetIndex, float Width) {} ImGuiID GetID() const { return ID; } diff --git a/Plugins/Cog/Source/CogWindow/Public/CogWindowWidgets.h b/Plugins/Cog/Source/CogWindow/Public/CogWindowWidgets.h index c6fbd3f..898cc3c 100644 --- a/Plugins/Cog/Source/CogWindow/Public/CogWindowWidgets.h +++ b/Plugins/Cog/Source/CogWindow/Public/CogWindowWidgets.h @@ -20,6 +20,12 @@ public: static void ToggleButton(bool* Value, const char* TextTrue, const char* TextFalse, const ImVec4& TrueColor, const ImVec4& FalseColor, const ImVec2& Size = ImVec2(0, 0)); + static bool MultiChoiceButton(const char* Label, bool IsSelected, const ImVec2& Size = ImVec2(0, 0)); + + static bool MultiChoiceButtonsInt(TArray& Values, int32& Value, const ImVec2& Size = ImVec2(0, 0)); + + static bool MultiChoiceButtonsFloat(TArray& Values, float& Value, const ImVec2& Size = ImVec2(0, 0)); + static void SliderWithReset(const char* Name, float* Value, float Min, float Max, const float& ResetValue, const char* Format); static void HelpMarker(const char* Text); diff --git a/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Cheats.cpp b/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Cheats.cpp index 346b4a5..030691f 100644 --- a/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Cheats.cpp +++ b/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Cheats.cpp @@ -48,6 +48,24 @@ void UCogAbilityWindow_Cheats::ResetConfig() void UCogAbilityWindow_Cheats::SetAsset(const UCogAbilityDataAsset* Value) { Asset = Value; +} + + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogAbilityWindow_Cheats::GameTick(float DeltaTime) +{ + Super::GameTick(DeltaTime); + + TryReapplyCheats(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogAbilityWindow_Cheats::TryReapplyCheats() +{ + if (bHasReappliedCheats) + { + return; + } if (bReapplyCheatsBetweenPlays == false) { @@ -78,7 +96,7 @@ void UCogAbilityWindow_Cheats::SetAsset(const UCogAbilityDataAsset* Value) return; } - TArray Targets{ LocalPawn }; + TArray Targets { LocalPawn }; for (const FString& AppliedCheatName : AppliedCheats) { @@ -88,6 +106,8 @@ void UCogAbilityWindow_Cheats::SetAsset(const UCogAbilityDataAsset* Value) Replicator->ApplyCheat(LocalPawn, Targets, *Cheat); } } + + bHasReappliedCheats = true; } //-------------------------------------------------------------------------------------------------------------------------- diff --git a/Plugins/CogAbility/Source/CogAbility/Public/CogAbilityWindow_Cheats.h b/Plugins/CogAbility/Source/CogAbility/Public/CogAbilityWindow_Cheats.h index 9827f6b..52021ea 100644 --- a/Plugins/CogAbility/Source/CogAbility/Public/CogAbilityWindow_Cheats.h +++ b/Plugins/CogAbility/Source/CogAbility/Public/CogAbilityWindow_Cheats.h @@ -23,17 +23,19 @@ public: protected: + virtual void GameTick(float DeltaTime); + virtual void ResetConfig() override; virtual void RenderHelp() override; virtual void RenderContent() override; -private: - - bool AddCheat(AActor* ControlledActor, AActor* TargetActor, const FCogAbilityCheat& CheatEffect, bool IsPersistent); + virtual void TryReapplyCheats(); - void RequestCheat(AActor* ControlledActor, AActor* TargetActor, const FCogAbilityCheat& CheatEffect); + virtual bool AddCheat(AActor* ControlledActor, AActor* TargetActor, const FCogAbilityCheat& CheatEffect, bool IsPersistent); + + virtual void RequestCheat(AActor* ControlledActor, AActor* TargetActor, const FCogAbilityCheat& CheatEffect); UPROPERTY(Config) bool bReapplyCheatsBetweenPlays = true; @@ -46,4 +48,6 @@ private: UPROPERTY() TObjectPtr Asset = nullptr; + + bool bHasReappliedCheats = false; }; diff --git a/Source/CogSample/CogSample.Build.cs b/Source/CogSample/CogSample.Build.cs index c1f1827..ec5da5c 100644 --- a/Source/CogSample/CogSample.Build.cs +++ b/Source/CogSample/CogSample.Build.cs @@ -19,6 +19,7 @@ public class CogSample : ModuleRules "GameplayTags", "InputCore", "NetCore", + "Niagara", }); if (Target.Configuration != UnrealTargetConfiguration.Shipping && Target.Type != TargetRules.TargetType.Server) diff --git a/Source/CogSample/CogSampleAbilityTask_PlayMontageAndWaitForEvent.cpp b/Source/CogSample/CogSampleAbilityTask_PlayMontageAndWaitForEvent.cpp index e52fe10..4f7ddf1 100644 --- a/Source/CogSample/CogSampleAbilityTask_PlayMontageAndWaitForEvent.cpp +++ b/Source/CogSample/CogSampleAbilityTask_PlayMontageAndWaitForEvent.cpp @@ -29,7 +29,7 @@ void UCogSampleAbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendingOut(UAni // Reset AnimRootMotionTranslationScale ACharacter* Character = Cast(GetAvatarActor()); if (Character && (Character->GetLocalRole() == ROLE_Authority || - (Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted))) + (Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::Predicted))) { Character->SetAnimRootMotionTranslationScale(1.f); } @@ -158,7 +158,7 @@ void UCogSampleAbilityTask_PlayMontageAndWaitForEvent::Activate() ACharacter* Character = Cast(GetAvatarActor()); if (Character && (Character->GetLocalRole() == ROLE_Authority || - (Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted))) + (Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::Predicted))) { Character->SetAnimRootMotionTranslationScale(AnimRootMotionTranslationScale); } diff --git a/Source/CogSample/CogSampleArea.cpp b/Source/CogSample/CogSampleArea.cpp deleted file mode 100644 index 583638a..0000000 --- a/Source/CogSample/CogSampleArea.cpp +++ /dev/null @@ -1,434 +0,0 @@ -#include "CogSampleArea.h" - -#include "AbilitySystemComponent.h" -#include "AbilitySystemGlobals.h" -#include "CogCommon.h" -#include "CogSampleDamageableInterface.h" -#include "CogSampleFunctionLibrary_Team.h" -#include "Engine/World.h" -#include "GameFramework/GameStateBase.h" -#include "Net/UnrealNetwork.h" -#include "TimerManager.h" - -//-------------------------------------------------------------------------------------------------------------------------- -ACogSampleArea::ACogSampleArea(const class FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - bReplicates = true; - - PrimaryActorTick.bCanEverTick = true; - SetActorTickEnabled(true); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const -{ - Super::GetLifetimeReplicatedProps(OutLifetimeProps); - - DOREPLIFETIME(ACogSampleArea, HalfExtent); - DOREPLIFETIME(ACogSampleArea, Level); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::BeginPlay() -{ - COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetInstigator(), TEXT("Area:%s"), *GetName()); - - IsAtStart = true; - IsAtEnd = false; - - if (HasAuthority()) - { - if (TickRate > 0.0f && IsInstant == false) - { - GetWorld()->GetTimerManager().SetTimer(TickTimerHandle, this, &ACogSampleArea::OnTickEffect, TickRate, true, InitialTickDelay); - } - - RegisterAllEffects(); - - OnActorBeginOverlap.AddDynamic(this, &ACogSampleArea::OnActorEntered); - OnActorEndOverlap.AddDynamic(this, &ACogSampleArea::OnActorExited); - } - - Super::BeginPlay(); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::EndPlay(const EEndPlayReason::Type EndPlayReason) -{ - COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetInstigator(), TEXT("Area:%s"), *GetName()); - - IsAtEnd = true; - - if (CanPerformDetection() && IsInstant == false) - { - int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnEnd; - - if (ApplyTickEffectOnExit) - { - EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick; - } - - ApplyEffectsOnActors(InsideActors, EventTypeFilter); - - for (AActor* Actor : InsideActors) - { - AffectExitingActor(Actor); - } - } - - InsideActors.Empty(); - - Super::EndPlay(EndPlayReason); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::Tick(float DeltaSeconds) -{ - Super::Tick(DeltaSeconds); - - //------------------------------------------------------------------------------ - // We cannot execute OnStart event inside the OnActorEntered callback because - // we need to know all the actors that are inside to execute the OnStart event. - // This is because we must iterate over all actors for each effects we need - // to apply. See ApplyEffectsOnActors as to why. - // So instead we execute it on Tick - //------------------------------------------------------------------------------ - if (IsAtStart) - { - int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnStart; - - if (ApplyTickEffectOnEnter) - { - EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick; - } - - ApplyEffectsOnActors(InsideActors, EventTypeFilter); - - IsAtStart = false; - } -} - -//-------------------------------------------------------------------------------------------------------------------------- -AActor* ACogSampleArea::GetInstigatorActor() const -{ - AActor* AreaInstigator = GetInstigator(); - if (AreaInstigator != nullptr) - { - return AreaInstigator; - } - - if (ParentActor != nullptr) - { - return ParentActor; - } - - //------------------------------------------------- - // The game state is the default Instigator - //------------------------------------------------- - UWorld* World = GetWorld(); - if (World != nullptr) - { - return World->GetGameState(); - } - - return nullptr; -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::RegisterAllEffects() -{ - AActor* AreaInstigator = GetInstigatorActor(); - if (AreaInstigator == nullptr) - { - return; - } - - UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(AreaInstigator); - if (AbilitySystem == nullptr) - { - return; - } - - FGameplayEffectContextHandle EffectContextHandle = AbilitySystem->MakeEffectContext(); - - for (const FCogSampleAreaEffectConfig& EffectConfig : Effects) - { - for (TSubclassOf EffectClass : EffectConfig.Effects) - { - if (EffectsMap.Contains(EffectClass)) - { - continue; - } - - FGameplayEffectSpecHandle* BakedEffectSpecHandle = BakedEffects.FindByPredicate([EffectClass](const FGameplayEffectSpecHandle& Handle) - { - return Handle.Data->Def.GetClass() == EffectClass; - }); - - if (BakedEffectSpecHandle != nullptr) - { - EffectsMap.Add(EffectClass, *BakedEffectSpecHandle); - } - else - { - FGameplayEffectSpecHandle EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, EffectContextHandle); - EffectsMap.Add(EffectClass, EffectSpecHandle); - } - } - } -} - -//-------------------------------------------------------------------------------------------------------------------------- -// We iterate over effects and then over actors to make sure effects are applied in order on all actors. -// One case where this is useful is an area that applies first a corruption effect and damage effect. -// The corruption must be given to everyone in the area before any damage is applied, because the corruption -// propagate the damage to other corrupted actors. -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::ApplyEffectsOnActors(const TArray& HitActors, int32 EventTypeFilter) -{ - for (const FCogSampleAreaEffectConfig& EffectConfig : Effects) - { - if (((int32)EffectConfig.Event & (int32)EventTypeFilter) == 0) - { - continue; - } - - for (TSubclassOf EffectClass : EffectConfig.Effects) - { - FGameplayEffectSpecHandle* Handle = EffectsMap.Find(EffectClass); - if (Handle == nullptr) - { - continue; - } - - FGameplayEffectSpec* Spec = Handle->Data.Get(); - if (Spec == nullptr) - { - continue; - } - - for (AActor* HitActor : HitActors) - { - if (UCogSampleFunctionLibrary_Team::MatchAllegianceBetweenTeamAndActor(Team, HitActor, EffectConfig.Allegiance) == false) - { - continue; - } - - if (IsAliveOfAffectDead(HitActor) == false) - { - continue; - } - - UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor); - if (TargetAbilitySystem != nullptr) - { - TArray* TargetInsideEffects = nullptr; - if ((EventTypeFilter & (int32)ECogSampleAreaEventType::OnInside) != 0) - { - TargetInsideEffects = &InsideEffects.FindOrAdd(HitActor); - } - - const FVector HitNormal = (HitActor->GetActorLocation() - GetActorLocation()) .GetSafeNormal(0.1f, FVector::UpVector); - const FHitResult HitResult(HitActor, nullptr, HitActor->GetActorLocation(), HitNormal); - Spec->GetContext().AddHitResult(HitResult, true); - - FActiveGameplayEffectHandle ActiveEffectHandle = TargetAbilitySystem->ApplyGameplayEffectSpecToSelf(*Spec); - - if (EffectConfig.Event == ECogSampleAreaEventType::OnInside && TargetInsideEffects != nullptr) - { - TargetInsideEffects->Add(ActiveEffectHandle); - } - } - } - } - } -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool ACogSampleArea::MakeAreaOutgoingEffectSpecs(TSubclassOf EffectClass, FGameplayEffectSpecHandle& EffectSpecHandle) const -{ - AActor* AreaInstigator = GetInstigatorActor(); - if (AreaInstigator == nullptr) - { - return false; - } - - UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(AreaInstigator); - if (AbilitySystem == nullptr) - { - return false; - } - - EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, AbilitySystem->MakeEffectContext()); - return true; -} - - -//-------------------------------------------------------------------------------------------------------------------------- -bool ACogSampleArea::CanPerformDetection() const -{ - if (OnlyDetectOnAuthority == false) - { - return true; - } - - if (HasAuthority()) - { - return true; - } - - return false; -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::OnTickEffect_Implementation() -{ - ApplyEffectsOnActors(InsideActors, (int32)ECogSampleAreaEventType::OnTick); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::OnActorEntered_Implementation(AActor* OverlappedActor, AActor* EnteringActor) -{ - check(EnteringActor); - if (CanBeAffected(EnteringActor) == false) - { - return; - } - - AffectEnteringActor(EnteringActor); - InsideActors.Add(EnteringActor); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::OnActorExited_Implementation(AActor* OverlappedActor, AActor* ExitingActor) -{ - if (IsInstant) - { - return; - } - - int32 Index = InsideActors.Find(ExitingActor); - if (Index == INDEX_NONE) - { - return; - } - - AffectExitingActor(ExitingActor); - - InsideActors.RemoveAt(Index); -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool ACogSampleArea::CanBeAffected_Implementation(AActor* OtherActor) const -{ - check(OtherActor); - - if (IsAlreadyAffected(OtherActor)) - { - return false; - } - - return true; -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool ACogSampleArea::IsAliveOfAffectDead(AActor* OtherActor) const -{ - check(OtherActor); - - if (AffectDead) - { - return true; - } - - const ICogSampleDamageableInterface* Damageable = Cast(OtherActor); - if (Damageable == nullptr) - { - return true; - } - - if (Damageable->IsDead() == false) - { - return true; - } - - return false; -} - -//-------------------------------------------------------------------------------------------------------------------------- -bool ACogSampleArea::IsAlreadyAffected(AActor* OtherActor) const -{ - check(OtherActor); - - if (InsideActors.Contains(OtherActor)) - { - return true; - } - - return false; -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::AffectEnteringActor_Implementation(AActor* EnteringActor) -{ - check(EnteringActor); - - if (IsInstant == false) - { - TArray Actors; - Actors.Add(EnteringActor); - ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnEnter | (int32)ECogSampleAreaEventType::OnInside); - } -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::AffectExitingActor_Implementation(AActor* ExitingActor) -{ - if (IsInstant || ExitingActor == nullptr) - { - return; - } - - TArray Actors; - Actors.Add(ExitingActor); - ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnExit); - - RemoveInsideEffects(ExitingActor); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::RemoveInsideEffects_Implementation(AActor* HitActor) -{ - UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor); - if (TargetAbilitySystem == nullptr) - { - return; - } - - TArray* TargetInsideEffects = InsideEffects.Find(HitActor); - if (TargetInsideEffects == nullptr) - { - return; - } - - for (FActiveGameplayEffectHandle Handle : *TargetInsideEffects) - { - TargetAbilitySystem->RemoveActiveGameplayEffect(Handle, 1); - } -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::ApplyInstantEffects_Implementation(AActor* HitActor) -{ -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::ApplyInsideEffects_Implementation(AActor* HitActor) -{ -} - -//-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleArea::ApplyTickEffects_Implementation(AActor* HitActor) -{ -} diff --git a/Source/CogSample/CogSampleAreaComponent.cpp b/Source/CogSample/CogSampleAreaComponent.cpp new file mode 100644 index 0000000..caf0dcc --- /dev/null +++ b/Source/CogSample/CogSampleAreaComponent.cpp @@ -0,0 +1,472 @@ +#include "CogSampleAreaComponent.h" + +#include "AbilitySystemComponent.h" +#include "AbilitySystemGlobals.h" +#include "CogCommon.h" +#include "CogSampleDamageableInterface.h" +#include "CogSampleFunctionLibrary_Gameplay.h" +#include "CogSampleFunctionLibrary_Team.h" +#include "CogSampleLogCategories.h" +#include "Components/BoxComponent.h" +#include "Components/SphereComponent.h" +#include "Engine/World.h" +#include "GameFramework/GameStateBase.h" +#include "Net/UnrealNetwork.h" +#include "NiagaraComponent.h" +#include "TimerManager.h" + +#if ENABLE_COG +#include "CogDebugDraw.h" +#include "CogDebugLog.h" +#endif //ENABLE_COG + +//-------------------------------------------------------------------------------------------------------------------------- +UCogSampleAreaComponent::UCogSampleAreaComponent(const class FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + SetIsReplicatedByDefault(true); + + PrimaryComponentTick.bCanEverTick = true; + PrimaryComponentTick.bStartWithTickEnabled = true; + + bWantsInitializeComponent = true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::InitializeComponent() +{ + Super::InitializeComponent(); + + RefreshOtherComponentsValues(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +//void UCogSampleAreaComponent::PreInitializeComponents() +//{ +// Super::PreInitializeComponents(); +// +// RefreshOtherComponentsValues(); +//} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + FDoRepLifetimeParams Params; + Params.bIsPushBased = true; + + DOREPLIFETIME_WITH_PARAMS_FAST(UCogSampleAreaComponent, HalfExtent, Params); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::BeginPlay() +{ + COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetOwner()->GetInstigator(), TEXT("Area:%s"), *GetName()); + + IsAtStart = true; + IsAtEnd = false; + + if (GetOwner()->HasAuthority()) + { + if (TickRate > 0.0f && DurationType != ECogSampleAreaDurationType::Instant) + { + GetWorld()->GetTimerManager().SetTimer(TickTimerHandle, this, &UCogSampleAreaComponent::OnTickEffect, TickRate, true, InitialTickDelay); + } + + if (DurationType == ECogSampleAreaDurationType::HasDuration) + { + GetWorld()->GetTimerManager().SetTimer(DurationTimerHandle, this, &UCogSampleAreaComponent::OnDurationElapsed, Duration, false); + } + + RegisterAllEffects(); + + GetOwner()->OnActorBeginOverlap.AddDynamic(this, &UCogSampleAreaComponent::OnActorEntered); + GetOwner()->OnActorEndOverlap.AddDynamic(this, &UCogSampleAreaComponent::OnActorExited); + + + } + + Super::BeginPlay(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetOwner()->GetInstigator(), TEXT("Area:%s"), *GetName()); + + IsAtEnd = true; + + if (CanPerformDetection() && DurationType != ECogSampleAreaDurationType::Instant) + { + int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnEnd; + + if (ApplyTickEffectOnExit) + { + EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick; + } + + ApplyEffectsOnActors(InsideActors, EventTypeFilter); + + for (AActor* Actor : InsideActors) + { + AffectExitingActor(Actor); + } + } + + InsideActors.Empty(); + + Super::EndPlay(EndPlayReason); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::OnDurationElapsed_Implementation() +{ + GetOwner()->Destroy(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + //------------------------------------------------------------------------------ + // We cannot execute OnStart event inside the OnActorEntered callback because + // we need to know all the actors that are inside to execute the OnStart event. + // This is because we must iterate over all actors for each effects we need + // to apply. See ApplyEffectsOnActors as to why. + // So instead we execute it on Tick + //------------------------------------------------------------------------------ + if (IsAtStart) + { + int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnStart; + + if (ApplyTickEffectOnEnter) + { + EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick; + } + + ApplyEffectsOnActors(InsideActors, EventTypeFilter); + + IsAtStart = false; + } + +#if ENABLE_COG + + const AActor* AreaInstigator = UCogSampleFunctionLibrary_Gameplay::GetInstigator(GetOwner()); + if (FCogDebugLog::IsLogCategoryActive(LogCogArea) && FCogDebugSettings::IsDebugActiveForObject(AreaInstigator)) + { + TArray> Components; + GetOwner()->GetComponents(Components); + for (USceneComponent* SceneComponent : Components) + { + if (USphereComponent* SphereComponent = Cast(SceneComponent)) + { + FCogDebugDraw::Sphere(LogCogArea, this, SphereComponent->GetComponentLocation(), SphereComponent->GetScaledSphereRadius(), FColor::Yellow, false, 0); + } + else if (UBoxComponent* BoxComponent = Cast(SceneComponent)) + { + FCogDebugDraw::Box(LogCogArea, this, BoxComponent->GetComponentLocation(), BoxComponent->GetScaledBoxExtent(), BoxComponent->GetComponentQuat(), FColor::Yellow, false, 0); + } + } + } +#endif //ENABLE_COG + +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::RegisterAllEffects() +{ + TArray> AllEffects; + + for (const FCogSampleAreaEffectConfig& EffectConfig : Effects) + { + for (TSubclassOf EffectClass : EffectConfig.Effects) + { + AllEffects.Add(EffectClass); + } + } + + UCogSampleFunctionLibrary_Gameplay::MakeOutgoingSpecs(GetOwner(), AllEffects, BakedEffects, EffectsMap); +} + +//-------------------------------------------------------------------------------------------------------------------------- +// We iterate over effects and then over actors to make sure effects are applied in order on all actors. +// One case where this is useful is an area that applies first a corruption effect and damage effect. +// The corruption must be given to everyone in the area before any damage is applied, because the corruption +// propagate the damage to other corrupted actors. +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::ApplyEffectsOnActors(const TArray& HitActors, int32 EventTypeFilter) +{ + for (const FCogSampleAreaEffectConfig& EffectConfig : Effects) + { + if (((int32)EffectConfig.Event & (int32)EventTypeFilter) == 0) + { + continue; + } + + for (TSubclassOf EffectClass : EffectConfig.Effects) + { + FGameplayEffectSpecHandle* Handle = EffectsMap.Find(EffectClass); + if (Handle == nullptr) + { + continue; + } + + FGameplayEffectSpec* Spec = Handle->Data.Get(); + if (Spec == nullptr) + { + continue; + } + + for (AActor* HitActor : HitActors) + { + if (UCogSampleFunctionLibrary_Team::MatchAllegiance(GetOwner(), HitActor, EffectConfig.Allegiance) == false) + { + continue; + } + + if (UCogSampleFunctionLibrary_Gameplay::IsDead(HitActor) && EffectConfig.AffectDead == false) + { + continue; + } + + UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor); + if (TargetAbilitySystem == nullptr) + { + continue; + } + + TArray* TargetInsideEffects = nullptr; + if ((EventTypeFilter & (int32)ECogSampleAreaEventType::OnInside) != 0) + { + TargetInsideEffects = &InsideEffects.FindOrAdd(HitActor); + } + + const FVector HitNormal = (HitActor->GetActorLocation() - GetOwner()->GetActorLocation()) .GetSafeNormal(0.1f, FVector::UpVector); + const FHitResult HitResult(HitActor, nullptr, HitActor->GetActorLocation(), HitNormal); + Spec->GetContext().AddHitResult(HitResult, true); + + FActiveGameplayEffectHandle ActiveEffectHandle = TargetAbilitySystem->ApplyGameplayEffectSpecToSelf(*Spec); + + if (EffectConfig.Event == ECogSampleAreaEventType::OnInside && TargetInsideEffects != nullptr) + { + TargetInsideEffects->Add(ActiveEffectHandle); + } + } + } + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleAreaComponent::MakeAreaOutgoingEffectSpecs(TSubclassOf EffectClass, FGameplayEffectSpecHandle& EffectSpecHandle) const +{ + AActor* AreaInstigator = UCogSampleFunctionLibrary_Gameplay::GetInstigator(GetOwner()); + if (AreaInstigator == nullptr) + { + return false; + } + + UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(AreaInstigator); + if (AbilitySystem == nullptr) + { + return false; + } + + const int32 Level = UCogSampleFunctionLibrary_Gameplay::GetProgressionLevel(GetOwner()); + EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, AbilitySystem->MakeEffectContext()); + + return true; +} + + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleAreaComponent::CanPerformDetection() const +{ + if (OnlyDetectOnAuthority == false) + { + return true; + } + + if (GetOwner()->HasAuthority()) + { + return true; + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::OnTickEffect_Implementation() +{ + ApplyEffectsOnActors(InsideActors, (int32)ECogSampleAreaEventType::OnTick); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::OnActorEntered_Implementation(AActor* OverlappedActor, AActor* EnteringActor) +{ + check(EnteringActor); + if (CanBeAffected(EnteringActor) == false) + { + return; + } + + AffectEnteringActor(EnteringActor); + InsideActors.Add(EnteringActor); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::OnActorExited_Implementation(AActor* OverlappedActor, AActor* ExitingActor) +{ + if (DurationType == ECogSampleAreaDurationType::Instant) + { + return; + } + + int32 Index = InsideActors.Find(ExitingActor); + if (Index == INDEX_NONE) + { + return; + } + + AffectExitingActor(ExitingActor); + + InsideActors.RemoveAt(Index); +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleAreaComponent::CanBeAffected_Implementation(AActor* OtherActor) const +{ + check(OtherActor); + + if (IsAlreadyAffected(OtherActor)) + { + return false; + } + + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleAreaComponent::IsAlreadyAffected(AActor* OtherActor) const +{ + check(OtherActor); + + if (InsideActors.Contains(OtherActor)) + { + return true; + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::AffectEnteringActor_Implementation(AActor* EnteringActor) +{ + check(EnteringActor); + + if (DurationType == ECogSampleAreaDurationType::Instant) + { + return; + } + + TArray Actors; + Actors.Add(EnteringActor); + ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnEnter | (int32)ECogSampleAreaEventType::OnInside); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::AffectExitingActor_Implementation(AActor* ExitingActor) +{ + if (DurationType == ECogSampleAreaDurationType::Instant || ExitingActor == nullptr) + { + return; + } + + TArray Actors; + Actors.Add(ExitingActor); + ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnExit); + + RemoveInsideEffects(ExitingActor); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::RemoveInsideEffects_Implementation(AActor* HitActor) +{ + UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor); + if (TargetAbilitySystem == nullptr) + { + return; + } + + TArray* TargetInsideEffects = InsideEffects.Find(HitActor); + if (TargetInsideEffects == nullptr) + { + return; + } + + for (FActiveGameplayEffectHandle Handle : *TargetInsideEffects) + { + TargetAbilitySystem->RemoveActiveGameplayEffect(Handle, 1); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::ApplyInstantEffects_Implementation(AActor* HitActor) +{ +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::ApplyInsideEffects_Implementation(AActor* HitActor) +{ +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::ApplyTickEffects_Implementation(AActor* HitActor) +{ +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::SetHalfExtent(const FVector& Value) +{ + COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(UCogSampleAreaComponent, HalfExtent, Value, this); + RefreshOtherComponentsValues(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::OnRep_HalfExtent() +{ + RefreshOtherComponentsValues(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAreaComponent::RefreshOtherComponentsValues() +{ + TArray> Components; + GetOwner()->GetComponents(Components); + + for (USceneComponent* SceneComponent : Components) + { + if (USphereComponent* SphereComponent = Cast(SceneComponent)) + { + if (SphereComponent->GetUnscaledSphereRadius() != HalfExtent.X) + { + SphereComponent->SetSphereRadius(HalfExtent.X); + } + } + else if (UBoxComponent* BoxComponent = Cast(SceneComponent)) + { + if (BoxComponent->GetUnscaledBoxExtent() != HalfExtent) + { + BoxComponent->SetBoxExtent(HalfExtent); + } + } + else if (UNiagaraComponent* NiagaraComponent = Cast(SceneComponent)) + { + NiagaraComponent->SetNiagaraVariableVec3("HalfExtent", HalfExtent); + NiagaraComponent->SetNiagaraVariableFloat("TickRate", TickRate); + NiagaraComponent->SetNiagaraVariableFloat("Duration", Duration); + } + } +} + diff --git a/Source/CogSample/CogSampleArea.h b/Source/CogSample/CogSampleAreaComponent.h similarity index 70% rename from Source/CogSample/CogSampleArea.h rename to Source/CogSample/CogSampleAreaComponent.h index 871af3b..28b73b1 100644 --- a/Source/CogSample/CogSampleArea.h +++ b/Source/CogSample/CogSampleAreaComponent.h @@ -1,11 +1,9 @@ #pragma once #include "CoreMinimal.h" - -#include "GameFramework/Actor.h" +#include "Components/ActorComponent.h" #include "GameplayEffect.h" - -#include "CogSampleArea.generated.h" +#include "CogSampleAreaComponent.generated.h" //-------------------------------------------------------------------------------------------------------------------------- // ECogSampleAreaEventType @@ -27,7 +25,7 @@ ENUM_CLASS_FLAGS(ECogSampleAreaEventType); // FCogSampleAreaEffectConfig //-------------------------------------------------------------------------------------------------------------------------- USTRUCT(BlueprintType) -struct COGSAMPLE_API FCogSampleAreaEffectConfig +struct FCogSampleAreaEffectConfig { GENERATED_BODY() @@ -37,72 +35,88 @@ struct COGSAMPLE_API FCogSampleAreaEffectConfig UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "/Script/CogSample.ECogSampleAllegianceFilter")) int32 Allegiance = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool AffectDead = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray> Effects; }; +//-------------------------------------------------------------------------------------------------------------------------- +// ECogSampleAreaDurationType +//-------------------------------------------------------------------------------------------------------------------------- +UENUM() +enum class ECogSampleAreaDurationType : uint8 +{ + Instant, + Infinite, + HasDuration +}; + //-------------------------------------------------------------------------------------------------------------------------- // ACogSampleArea //-------------------------------------------------------------------------------------------------------------------------- -UCLASS() -class COGSAMPLE_API ACogSampleArea : public AActor +UCLASS(BlueprintType, meta = (BlueprintSpawnableComponent)) +class UCogSampleAreaComponent : public UActorComponent { GENERATED_UCLASS_BODY() public: - + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; + virtual void InitializeComponent() override; + + //virtual void PreInitializeComponents() override; + virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; - virtual void Tick(float DeltaSeconds) override; + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true)) - TObjectPtr ParentActor = nullptr; + FVector GetHalfExtent() const { return HalfExtent; } - UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true)) - FVector HalfExtent = FVector(100.f, 100.f, 100.f); + void SetHalfExtent(const FVector& Value); - UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true)) - int32 Level = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area") + ECogSampleAreaDurationType DurationType; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true)) - int32 Team = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType == ECogSampleAreaDurationType::HasDuration")) + float Duration = 0.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true)) - bool IsInstant = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true)) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant")) float TickRate = 0.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true)) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant")) float InitialTickDelay = 0.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant")) bool ApplyTickEffectOnEnter = false; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant")) bool ApplyTickEffectOnExit = false; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area") bool OnlyDetectOnAuthority = true; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General") - bool AffectDead = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|Effects", meta = (ExposeOnSpawn = true)) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area") TArray Effects; - UPROPERTY(Transient, BlueprintReadWrite, Category = "Internal", meta = (ExposeOnSpawn = true)) + UPROPERTY(Transient, BlueprintReadWrite, Category = "Internal") TArray BakedEffects; protected: + UFUNCTION(Category = "Area") + void OnRep_HalfExtent(); + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Area") void OnTickEffect(); + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Area") + void OnDurationElapsed(); + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Area") void OnActorEntered(AActor* OverlappedActor, AActor* EnteringActor); @@ -133,21 +147,20 @@ protected: UFUNCTION(BlueprintPure, Category = "Area") bool CanPerformDetection() const; - UFUNCTION(BlueprintPure, Category = "Area") - bool IsAliveOfAffectDead(AActor* OtherActor) const; - UFUNCTION(BlueprintPure, Category = "Area") bool IsAlreadyAffected(AActor* OtherActor) const; - UFUNCTION(BlueprintPure, Category = "Area") - AActor* GetInstigatorActor() const; - UFUNCTION(BlueprintCallable, BlueprintPure=false, Category = "Area") bool MakeAreaOutgoingEffectSpecs(TSubclassOf EffectClass, FGameplayEffectSpecHandle& EffectSpecHandle) const; - void RegisterAllEffects(); + UPROPERTY(ReplicatedUsing = OnRep_HalfExtent, EditAnywhere, BlueprintReadWrite, Category = "Area") + FVector HalfExtent = FVector(100.f, 100.f, 100.f); - void ApplyEffectsOnActors(const TArray& HitActors, int32 EventTypeFilter); + virtual void RegisterAllEffects(); + + virtual void ApplyEffectsOnActors(const TArray& HitActors, int32 EventTypeFilter); + + virtual void RefreshOtherComponentsValues(); UPROPERTY(BlueprintReadOnly, Transient) bool IsAtStart = true; @@ -162,4 +175,6 @@ protected: TMap> InsideEffects; FTimerHandle TickTimerHandle; + + FTimerHandle DurationTimerHandle; }; diff --git a/Source/CogSample/CogSampleBasicActor.cpp b/Source/CogSample/CogSampleBasicActor.cpp new file mode 100644 index 0000000..f5b71d7 --- /dev/null +++ b/Source/CogSample/CogSampleBasicActor.cpp @@ -0,0 +1,41 @@ +#include "CogSampleBasicActor.h" + +#include "Net/Core/PushModel/PushModel.h" +#include "Net/UnrealNetwork.h" + + +//-------------------------------------------------------------------------------------------------------------------------- +ACogSampleBasicActor::ACogSampleBasicActor(const FObjectInitializer& ObjectInitializer) +{ +} + +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSampleBasicActor::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + FDoRepLifetimeParams Params; + Params.bIsPushBased = true; + + DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleBasicActor, Team, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleBasicActor, ProgressionLevel, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleBasicActor, Creator, Params); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSampleBasicActor::SetTeam(int32 Value) +{ + COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleBasicActor, Team, Value, this); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSampleBasicActor::SetProgressionLevel(int32 Value) +{ + COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleBasicActor, ProgressionLevel, Value, this); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSampleBasicActor::SetCreator(AActor* Value) +{ + COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleBasicActor, Creator, Value, this); +} diff --git a/Source/CogSample/CogSampleBasicActor.h b/Source/CogSample/CogSampleBasicActor.h new file mode 100644 index 0000000..e60e977 --- /dev/null +++ b/Source/CogSample/CogSampleBasicActor.h @@ -0,0 +1,52 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogSampleProgressionLevelInterface.h" +#include "CogSampleTeamInterface.h" +#include "CogSampleSpawnableInterface.h" +#include "GameFramework/Actor.h" +#include "CogSampleBasicActor.generated.h" + +//-------------------------------------------------------------------------------------------------------------------------- +UCLASS(config=Game) +class ACogSampleBasicActor : public AActor + , public ICogSampleProgressionLevelInterface + , public ICogSampleSpawnableInterface + , public ICogSampleTeamInterface +{ + GENERATED_BODY() + +public: + + ACogSampleBasicActor(const FObjectInitializer& ObjectInitializer); + + UFUNCTION(BlueprintPure) + virtual int32 GetTeam() const override { return Team; } + + UFUNCTION(BlueprintCallable) + virtual void SetTeam(int32 Value) override; + + UFUNCTION(BlueprintPure) + virtual int32 GetProgressionLevel() const override { return ProgressionLevel; } + + UFUNCTION(BlueprintCallable) + virtual void SetProgressionLevel(int32 Value) override; + + UFUNCTION(BlueprintPure) + virtual AActor* GetCreator() const override { return Creator.Get(); } + + UFUNCTION(BlueprintCallable) + virtual void SetCreator(AActor* Value) override; + +protected: + + UPROPERTY(Replicated, EditAnywhere, BlueprintReadOnly, Category = Team, meta = (AllowPrivateAccess, ExposeOnSpawn)) + int32 Team = 0; + + UPROPERTY(Replicated, EditAnywhere, BlueprintReadOnly, Category = Team, meta = (AllowPrivateAccess, ExposeOnSpawn)) + int32 ProgressionLevel = 0; + + UPROPERTY(Replicated, EditAnywhere, BlueprintReadOnly, Category = Team, meta = (AllowPrivateAccess, ExposeOnSpawn)) + TWeakObjectPtr Creator = nullptr; +}; + diff --git a/Source/CogSample/CogSampleCharacter.cpp b/Source/CogSample/CogSampleCharacter.cpp index 2154b10..eef9ec9 100644 --- a/Source/CogSample/CogSampleCharacter.cpp +++ b/Source/CogSample/CogSampleCharacter.cpp @@ -77,13 +77,14 @@ void ACogSampleCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > FDoRepLifetimeParams Params; Params.bIsPushBased = true; - Params.Condition = COND_OwnerOnly; - DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, ActiveAbilityHandles, Params); - DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, Team, Params); - Params.Condition = COND_None; + DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, Team, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, ProgressionLevel, Params); DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, Scale, Params); + + Params.Condition = COND_OwnerOnly; + DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, ActiveAbilityHandles, Params); } @@ -709,10 +710,15 @@ void ACogSampleCharacter::OnRep_Scale() } //-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleCharacter::SetTeamID(int32 Value) +void ACogSampleCharacter::SetTeam(int32 Value) { - Team = Value; - MARK_PROPERTY_DIRTY_FROM_NAME(ACogSampleCharacter, Team, this); + COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleCharacter, Team, Value, this); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSampleCharacter::SetProgressionLevel(int32 Value) +{ + COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleCharacter, ProgressionLevel, Value, this); } //-------------------------------------------------------------------------------------------------------------------------- diff --git a/Source/CogSample/CogSampleCharacter.h b/Source/CogSample/CogSampleCharacter.h index 4dc9e72..fe4e54e 100644 --- a/Source/CogSample/CogSampleCharacter.h +++ b/Source/CogSample/CogSampleCharacter.h @@ -1,16 +1,16 @@ #pragma once -#include "CoreMinimal.h" #include "AbilitySystemInterface.h" #include "ActiveGameplayEffectHandle.h" #include "AttributeSet.h" #include "CogCommonAllegianceActorInterface.h" #include "CogCommonDebugFilteredActorInterface.h" -#include "CogSampleDamageEvent.h" -#include "CogSampleDefines.h" #include "CogSampleDamageableInterface.h" +#include "CogSampleDefines.h" +#include "CogSampleProgressionLevelInterface.h" #include "CogSampleTargetableInterface.h" #include "CogSampleTeamInterface.h" +#include "CoreMinimal.h" #include "GameFramework/Character.h" #include "GameplayAbilitySpecHandle.h" #include "GameplayTagContainer.h" @@ -82,6 +82,7 @@ class ACogSampleCharacter : public ACharacter , public ICogCommonDebugFilteredActorInterface , public ICogCommonAllegianceActorInterface , public ICogSampleTeamInterface + , public ICogSampleProgressionLevelInterface , public ICogSampleTargetableInterface , public ICogSampleDamageableInterface { @@ -151,10 +152,16 @@ public: virtual int32 GetTeam() const override { return Team; } UFUNCTION(BlueprintCallable) - void SetTeamID(int32 Value); + virtual void SetTeam(int32 Value) override; - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Team, Replicated, meta = (AllowPrivateAccess = "true")) - int32 Team = 0; + //---------------------------------------------------------------------------------------------------------------------- + // Level + //---------------------------------------------------------------------------------------------------------------------- + + virtual int32 GetProgressionLevel() const override { return ProgressionLevel; } + + UFUNCTION(BlueprintCallable) + virtual void SetProgressionLevel(int32 Value) override; //---------------------------------------------------------------------------------------------------------------------- // Camera @@ -163,10 +170,10 @@ public: UCameraComponent* GetFollowCamera() const { return FollowCamera; } - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true")) + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess)) USpringArmComponent* CameraBoom; - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true")) + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess)) UCameraComponent* FollowCamera; //---------------------------------------------------------------------------------------------------------------------- @@ -179,26 +186,26 @@ public: FVector GetMoveInputInWorldSpace() const { return MoveInputInWorldSpace; } /** MappingContext */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess)) UInputMappingContext* DefaultMappingContext; /** MappingContext */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess)) UInputMappingContext* GhostMappingContext; /** Move Input Action */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess)) UInputAction* MoveAction; /** Move Input Action */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess)) UInputAction* MoveZAction; /** Look Input Action */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess)) UInputAction* LookAction; - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess)) TArray ItemActions; //---------------------------------------------------------------------------------------------------------------------- @@ -211,7 +218,7 @@ public: void OnRevived(AActor* InInstigator, AActor* InCauser, const FGameplayEffectSpec& InEffectSpec, float InMagnitude); - UPROPERTY(BlueprintReadOnly, Category = Ability, meta = (AllowPrivateAccess = "true")) + UPROPERTY(BlueprintReadOnly, Category = Ability, meta = (AllowPrivateAccess)) UCogSampleAbilitySystemComponent* AbilitySystem = nullptr; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Ability) @@ -259,11 +266,16 @@ public: UFUNCTION(BlueprintCallable, BlueprintPure=false) bool GetMontage(FName MontageName, UAnimMontage*& Montage, bool bPrintWarning) const; -private: +protected: friend class ACogSamplePlayerController; void RefreshServerAnimTickOption(); + UPROPERTY(EditAnywhere, Category = Team, Replicated, meta = (AllowPrivateAccess)) + int32 Team = 0; + + UPROPERTY(EditAnywhere, Category = Level, Replicated, meta = (AllowPrivateAccess)) + int32 ProgressionLevel = 0; UPROPERTY() AController* InitialController = nullptr; diff --git a/Source/CogSample/CogSampleDamageEvent.h b/Source/CogSample/CogSampleDamageEvent.h deleted file mode 100644 index 56c5ef1..0000000 --- a/Source/CogSample/CogSampleDamageEvent.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" diff --git a/Source/CogSample/CogSampleExecCalculation_Damage.h b/Source/CogSample/CogSampleExecCalculation_Damage.h index c3026a2..72bc012 100644 --- a/Source/CogSample/CogSampleExecCalculation_Damage.h +++ b/Source/CogSample/CogSampleExecCalculation_Damage.h @@ -21,7 +21,7 @@ public: }; UCLASS(meta = (ScriptName = "CogSampleFunctionLibrary_Damage")) -class COGSAMPLE_API UCogSampleFunctionLibrary_Damage : public UBlueprintFunctionLibrary +class UCogSampleFunctionLibrary_Damage : public UBlueprintFunctionLibrary { GENERATED_BODY() diff --git a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp index dcf05ce..66c4046 100644 --- a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp +++ b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp @@ -3,10 +3,14 @@ #include "Abilities/GameplayAbility.h" #include "AbilitySystemComponent.h" #include "AbilitySystemGlobals.h" +#include "CogSampleDamageableInterface.h" #include "CogSampleGameplayEffectContext.h" +#include "CogSampleProgressionLevelInterface.h" +#include "CogSampleSpawnableInterface.h" #include "CogSampleTargetableInterface.h" #include "Components/CapsuleComponent.h" #include "GameFramework/Character.h" +#include "GameFramework/GameStateBase.h" #include "GameplayCueNotifyTypes.h" #include "GameplayEffectTypes.h" #include "GameplayTagContainer.h" @@ -317,7 +321,7 @@ bool UCogSampleFunctionLibrary_Gameplay::IsActorMatchingTags(const AActor* Actor } //-------------------------------------------------------------------------------------------------------------------------- -AActor* UCogSampleFunctionLibrary_Gameplay::GetActorInstigator(AActor* Actor) +AActor* UCogSampleFunctionLibrary_Gameplay::GetInstigator(const AActor* Actor) { if (Actor == nullptr) { @@ -340,4 +344,149 @@ AActor* UCogSampleFunctionLibrary_Gameplay::GetActorInstigator(AActor* Actor) } return nullptr; -} \ No newline at end of file +} + +//-------------------------------------------------------------------------------------------------------------------------- +int32 UCogSampleFunctionLibrary_Gameplay::GetProgressionLevel(const AActor* Actor) +{ + if (Actor == nullptr) + { + return 0; + } + + const ICogSampleProgressionLevelInterface* LevelActor = Cast(Actor); + if (LevelActor == nullptr) + { + return 0; + } + + const int32 Value = LevelActor->GetProgressionLevel(); + return Value; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleFunctionLibrary_Gameplay::SetProgressionLevel(AActor* Actor, int32 Value) +{ + if (Actor == nullptr) + { + return; + } + + ICogSampleProgressionLevelInterface* LevelActor = Cast(Actor); + if (LevelActor == nullptr) + { + return; + } + + LevelActor->SetProgressionLevel(Value); +} + +//-------------------------------------------------------------------------------------------------------------------------- +AActor* UCogSampleFunctionLibrary_Gameplay::GetCreator(const AActor* Actor) +{ + if (Actor == nullptr) + { + return 0; + } + + const ICogSampleSpawnableInterface* Spawnable = Cast(Actor); + if (Spawnable == nullptr) + { + return 0; + } + + AActor* Value = Spawnable->GetCreator(); + return Value; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleFunctionLibrary_Gameplay::SetCreator(AActor* Actor, AActor* Value) +{ + if (Actor == nullptr) + { + return; + } + + ICogSampleSpawnableInterface* Spawnable = Cast(Actor); + if (Spawnable == nullptr) + { + return; + } + + Spawnable->SetCreator(Value); +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleFunctionLibrary_Gameplay::IsAlive(const AActor* Actor) +{ + if (Actor == nullptr) + { + return false; + } + + const ICogSampleDamageableInterface* Damageable = Cast(Actor); + if (Damageable == nullptr) + { + return true; + } + + if (Damageable->IsDead() == false) + { + return true; + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleFunctionLibrary_Gameplay::IsDead(const AActor* Actor) +{ + return IsAlive(Actor) == false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleFunctionLibrary_Gameplay::MakeOutgoingSpecs( + const AActor* Actor, + const TArray>& Effects, + const TArray& BakedEffects, + TMap, FGameplayEffectSpecHandle>& Results) +{ + const AActor* Instigator = UCogSampleFunctionLibrary_Gameplay::GetInstigator(Actor); + if (Instigator == nullptr) + { + return; + } + + const UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Instigator); + if (AbilitySystem == nullptr) + { + return; + } + + const int32 Level = UCogSampleFunctionLibrary_Gameplay::GetProgressionLevel(Actor); + + FGameplayEffectContextHandle EffectContextHandle = AbilitySystem->MakeEffectContext(); + + for (const TSubclassOf& EffectClass : Effects) + { + if (Results.Contains(EffectClass)) + { + continue; + } + + const FGameplayEffectSpecHandle* BakedEffectSpecHandle = BakedEffects.FindByPredicate([EffectClass](const FGameplayEffectSpecHandle& Handle) + { + return Handle.Data->Def.GetClass() == EffectClass; + }); + + if (BakedEffectSpecHandle != nullptr) + { + Results.Add(EffectClass, *BakedEffectSpecHandle); + } + else + { + FGameplayEffectSpecHandle EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, EffectContextHandle); + Results.Add(EffectClass, EffectSpecHandle); + } + } +} diff --git a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h index 6c337af..096f539 100644 --- a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h +++ b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h @@ -2,15 +2,18 @@ #include "CoreMinimal.h" #include "CogSampleDefines.h" +#include "Kismet/BlueprintFunctionLibrary.h" #include "CogSampleFunctionLibrary_Gameplay.generated.h" class UAbilitySystemComponent; class UGameplayAbility; +class UGameplayEffect; struct FGameplayAbilitySpecHandle; struct FGameplayAttribute; struct FGameplayAttributeData; struct FGameplayCueNotify_SpawnResult; struct FGameplayCueParameters; +struct FGameplayEffectSpecHandle; struct FGameplayTagContainer; //-------------------------------------------------------------------------------------------------------------------------- @@ -65,7 +68,25 @@ public: static void FindCapsulePointDistance(const FVector2D& CapsulePoint1, const FVector2D& CapsulePoint2, const float CapsuleRadius, const FVector2D& Point, FVector2D& Projection, float& Time, float& Distance); UFUNCTION(BlueprintPure) - static AActor* GetActorInstigator(AActor* Actor); + static AActor* GetInstigator(const AActor* Actor); + + UFUNCTION(BlueprintPure) + static int32 GetProgressionLevel(const AActor* Actor); + + UFUNCTION(BlueprintCallable) + static void SetProgressionLevel(AActor* Actor, int32 Value); + + UFUNCTION(BlueprintPure) + static AActor* GetCreator(const AActor* Actor); + + UFUNCTION(BlueprintCallable) + static void SetCreator(AActor* Actor, AActor* Value); + + UFUNCTION(BlueprintCallable) + static bool IsAlive(const AActor* Actor); + + UFUNCTION(BlueprintCallable) + static bool IsDead(const AActor* Actor); static void AdjustAttributeForMaxChange(UAbilitySystemComponent* AbilityComponent, FGameplayAttributeData& AffectedAttribute, float OldValue, float NewMaxValue, const FGameplayAttribute& AffectedAttributeProperty); @@ -85,5 +106,5 @@ public: static bool IsActorMatchingTags(const AActor* Actor, const FGameplayTagContainer& RequiredTags, const FGameplayTagContainer& IgnoredTags); - static bool MatchCooldownTag(const FGameplayTagContainer& TagContainer); + static void MakeOutgoingSpecs(const AActor* Actor, const TArray>& Effects, const TArray& BakedEffects, TMap, FGameplayEffectSpecHandle>& Result); }; diff --git a/Source/CogSample/CogSampleFunctionLibrary_Team.cpp b/Source/CogSample/CogSampleFunctionLibrary_Team.cpp index d0236b5..ee63755 100644 --- a/Source/CogSample/CogSampleFunctionLibrary_Team.cpp +++ b/Source/CogSample/CogSampleFunctionLibrary_Team.cpp @@ -3,7 +3,7 @@ #include "CogSampleTeamInterface.h" //-------------------------------------------------------------------------------------------------------------------------- -int32 UCogSampleFunctionLibrary_Team::GetTeamSafe(const AActor* Actor) +int32 UCogSampleFunctionLibrary_Team::GetTeam(const AActor* Actor) { if (Actor == nullptr) { @@ -20,6 +20,24 @@ int32 UCogSampleFunctionLibrary_Team::GetTeamSafe(const AActor* Actor) return Team; } +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleFunctionLibrary_Team::SetTeam(AActor* Actor, int32 Value) +{ + if (Actor == nullptr) + { + return; + } + + ICogSampleTeamInterface* TeamActor = Cast(Actor); + if (TeamActor == nullptr) + { + return; + } + + TeamActor->SetTeam(Value); +} + + //-------------------------------------------------------------------------------------------------------------------------- ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetTeamsAllegiance(int32 Team1, int32 Team2) { @@ -39,8 +57,8 @@ ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetTeamsAllegiance(int32 Te //-------------------------------------------------------------------------------------------------------------------------- ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetActorsAllegiance(const AActor* Actor1, const AActor* Actor2) { - const int32 Team1 = GetTeamSafe(Actor1); - const int32 Team2 = GetTeamSafe(Actor2); + const int32 Team1 = GetTeam(Actor1); + const int32 Team2 = GetTeam(Actor2); const ECogSampleAllegiance Allegiance = GetTeamsAllegiance(Team1, Team2); return Allegiance; } @@ -64,7 +82,7 @@ bool UCogSampleFunctionLibrary_Team::MatchAllegianceFromTeams(int32 Team1, int32 //-------------------------------------------------------------------------------------------------------------------------- bool UCogSampleFunctionLibrary_Team::MatchAllegianceBetweenTeamAndActor(int32 Team, const AActor* Actor, int32 AllegianceFilter) { - const int32 ActorTeam = GetTeamSafe(Actor); + const int32 ActorTeam = GetTeam(Actor); const ECogSampleAllegiance Allegiance = GetTeamsAllegiance(Team, ActorTeam); const bool Result = MatchAllegianceFilter(Allegiance, AllegianceFilter); return Result; diff --git a/Source/CogSample/CogSampleFunctionLibrary_Team.h b/Source/CogSample/CogSampleFunctionLibrary_Team.h index 3ead3ad..beb3b72 100644 --- a/Source/CogSample/CogSampleFunctionLibrary_Team.h +++ b/Source/CogSample/CogSampleFunctionLibrary_Team.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" #include "CogSampleFunctionLibrary_Team.generated.h" //-------------------------------------------------------------------------------------------------------------------------- @@ -32,7 +33,10 @@ class UCogSampleFunctionLibrary_Team : public UBlueprintFunctionLibrary public: UFUNCTION(BlueprintPure) - static int32 GetTeamSafe(const AActor* Actor); + static int32 GetTeam(const AActor* Actor); + + UFUNCTION(BlueprintCallable) + static void SetTeam(AActor* Actor, int32 Value); UFUNCTION(BlueprintPure) static ECogSampleAllegiance GetTeamsAllegiance(int32 Team1, int32 Team2); diff --git a/Source/CogSample/CogSampleGameplayAbility.cpp b/Source/CogSample/CogSampleGameplayAbility.cpp index 25d4174..538f748 100644 --- a/Source/CogSample/CogSampleGameplayAbility.cpp +++ b/Source/CogSample/CogSampleGameplayAbility.cpp @@ -5,9 +5,8 @@ #include "CogSampleFunctionLibrary_Tag.h" #include "CogSampleGameplayEffectContext.h" #include "CogSampleLogCategories.h" - - - +#include "CogSamplePlayerController.h" +#include "CogSampleSpawnPredictionComponent.h" //-------------------------------------------------------------------------------------------------------------------------- UCogSampleGameplayAbility::UCogSampleGameplayAbility() @@ -235,4 +234,49 @@ void UCogSampleGameplayAbility::GetCooldownInfos(float& TimeRemaining, float& Co //------------------------------------------------------------------------------------- FGameplayAbilitySpecHandle Handle; GetCooldownTimeRemainingAndDuration(Handle, CurrentActorInfo, TimeRemaining, CooldownDuration); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleGameplayAbility::SetupSpawnPrediction(AActor* Actor, int32 InstanceIndex) +{ + if (Actor == nullptr) + { + return; + } + + UCogSampleSpawnPredictionComponent* SpawnPrediction = Actor->FindComponentByClass(); + if (SpawnPrediction == nullptr) + { + return; + } + + SetupSpawnPredictionComponent(SpawnPrediction, InstanceIndex); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleGameplayAbility::SetupSpawnPredictionComponent(UCogSampleSpawnPredictionComponent* SpawnPrediction, int32 InstanceIndex) +{ + if (SpawnPrediction == nullptr) + { + return; + } + + SpawnPrediction->SetCreator(CurrentActorInfo->AvatarActor.Get()); + + FCogSampleSpawnPredictionKey PredictedActorKey = FCogSampleSpawnPredictionKey::MakeFromAbility(*this, InstanceIndex); + SpawnPrediction->SetSpawnPredictionKey(PredictedActorKey); + + if (GetCurrentActorInfo()->IsNetAuthority()) + { + SpawnPrediction->SetRole(ECogSampleSpawnPredictionRole::Server); + } + else + { + SpawnPrediction->SetRole(ECogSampleSpawnPredictionRole::Predicted); + + if (ACogSamplePlayerController* PlayerController = Cast(GetCurrentActorInfo()->PlayerController)) + { + PlayerController->SpawnPredictions.Add(SpawnPrediction); + } + } } \ No newline at end of file diff --git a/Source/CogSample/CogSampleGameplayAbility.h b/Source/CogSample/CogSampleGameplayAbility.h index e02da56..6c52c6f 100644 --- a/Source/CogSample/CogSampleGameplayAbility.h +++ b/Source/CogSample/CogSampleGameplayAbility.h @@ -4,6 +4,8 @@ #include "Abilities/GameplayAbility.h" #include "CogSampleGameplayAbility.generated.h" +class UCogSampleSpawnPredictionComponent; + UCLASS() class UCogSampleGameplayAbility : public UGameplayAbility { @@ -17,7 +19,9 @@ public: // UGameplayAbility overrides //---------------------------------------------------------------------------------------------------------------------- virtual void PreActivate(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData) override; + virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; + virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; //---------------------------------------------------------------------------------------------------------------------- @@ -59,6 +63,16 @@ public: UFUNCTION(BlueprintPure) int32 GetIntValueAtAbilityLevel(const FScalableFloat& ScalableFloat) const; + //---------------------------------------------------------------------------------------------------------------------- + // Predicated Actors + //---------------------------------------------------------------------------------------------------------------------- + + UFUNCTION(BlueprintCallable) + void SetupSpawnPrediction(AActor* PredictedSpawn, int32 InstanceIndex); + + UFUNCTION(BlueprintCallable) + void SetupSpawnPredictionComponent(UCogSampleSpawnPredictionComponent* SpawnPrediction, int32 InstanceIndex); + private: bool IsCostGameplayEffectIsZero(const UGameplayEffect* GameplayEffect, float Level, const FGameplayEffectContextHandle& EffectContext) const; diff --git a/Source/CogSample/CogSampleLogCategories.cpp b/Source/CogSample/CogSampleLogCategories.cpp index 425bd83..6dc265a 100644 --- a/Source/CogSample/CogSampleLogCategories.cpp +++ b/Source/CogSample/CogSampleLogCategories.cpp @@ -18,6 +18,8 @@ DEFINE_LOG_CATEGORY(LogCogControlRotation); DEFINE_LOG_CATEGORY(LogCogInput); DEFINE_LOG_CATEGORY(LogCogPosition); DEFINE_LOG_CATEGORY(LogCogPossession); +DEFINE_LOG_CATEGORY(LogCogPredictedActor); +DEFINE_LOG_CATEGORY(LogCogProjectile); DEFINE_LOG_CATEGORY(LogCogRotation); DEFINE_LOG_CATEGORY(LogCogSkeleton); DEFINE_LOG_CATEGORY(LogCogTargetAcquisition); @@ -39,7 +41,9 @@ namespace CogSampleLog FCogDebugLog::AddLogCategory(LogCogControlRotation, "Control Rotation", "Debug Draw of the Character Control Rotation"); FCogDebugLog::AddLogCategory(LogCogInput, "Input", "Log about the input actions"); FCogDebugLog::AddLogCategory(LogCogPosition, "Position", "Debug draw of a character position"); - FCogDebugLog::AddLogCategory(LogCogPossession, "Possession", "Log about the possession of player controller over a Character"); + FCogDebugLog::AddLogCategory(LogCogPossession, "Possession", "Log about the possession of a PlayerController over a Character"); + FCogDebugLog::AddLogCategory(LogCogPredictedActor, "Predicted Actor", "Log and debug draw about the prediction of actors."); + FCogDebugLog::AddLogCategory(LogCogProjectile, "Projectile", "Log and debug draw about the projectiles."); FCogDebugLog::AddLogCategory(LogCogRotation, "Rotation", "Debug Draw a Character Rotation"); FCogDebugLog::AddLogCategory(LogCogSkeleton, "Skeleton", "Debug Draw a Character Skeleton"); FCogDebugLog::AddLogCategory(LogCogTargetAcquisition, "Target Acquisition", "Debug Draw the target acquisition debug draw"); diff --git a/Source/CogSample/CogSampleLogCategories.h b/Source/CogSample/CogSampleLogCategories.h index 84dceb2..4a07f99 100644 --- a/Source/CogSample/CogSampleLogCategories.h +++ b/Source/CogSample/CogSampleLogCategories.h @@ -12,6 +12,8 @@ DECLARE_LOG_CATEGORY_EXTERN(LogCogControlRotation, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogInput, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogPosition, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogPossession, Warning, All); +DECLARE_LOG_CATEGORY_EXTERN(LogCogPredictedActor, Warning, All); +DECLARE_LOG_CATEGORY_EXTERN(LogCogProjectile, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogRotation, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogSkeleton, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogTargetAcquisition, Warning, All); diff --git a/Source/CogSample/CogSamplePlayerController.cpp b/Source/CogSample/CogSamplePlayerController.cpp index 68b365e..5a4abc8 100644 --- a/Source/CogSample/CogSamplePlayerController.cpp +++ b/Source/CogSample/CogSamplePlayerController.cpp @@ -5,6 +5,7 @@ #include "CogSampleCharacter.h" #include "CogSampleLogCategories.h" #include "CogSampleTargetAcquisition.h" +#include "GameFramework/PlayerState.h" #include "Net/UnrealNetwork.h" #if ENABLE_COG @@ -171,7 +172,7 @@ void ACogSamplePlayerController::TickTargeting(float DeltaSeconds) #if ENABLE_COG if (Target != nullptr && PossessedCharacter != nullptr) { - FCogDebugDraw::Segment(LogCogTargetAcquisition, PossessedCharacter.Get(), PossessedCharacter->GetActorLocation(), Target->GetActorLocation(), FColor::White, false); + FCogDebugDraw::Segment(LogCogTargetAcquisition, this, PossessedCharacter->GetActorLocation(), Target->GetActorLocation(), FColor::White, false); } #endif //ENABLE_COG } diff --git a/Source/CogSample/CogSamplePlayerController.h b/Source/CogSample/CogSamplePlayerController.h index 2e086ca..c8e2fb4 100644 --- a/Source/CogSample/CogSamplePlayerController.h +++ b/Source/CogSample/CogSamplePlayerController.h @@ -6,8 +6,9 @@ #include "GameFramework/PlayerController.h" #include "CogSamplePlayerController.generated.h" -class UCogSampleTargetAcquisition; class ACogSampleCharacter; +class UCogSampleSpawnPredictionComponent; +class UCogSampleTargetAcquisition; //-------------------------------------------------------------------------------------------------------------------------- DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FCogSampleTargetChangedEventDelegate, ACogSamplePlayerController*, Controller, AActor*, NewTarget, AActor*, OldTarget); @@ -53,10 +54,15 @@ public: AActor* GetTarget() const { return Target.Get(); } - UPROPERTY(BlueprintAssignable) FCogSampleTargetChangedEventDelegate OnTargetChanged; + //---------------------------------------------------------------------------------------------------------------------- + // Spawn Predictions + //---------------------------------------------------------------------------------------------------------------------- + UPROPERTY() + TArray SpawnPredictions; + private: //---------------------------------------------------------------------------------------------------------------------- diff --git a/Source/CogSample/CogSampleProgressionLevelInterface.h b/Source/CogSample/CogSampleProgressionLevelInterface.h new file mode 100644 index 0000000..50ebce3 --- /dev/null +++ b/Source/CogSample/CogSampleProgressionLevelInterface.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogSampleProgressionLevelInterface.generated.h" + +//-------------------------------------------------------------------------------------------------------------------------- +UINTERFACE(MinimalAPI, Blueprintable) +class UCogSampleProgressionLevelInterface : public UInterface +{ + GENERATED_BODY() +}; + +//-------------------------------------------------------------------------------------------------------------------------- +class ICogSampleProgressionLevelInterface +{ + GENERATED_BODY() + +public: + + virtual int32 GetProgressionLevel() const { return 0; } + + virtual void SetProgressionLevel(int32) { } +}; \ No newline at end of file diff --git a/Source/CogSample/CogSampleProjectileComponent.cpp b/Source/CogSample/CogSampleProjectileComponent.cpp new file mode 100644 index 0000000..58223ba --- /dev/null +++ b/Source/CogSample/CogSampleProjectileComponent.cpp @@ -0,0 +1,428 @@ +#include "CogSampleProjectileComponent.h" + +#include "AbilitySystemGlobals.h" +#include "AbilitySystemComponent.h" +#include "CogCommon.h" +#include "CogSampleFunctionLibrary_Gameplay.h" +#include "CogSampleFunctionLibrary_Team.h" +#include "CogSampleLogCategories.h" + +#if ENABLE_COG +#include "CogDebugLog.h" +#include "CogDebugDraw.h" +#endif //ENABLE_COG + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::BeginPlay() +{ + Super::BeginPlay(); + + Creator = UCogSampleFunctionLibrary_Gameplay::GetCreator(GetOwner()); + SpawnPrediction = GetOwner()->FindComponentByClass(); + + if (GetOwner()->HasAuthority()) + { + RegisterAllEffects(); + } + + Collision = Cast(CollisionReference.GetComponent(GetOwner())); + if (Collision != nullptr) + { + Collision->OnComponentBeginOverlap.AddDynamic(this, &UCogSampleProjectileComponent::OnCollisionOverlapBegin); + } + + AssistanceOverlap = Cast(OverlapReference.GetComponent(GetOwner())); + if (AssistanceOverlap != nullptr) + { + AssistanceOverlap->OnComponentBeginOverlap.AddDynamic(this, &UCogSampleProjectileComponent::OnAssistanceOverlapBegin); + } + +#if ENABLE_COG + if (FCogDebugLog::IsLogCategoryActive(LogCogProjectile)) + { + LastDebugLocation = GetOwner()->GetActorLocation(); + } +#endif //ENABLE_COG +} + + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + //if (CosmeticComponent != nullptr) + //{ + // CosmeticComponent->AddLocalRotation(CurrentCosmeticAngularVelocity * DeltaTime); + // CurrentCosmeticAngularVelocity *= FMath::Clamp(1.0f - CosmeticAngularDrag * DeltaTime, 0.0f, 1.0f); + //} + + //PreviousVelocity = Velocity; + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + //TravelingTime += DeltaTime; + +#if ENABLE_COG + + if (FCogDebugLog::IsLogCategoryActive(LogCogProjectile)) + { + const float CollisionRadius = Collision != nullptr ? Collision->GetScaledSphereRadius() : 0.0f; + const float AssistanceRadius = AssistanceOverlap != nullptr ? AssistanceOverlap->GetScaledSphereRadius() : 0.0f; + const float DebugRadius = FMath::Max(CollisionRadius, AssistanceRadius); + const FColor Color = SpawnPrediction != nullptr ? SpawnPrediction->GetRoleColor() : FColor(128, 128, 128, 255); + const bool Show = SpawnPrediction == nullptr || SpawnPrediction->GetRole() != ECogSampleSpawnPredictionRole::Replicated; + + if (Show && UpdatedComponent != nullptr) + { + const FVector Location = UpdatedComponent->GetComponentLocation(); + const FVector Delta = Location - LastDebugLocation; + + if (LogCogProjectile.GetVerbosity() == ELogVerbosity::VeryVerbose) + { + FCogDebugDraw::Sphere(LogCogProjectile, GetOwner(), Location, DebugRadius, Color, true, 0); + FCogDebugDraw::Axis(LogCogProjectile, GetOwner(), Location, UpdatedComponent->GetComponentRotation(), 50.0f, true, 0); + } + else + { + FCogDebugDraw::Point(LogCogProjectile, GetOwner(), Location, 5.0f, Color, true, 0); + } + + if (Delta.IsNearlyZero() == false) + { + FCogDebugDraw::Segment(LogCogProjectile, GetOwner(), LastDebugLocation, Location, Color, true, 0); + } + + LastDebugLocation = Location; + } + + } + +#endif //ENABLE_COG +} +//-------------------------------------------------------------------------------------------------------------------------- +//void UCogSampleProjectileComponent::CatchupReplicatedActor(float CatchupDuration) +//{ +// COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - CatchupDuration:%0.2f"), *GetName(), *GetRoleName(), CatchupDuration); +// +// const FVector OldPosition = GetOwner()->GetActorLocation(); +// const float OldSpeed = Velocity.Length(); +// +// TickComponent(CatchupDuration, LEVELTICK_All, nullptr); +// +// COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator, TEXT("Distance:%0.2f - OldSpeed:%0.2f - NewSpeed:%0.2f"), +// (GetOwner()->GetActorLocation() - OldPosition).Length(), +// OldSpeed, +// Velocity.Length()); +// +// if (SpawnPrediction != nullptr) +// { +// if (UCogSampleSpawnPrediction* SpawnPrediction2 = SpawnPrediction->GetSpawnPrediction()) +// { +// SpawnPrediction2->ProjectileMovement->Velocity = ProjectileMovement->Velocity; +// } +// } +//} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::RegisterAllEffects() +{ + TArray> AllEffects; + + for (const FCogSampleProjectileEffectConfig& EffectConfig : Effects) + { + for (TSubclassOf EffectClass : EffectConfig.Effects) + { + AllEffects.Add(EffectClass); + } + } + + UCogSampleFunctionLibrary_Gameplay::MakeOutgoingSpecs(GetOwner(), AllEffects, BakedEffects, EffectsMap); +} + + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleProjectileComponent::ShouldProcessOverlap(AActor* OtherActor, UPrimitiveComponent* OtherComp, bool RequireValidActor) +{ + if (RequireValidActor && OtherActor == nullptr) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:InvalidActor")); + return false; + } + + if (GetOwner()->GetLocalRole() != ROLE_Authority) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:NotAuthority")); + return false; + } + + //if (IsStopped) + //{ + // //---------------------------------------------------------------------------------------- + // // We can receive overlap events the same frame Stop is called. It shouldn't happen after. + // //---------------------------------------------------------------------------------------- + // COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:Stopped")); + // return false; + //} + + if (OtherComp == nullptr) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Warning, Creator.Get(), TEXT("Skipped:InvalidCollider")); + return false; + } + + if (OtherActor == GetOwner()) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:HittingSelf")); + return false; + } + + if (CanHitCreator == false && OtherActor == Creator) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:HittingCreator")); + return false; + } + + ////----------------------------------------------------------------------------------------- + //// Don't overlap if OtherActor is the simulated proxy replicated one time from server to + //// client for synch-up + ////----------------------------------------------------------------------------------------- + //if (OtherActor->IsA(AGPCoreProjectile::StaticClass()) && OtherActor->GetLocalRole() == ROLE_SimulatedProxy) + //{ + // COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:SimulatedProxyProjectile")); + // return false; + //} + + if (IsAlreadyProcessingAnOverlap) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:AlreadyProcessingAnOverlap")); + return false; + } + + //----------------------------------------------------------------------------------------- + // Ignore multiple hits on the same actor. This can happen if this the has multiple + // collisions, such as the character hit volumes (head, arm, chest, ...) + //----------------------------------------------------------------------------------------- + if (HitActors.Contains(OtherActor)) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:AlreadyHit")); + return false; + } + + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::OnCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& SweepHit) +{ + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - Other:%s - Comp:%s"), + *GetName(), + *GetRoleName(), + OtherActor != nullptr ? *OtherActor->GetName() : TEXT("NULL"), + OtherComp != nullptr ? *OtherComp->GetName() : TEXT("NULL")); + + if (ShouldProcessOverlap(OtherActor, OtherComp, false) == false) + { + return; + } + + TGuardValue OverlapGuard(IsAlreadyProcessingAnOverlap, true); + FHitResult PreciseHit; + + if (IsFromSweep) + { + //----------------------------------------------------------------------------- + // When the projectile moves, it moves with sweep activated. + //----------------------------------------------------------------------------- + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Method:IsFromSweep")); + PreciseHit = SweepHit; + } + else if (Collision != nullptr) + { + //----------------------------------------------------------------------------- + // Trace to find the accurate collision location. Use the largest collider + // which should be the assistance sphere, to make sure we find a result. + // If we were using the collision sphere after an assistance sphere overlap, + // we could miss the sweep. + //----------------------------------------------------------------------------- + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Method:SweepComponent")); + + USphereComponent* TestComp = (AssistanceOverlap != nullptr && AssistanceOverlap->GetUnscaledSphereRadius() > Collision->GetUnscaledSphereRadius()) ? AssistanceOverlap : Collision; + OtherComp->SweepComponent(PreciseHit, GetOwner()->GetActorLocation() - Velocity * 10.f, GetOwner()->GetActorLocation() + Velocity, FQuat::Identity, TestComp->GetCollisionShape(), TestComp->bTraceComplexOnMove); + + // SweepComponent specifically does not return us the Physical Material of the hit surface, so we look it up manually + if (PreciseHit.GetComponent() != nullptr) + { + PreciseHit.PhysMaterial = PreciseHit.GetComponent()->GetBodyInstance()->GetSimplePhysicalMaterial(); + } + } + else + { + //----------------------------------------------------------------------------- + // Fallback that uses a raycast if we have no CollisionComp to find a more + // accurate collision location. It should never happen. + //----------------------------------------------------------------------------- + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Method:LineTrace")); + static const FName TraceTag(TEXT("UCogSampleProjectileComponent::OnCollisionOverlapBegin")); + FCollisionQueryParams QueryParams(TraceTag, false, GetOwner()); + QueryParams.bReturnPhysicalMaterial = true; + OtherComp->LineTraceComponent(PreciseHit, GetOwner()->GetActorLocation() - Velocity * 10.f, GetOwner()->GetActorLocation() + Velocity, QueryParams); + } + + TryHit(PreciseHit); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::OnAssistanceOverlapBegin(UPrimitiveComponent* overlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& Hit) +{ + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - Other:%s - Comp:%s"), + *GetName(), + *GetRoleName(), + OtherActor != nullptr ? *OtherActor->GetName() : TEXT("NULL"), + OtherComp != nullptr ? *OtherComp->GetName() : TEXT("NULL")); + + //------------------------------------------------------------------------------------- + // Call ShouldProcessOverlap with a requirement of a valid actor because the + // assistance overlap is made to overlap only against actors, not static objects which + // would be null actor + //------------------------------------------------------------------------------------- + if (ShouldProcessOverlap(OtherActor, OtherComp, true) == false) + { + return; + } + + //------------------------------------------------------------------------------------- + // Since PawnOverlapSphere doesn't hit blocking objects, it is possible it is touching + // a target through a wall. Make sure that the hit is valid before proceeding. + // + // TODO: This is an approximation that does not always work if the assistance sphere + // is big. The raycast can pass even if there is a collision in the trajectory of the + // projectile, because we don't trace along the projectiles trajectory, but against + // both actors positions. + // + // This test is skipped if the projectile collision has been disabled. When the + // collision is disabled it means we want the projectile to pass through walls. + //------------------------------------------------------------------------------------- + bool IsValidOverlap = true; + if (Collision->GetCollisionEnabled()) + { + static const FName TraceTag(TEXT("UCogSampleProjectileComponent::OnAssistanceOverlapBegin")); + FCollisionQueryParams QueryParams(TraceTag, true, GetOwner()); + QueryParams.AddIgnoredActor(OtherActor); + const FVector OtherLocation = IsFromSweep ? (FVector)Hit.Location : OtherActor->GetActorLocation(); + if (GetWorld()->LineTraceTestByProfile(OtherLocation, GetOwner()->GetActorLocation(), Collision->GetCollisionProfileName(), QueryParams) == false) + { + IsValidOverlap = true; + } + } + + //------------------------------------------------------------------------------------- + // Call OnCollisionOverlapBegin since its doing the sweep test. + //------------------------------------------------------------------------------------- + if (IsValidOverlap) + { + OnCollisionOverlapBegin(Collision, OtherActor, OtherComp, OtherBodyIndex, IsFromSweep, Hit); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::TryHit(const FHitResult& HitResult) +{ + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - Other:%s - Comp:%s - Bone:%s"), + *GetName(), + *GetRoleName(), + HitResult.GetActor() != nullptr ? *HitResult.GetActor()->GetName() : TEXT("NULL"), + HitResult.GetComponent() != nullptr ? *HitResult.GetComponent()->GetName() : TEXT("NULL"), + *HitResult.BoneName.ToString()); + + //----------------------------------------------------------------------------------------- + // User defined callback for gameplay logic + //----------------------------------------------------------------------------------------- + if (ShouldHit(HitResult) == false) + { + COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped by ShouldHit callback")); + return; + } + + HitActors.Add(HitResult.GetActor()); + + //----------------------------------------------------------------------------------------- + // User defined callback to decide what should happen to projectile (Stop, Attach, ...) + //----------------------------------------------------------------------------------------- + FCogSampleHitConsequence HitConsequence; + Hit(HitResult, HitConsequence); + + if (HitConsequence.Stop) + { + //InternalStop(HitResult, HitConsequence); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::ClearHitActors() +{ + HitActors.Empty(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleProjectileComponent::ShouldHit_Implementation(const FHitResult& Hit) +{ + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleProjectileComponent::Hit_Implementation(const FHitResult& HitResult, FCogSampleHitConsequence& hitConsequence) +{ + AActor* HitActor = HitResult.GetActor(); + +#if ENABLE_COG + FCogDebugDraw::Arrow(LogCogProjectile, GetOwner(), HitResult.Location, HitResult.Location + HitResult.Normal * 50.0f, FColor::Red, true, 0); + FCogDebugDraw::Box(LogCogProjectile, GetOwner(), HitResult.Location, FVector(0.0f, 5.0f, 5.0f), FRotationMatrix::MakeFromX(HitResult.Normal).ToQuat(), FColor::Red, true, 0); +#endif //ENABLE_COG + + for (const FCogSampleProjectileEffectConfig& EffectConfig : Effects) + { + for (TSubclassOf EffectClass : EffectConfig.Effects) + { + if (UCogSampleFunctionLibrary_Team::MatchAllegiance(GetOwner(), HitActor, EffectConfig.Allegiance) == false) + { + continue; + } + + FGameplayEffectSpecHandle* Handle = EffectsMap.Find(EffectClass); + if (Handle == nullptr) + { + continue; + } + + FGameplayEffectSpec* Spec = Handle->Data.Get(); + if (Spec == nullptr) + { + continue; + } + + if (UCogSampleFunctionLibrary_Gameplay::IsDead(HitActor) && EffectConfig.AffectDead == false) + { + continue; + } + + UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor); + if (TargetAbilitySystem == nullptr) + { + continue; + } + + Spec->GetContext().AddHitResult(HitResult, true); + TargetAbilitySystem->ApplyGameplayEffectSpecToSelf(*Spec); + } + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +FString UCogSampleProjectileComponent::GetRoleName() const +{ + if (SpawnPrediction == nullptr) + { + return TEXT("No Prediction"); + } + + return SpawnPrediction->GetRoleName(); +} diff --git a/Source/CogSample/CogSampleProjectileComponent.h b/Source/CogSample/CogSampleProjectileComponent.h new file mode 100644 index 0000000..3a7f25a --- /dev/null +++ b/Source/CogSample/CogSampleProjectileComponent.h @@ -0,0 +1,128 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/SphereComponent.h" +#include "GameFramework/ProjectileMovementComponent.h" +#include "CogSampleSpawnPredictionComponent.h" +#include "GameplayEffect.h" +#include "CogSampleProjectileComponent.generated.h" + +//-------------------------------------------------------------------------------------------------------------------------- +// FCogSampleProjectileEffectConfig +//-------------------------------------------------------------------------------------------------------------------------- +USTRUCT(BlueprintType) +struct FCogSampleProjectileEffectConfig +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "/Script/CogSample.ECogSampleAllegianceFilter")) + int32 Allegiance = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool AffectDead = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray> Effects; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +// FCogSampleHitConsequence +//-------------------------------------------------------------------------------------------------------------------------- +USTRUCT(BlueprintType) +struct FCogSampleHitConsequence +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY(BlueprintReadWrite) + bool Stop = false; + + UPROPERTY(BlueprintReadWrite) + bool AttachToComponent = false; + + UPROPERTY(BlueprintReadWrite) + bool HideOnStop = false; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +// UCogSampleProjectileComponent +//-------------------------------------------------------------------------------------------------------------------------- +UCLASS(BlueprintType, meta = (BlueprintSpawnableComponent)) +class UCogSampleProjectileComponent : public UProjectileMovementComponent +{ + GENERATED_BODY() + +public: + + virtual void BeginPlay() override; + + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction); + + UFUNCTION(BlueprintCallable) + void ClearHitActors(); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile") + TArray Effects; + + UPROPERTY(Transient, BlueprintReadWrite, Category = "Internal") + TArray BakedEffects; + +protected: + + virtual FString GetRoleName() const; + + //virtual void CatchupReplicatedActor(float CatchupDuration); + + virtual bool ShouldProcessOverlap(AActor* OtherActor, UPrimitiveComponent* OtherComp, bool RequireValidActor); + + virtual void TryHit(const FHitResult& Hit); + + UFUNCTION() + virtual void OnCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& SweepHit); + + UFUNCTION() + virtual void OnAssistanceOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& SweepHit); + + UFUNCTION(BlueprintNativeEvent, BlueprintCallable) + bool ShouldHit(const FHitResult& hit); + + /** Blueprint hook called when projectile hits something */ + UFUNCTION(BlueprintNativeEvent, BlueprintCallable) + void Hit(const FHitResult& Hit, FCogSampleHitConsequence& HitConsequence); + + void RegisterAllEffects(); + + UPROPERTY(EditAnywhere) + FComponentReference CollisionReference; + + UPROPERTY(EditAnywhere) + FComponentReference OverlapReference; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile") + bool CanHitCreator = false; + + /** Sphere collision component */ + UPROPERTY() + TObjectPtr Collision = nullptr; + + /** Overlap collision component */ + UPROPERTY() + TObjectPtr AssistanceOverlap = nullptr; + + UPROPERTY() + TWeakObjectPtr Creator = nullptr; + + UPROPERTY() + TWeakObjectPtr SpawnPrediction = nullptr; + + /** Re-entrancy guard */ + bool IsAlreadyProcessingAnOverlap = false; + + /** The actors that were hit with this projectile. Used to prevent touching the same actor multiple times */ + TSet HitActors; + + TMap, FGameplayEffectSpecHandle> EffectsMap; + +#if ENABLE_COG + FVector LastDebugLocation = FVector::ZeroVector; +#endif //ENABLE_COG +}; \ No newline at end of file diff --git a/Source/CogSample/CogSampleSpawnPredictionComponent.cpp b/Source/CogSample/CogSampleSpawnPredictionComponent.cpp new file mode 100644 index 0000000..2df7329 --- /dev/null +++ b/Source/CogSample/CogSampleSpawnPredictionComponent.cpp @@ -0,0 +1,334 @@ +#include "CogSampleSpawnPredictionComponent.h" + +#include "Abilities/GameplayAbility.h" +#include "Abilities/GameplayAbilityTypes.h" +#include "CogSampleLogCategories.h" +#include "CogSampleFunctionLibrary_Gameplay.h" +#include "CogSamplePlayerController.h" +#include "Engine/World.h" +#include "GameFramework/Pawn.h" +#include "GameFramework/PlayerController.h" +#include "Net/UnrealNetwork.h" + +#if ENABLE_COG +#include "CogDebugDraw.h" +#include "CogDebugLog.h" +#endif //ENABLE_COG + +//-------------------------------------------------------------------------------------------------------------------------- +// FCogSampleSpawnPredictionKey +//-------------------------------------------------------------------------------------------------------------------------- +FString FCogSampleSpawnPredictionKey::ToString() const +{ + return FString::Printf(TEXT("[Creator=%s Ability=%s PredictionKey=%s Index=%d]"), *Creator.ToString(), *Ability.ToString(), *PredictionKey.ToString(), InstanceIndex); +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogSampleSpawnPredictionKey::operator==(const FCogSampleSpawnPredictionKey& other) const +{ + return Creator == other.Creator + && Ability == other.Ability + && PredictionKey == other.PredictionKey + && InstanceIndex == other.InstanceIndex; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogSampleSpawnPredictionKey::operator!=(const FCogSampleSpawnPredictionKey& other) const +{ + return ((*this == other) == false); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FCogSampleSpawnPredictionKey FCogSampleSpawnPredictionKey::MakeFromAbility(const UGameplayAbility& InAbility, int32 InInstanceIndex) +{ + FGameplayAbilitySpec* Spec = InAbility.GetCurrentAbilitySpec(); + check(Spec); + + FCogSampleSpawnPredictionKey Key; + Key.Creator = InAbility.GetAvatarActorFromActorInfo() != nullptr ? InAbility.GetAvatarActorFromActorInfo()->GetFName() : FName(); + Key.Ability = InAbility.GetFName(); + Key.PredictionKey = Spec->ActivationInfo.GetActivationPredictionKey(); + Key.InstanceIndex = InInstanceIndex; + Key.GameTime = InAbility.GetWorld()->GetTimeSeconds(); + return Key; +} + +//-------------------------------------------------------------------------------------------------------------------------- +// UCogSampleSpawnPredictionComponent +//-------------------------------------------------------------------------------------------------------------------------- +UCogSampleSpawnPredictionComponent::UCogSampleSpawnPredictionComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + SetIsReplicatedByDefault(true); + + PredictedSpawn = nullptr; + ReplicatedActor = nullptr; + +#if ENABLE_COG + PrimaryComponentTick.bCanEverTick = true; + PrimaryComponentTick.bStartWithTickEnabled = true; +#endif //ENABLE_COG +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + //------------------------------------------------------------------------- + // TODO: Push Model + //------------------------------------------------------------------------- + DOREPLIFETIME_CONDITION(UCogSampleSpawnPredictionComponent, PredictedActorKey, COND_OwnerOnly); + DOREPLIFETIME(UCogSampleSpawnPredictionComponent, Creator); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::BeginPlay() +{ + Super::BeginPlay(); + + APawn* PawnCreator = Cast(Creator); + PlayerController = PawnCreator ? Cast(PawnCreator->Controller) : nullptr; + //IsReplicatedActor = GetOwner()->HasAuthority() == false && PlayerController != nullptr; + + if (PlayerController != nullptr) + { + if (GetOwner()->HasAuthority() == false) + { + Role = ECogSampleSpawnPredictionRole::Replicated; + } + } + else + { + Role = ECogSampleSpawnPredictionRole::Remote; + } + + if (PlayerController != nullptr) + { + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, UCogSampleFunctionLibrary_Gameplay::GetInstigator(GetOwner()), TEXT("Actor:%s - Role:%s"), *GetName(), *GetRoleName()); + ReconcileReplicatedWithPredicted(); + } + +#if ENABLE_COG + if (FCogDebugLog::IsLogCategoryActive(LogCogPredictedActor)) + { + LastDebugLocation = GetOwner()->GetActorLocation(); + } +#endif //ENABLE_COG +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::ReconcileReplicatedWithPredicted() +{ + //------------------------------------------------------------------------- + // If we are a replicated actor on a local client, find the corresponding + // predicted actor and sync them together. + //------------------------------------------------------------------------- + if (Role != ECogSampleSpawnPredictionRole::Replicated) + { + return; + } + + CleanInvalidPredictedActors(); + + int32 PredictedActorIndex = FindSpawnPredictionIndex(); + if (PredictedActorIndex == INDEX_NONE) + { + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Warning, Creator.Get(), TEXT("Failed to find predicted actor. Key:%s"), *PredictedActorKey.ToString()); + +#if ENABLE_COG + for (const UCogSampleSpawnPredictionComponent* Candidate : PlayerController->SpawnPredictions) + { + if (Candidate != nullptr) + { + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Warning, Creator.Get(), TEXT(" Candidate:%s"), *Candidate->PredictedActorKey.ToString()); + } + else + { + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Warning, Creator.Get(), TEXT(" Candidate:NULL")); + } + } +#endif //ENABLE_COG + + return; + } + + UCogSampleSpawnPredictionComponent* SpawnPrediction = PlayerController->SpawnPredictions[PredictedActorIndex]; + PlayerController->SpawnPredictions.RemoveAt(PredictedActorIndex, 1); + SyncReplicatedWithPredicted(SpawnPrediction); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::OnRep_PredictedActorKey() +{ + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s - PredictedActorKey:%s"), *GetName(), *GetRoleName(), *PredictedActorKey.ToString()); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::CleanInvalidPredictedActors() +{ + if (PlayerController == nullptr) + { + return; + } + + TArray& SpawnPredictions = PlayerController->SpawnPredictions; + for (int32 i = 0; i < SpawnPredictions.Num(); ) + { + UCogSampleSpawnPredictionComponent* Candidate = SpawnPredictions[i]; + if (Candidate == nullptr || Candidate->GetOwner()->IsPendingKillPending()) + { + SpawnPredictions.RemoveAt(i, 1); + } + else + { + i++; + } + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +int32 UCogSampleSpawnPredictionComponent::FindSpawnPredictionIndex() +{ + if (PlayerController == nullptr) + { + return INDEX_NONE; + } + + int32 Index = PlayerController->SpawnPredictions.IndexOfByPredicate([this](const UCogSampleSpawnPredictionComponent* Candidate) + { + if (Candidate == nullptr) + { + return false; + } + + if (Candidate->GetClass() != GetClass()) + { + return false; + } + + if (Candidate->PredictedActorKey != PredictedActorKey) + { + return false; + } + + return true; + }); + + return Index; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::SyncReplicatedWithPredicted(UCogSampleSpawnPredictionComponent* InPredictedActor) +{ + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s - PredictedActor:%s"), *GetName(), *GetRoleName(), InPredictedActor != nullptr ? *InPredictedActor->GetName() : TEXT("NULL")); + + PredictedSpawn = InPredictedActor; + + if (PredictedSpawn != nullptr) + { + PredictedSpawn->ReplicatedActor = this; + + const float PredictedLifeSpawn = PredictedSpawn->GetOwner()->GetLifeSpan(); + GetOwner()->SetLifeSpan(PredictedLifeSpawn); + } + + //------------------------------------------------------------------------------------------------------------------ + // Destroying the replicated actor doesn't currently work because the server will keep it alive on all clients + // no matter what. We should investigate a way to make it work, but currently we hide it. + //------------------------------------------------------------------------------------------------------------------ + if (DestroyReplicatedActor) + { + SetVisibility(false); + GetOwner()->Destroy(); + } + else if (HideReplicatedActor) + { + SetVisibility(false); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::SetVisibility(bool Value) +{ + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s - Visibility:%d"), *GetName(), *GetRoleName(), Value); + + GetOwner()->SetActorHiddenInGame(!Value); + + TArray OtherComponents; + GetOwner()->GetComponents(OtherComponents); + + for (int32 i = 0; i < OtherComponents.Num(); i++) + { + OtherComponents[i]->SetVisibility(Value); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleSpawnPredictionComponent::OnComponentDestroyed(bool bDestroyingHierarchy) +{ + COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s"), *GetName(), *GetRoleName()); + + if (ReplicatedActor != nullptr) + { + ReplicatedActor->GetOwner()->Destroy(); + } + + Super::OnComponentDestroyed(bDestroyingHierarchy); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FString UCogSampleSpawnPredictionComponent::GetRoleName() const +{ + switch (Role) + { + case ECogSampleSpawnPredictionRole::Server: return "Server"; + case ECogSampleSpawnPredictionRole::Predicted: return "Predicted"; + case ECogSampleSpawnPredictionRole::Replicated: return "Replicated"; + case ECogSampleSpawnPredictionRole::Remote: return "Remote"; + } + + return "Unknown"; +} + +//-------------------------------------------------------------------------------------------------------------------------- +FColor UCogSampleSpawnPredictionComponent::GetRoleColor() const +{ + switch (Role) + { + case ECogSampleSpawnPredictionRole::Server: return FColor::FColor(255, 0, 0, 255); + case ECogSampleSpawnPredictionRole::Predicted: return FColor::FColor(255, 255, 0, 255); + case ECogSampleSpawnPredictionRole::Replicated: return FColor::FColor(128, 128, 0, 255); + case ECogSampleSpawnPredictionRole::Remote: return FColor::FColor(255, 0, 255, 255); + } + + return FColor(128, 128, 128, 255); +} + +//-------------------------------------------------------------------------------------------------------------------------- +#if ENABLE_COG + +void UCogSampleSpawnPredictionComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + const FVector Location = GetOwner()->GetActorLocation(); + + if (FCogDebugLog::IsLogCategoryActive(LogCogPredictedActor)) + { + const FColor Color = GetRoleColor(); + const FVector Delta = Location - LastDebugLocation; + + FCogDebugDraw::Axis(LogCogPredictedActor, this, Location, GetOwner()->GetActorRotation(), 50.0f, true, 0); + FCogDebugDraw::Point(LogCogPredictedActor, this, Location, 8.0f, Color, true, 0); + + if (Delta.IsNearlyZero() == false) + { + FCogDebugDraw::Segment(LogCogPredictedActor, this, LastDebugLocation, Location, Color, true, 0); + } + } + + LastDebugLocation = Location; +} + +#endif //ENABLE_COG diff --git a/Source/CogSample/CogSampleSpawnPredictionComponent.h b/Source/CogSample/CogSampleSpawnPredictionComponent.h new file mode 100644 index 0000000..97af607 --- /dev/null +++ b/Source/CogSample/CogSampleSpawnPredictionComponent.h @@ -0,0 +1,146 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogCommon.h" +#include "Components/ActorComponent.h" +#include "GameFramework/Actor.h" +#include "GameplayPrediction.h" +#include "CogSampleSpawnPredictionComponent.generated.h" + +class UGameplayAbility; +class ACogSamplePlayerController; + +//-------------------------------------------------------------------------------------------------------------------------- +// FCogSamplePredictedActorKey +//-------------------------------------------------------------------------------------------------------------------------- +USTRUCT() +struct FCogSampleSpawnPredictionKey +{ + GENERATED_BODY() + + UPROPERTY() + FName Creator; + + UPROPERTY() + FName Ability; + + UPROPERTY() + FPredictionKey PredictionKey; + + UPROPERTY() + int32 InstanceIndex = 0; + + UPROPERTY() + float GameTime = 0; + + FString ToString() const; + + bool operator==(const FCogSampleSpawnPredictionKey& other) const; + + bool operator!=(const FCogSampleSpawnPredictionKey& other) const; + + static FCogSampleSpawnPredictionKey MakeFromAbility(const UGameplayAbility& InAbility, int32 InInstanceIndex); +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM() +enum class ECogSampleSpawnPredictionRole : uint8 +{ + Server, + Predicted, + Replicated, + Remote, +}; + +//-------------------------------------------------------------------------------------------------------------------------- +// UCogSamplePredictedActorComponent +//-------------------------------------------------------------------------------------------------------------------------- +UCLASS(BlueprintType, meta = (BlueprintSpawnableComponent)) +class UCogSampleSpawnPredictionComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + + UCogSampleSpawnPredictionComponent(const class FObjectInitializer& ObjectInitializer); + + virtual void BeginPlay() override; + + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; + + virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override; + +#if ENABLE_COG + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; +#endif //ENABLE_COG + + virtual TWeakObjectPtr GetCreator() const { return Creator; } + + virtual void SetCreator(TWeakObjectPtr Value) { Creator = Value; } + + virtual const FCogSampleSpawnPredictionKey& GetSpawnPredictionKey() const { return PredictedActorKey; } + + virtual void SetSpawnPredictionKey(const FCogSampleSpawnPredictionKey& Value) { PredictedActorKey = Value; } + + virtual void SetSpawnTransform(FTransform Transform) { SpawnTransform = Transform; } + + virtual FTransform GetSpawnTransform() { return SpawnTransform; } + + virtual ECogSampleSpawnPredictionRole GetRole() const { return Role; } + + virtual void SetRole(ECogSampleSpawnPredictionRole Value) { Role = Value; } + + UCogSampleSpawnPredictionComponent* GetPredicted() const { return PredictedSpawn; } + + UCogSampleSpawnPredictionComponent* GetReplicated() const { return ReplicatedActor; } + + virtual void ReconcileReplicatedWithPredicted(); + + FString GetRoleName() const; + + FColor GetRoleColor() const; + +protected: + + virtual int32 FindSpawnPredictionIndex(); + + virtual void SyncReplicatedWithPredicted(UCogSampleSpawnPredictionComponent* PredictedSpawn); + + void CleanInvalidPredictedActors(); + + void SetVisibility(bool Value); + + UFUNCTION() + void OnRep_PredictedActorKey(); + + UPROPERTY(Replicated) + TWeakObjectPtr Creator = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadonly) + bool DestroyReplicatedActor = false; + + UPROPERTY(EditAnywhere, BlueprintReadonly) + bool HideReplicatedActor = true; + + UPROPERTY(BlueprintReadonly) + UCogSampleSpawnPredictionComponent* PredictedSpawn = nullptr; + + UPROPERTY(BlueprintReadonly) + UCogSampleSpawnPredictionComponent* ReplicatedActor = nullptr; + + UPROPERTY(BlueprintReadonly) + ECogSampleSpawnPredictionRole Role = ECogSampleSpawnPredictionRole::Replicated; + + UPROPERTY(BlueprintReadonly) + TWeakObjectPtr PlayerController = nullptr; + + UPROPERTY(ReplicatedUsing = OnRep_PredictedActorKey) + FCogSampleSpawnPredictionKey PredictedActorKey; + + FTransform SpawnTransform; + +#if ENABLE_COG + FVector LastDebugLocation = FVector::ZeroVector; +#endif //ENABLE_COG +}; + diff --git a/Source/CogSample/CogSampleSpawnableInterface.h b/Source/CogSample/CogSampleSpawnableInterface.h new file mode 100644 index 0000000..bde9ac8 --- /dev/null +++ b/Source/CogSample/CogSampleSpawnableInterface.h @@ -0,0 +1,22 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogSampleSpawnableInterface.generated.h" + +UINTERFACE(MinimalAPI, Blueprintable) +class UCogSampleSpawnableInterface : public UInterface +{ + GENERATED_BODY() +}; + +class ICogSampleSpawnableInterface +{ + GENERATED_BODY() + +public: + + virtual AActor* GetCreator() const { return nullptr; } + + virtual void SetCreator(AActor* Value) { } +}; + diff --git a/Source/CogSample/CogSampleTeamInterface.h b/Source/CogSample/CogSampleTeamInterface.h index 748a622..93e8b8d 100644 --- a/Source/CogSample/CogSampleTeamInterface.h +++ b/Source/CogSample/CogSampleTeamInterface.h @@ -3,7 +3,6 @@ #include "CoreMinimal.h" #include "CogSampleTeamInterface.generated.h" -//-------------------------------------------------------------------------------------------------------------------------- UINTERFACE(MinimalAPI, Blueprintable) class UCogSampleTeamInterface : public UInterface { @@ -17,4 +16,7 @@ class ICogSampleTeamInterface public: virtual int32 GetTeam() const { return 0; } + + virtual void SetTeam(int32) { } }; + diff --git a/TODO.txt b/TODO.txt index 3e36b3d..8442c22 100644 --- a/TODO.txt +++ b/TODO.txt @@ -6,9 +6,17 @@ - CogEngine: More stats in the stats window - CogEngine: Overlay mode of stats. -- CogEngine: Stat main menu widget could have a tooltip on each stat with a control to change set the emulation (FPS, Ping, Packetloss) +- CogEngine: Add more info in stats tooltip (details, curves, ...) - CogEngine: Add screen settings (fullscreen, borderless, window and resolution) +- CogEngine: make a better widget for CheckBoxState for input shortcuts - CogSample: Add a custom window in sample (changing the character faction) - CogSample: Create more abilities +- CogSample: Push Model +- CogSample: Add timescale game tweak +- CogSample: Add cooldown reduction +- CogSample: Add more debug for area (change color on tick, duration ...) +- CogDebug: Check KismetExecutionMessage for warnings. As an exemple it is used by GEngine::GetWorldFromContextObject. + +- CogInput: Add gamepad stick drag to set their values \ No newline at end of file