From 0f775a367973dff0060e9f7568514b49904a21c2 Mon Sep 17 00:00:00 2001 From: Kingsmedia Date: Thu, 12 May 2022 15:37:49 +0200 Subject: [PATCH 1/3] Reorg with Stephens repo --- Config/DefaultEngine.ini | 2 + Config/DefaultGame.ini | 10 +- Config/DefaultInput.ini | 5 +- .../Private/MultiplayerSessionsSubsystem.cpp | 59 +- .../Public/MultiplayerSessionsSubsystem.h | 15 +- Source/Blaster/Blaster.Build.cs | 2 +- .../Blaster/Character/BlasterAnimInstance.cpp | 28 +- .../Blaster/Character/BlasterAnimInstance.h | 2 +- Source/Blaster/Character/BlasterCharacter.cpp | 536 +++++++++-------- Source/Blaster/Components/CombatComponent.cpp | 540 +++++++++--------- Source/Blaster/GameMode/BlasterGameMode.cpp | 32 +- Source/Blaster/GameMode/BlasterGameMode.h | 6 +- Source/Blaster/HUD/BlasterHUD.cpp | 10 +- Source/Blaster/HUD/BlasterHUD.h | 2 +- .../BlasterPlayerController.cpp | 513 +++++++++-------- .../BlasterPlayerController.h | 6 +- .../PlayerState/BlasterPlayerState.cpp | 58 +- .../Blaster/PlayerState/BlasterPlayerState.h | 2 - 18 files changed, 908 insertions(+), 920 deletions(-) diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index a1d012c..d7bf3e8 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -21,6 +21,8 @@ r.ReflectionMethod=1 r.Shadow.Virtual.Enable=1 [/Script/WorldPartitionEditor.WorldPartitionEditorSettings] +bEnableWorldPartition=False +bEnableConversionPrompt=True CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' [/Script/Engine.Engine] diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index 575d0c6..cd6827c 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -10,8 +10,7 @@ MaxPlayers=100 Build=IfProjectHasCode BuildConfiguration=PPBC_Development BuildTarget= -LaunchOnTarget= -StagingDirectory=(Path="C:/UEProjects/Blaster/Build") +StagingDirectory=(Path="") FullRebuild=False ForDistribution=False IncludeDebugFiles=False @@ -20,7 +19,6 @@ bIncludeNativizedAssetsInProjectGeneration=False bExcludeMonolithicEngineHeadersInNativizedCode=False UsePakFile=True bUseIoStore=True -bUseZenStore=False bMakeBinaryConfig=False bGenerateChunks=False bGenerateNoChunks=False @@ -91,12 +89,10 @@ bSkipMovies=False +IniKeyBlacklist=MobileProvision +IniKeyBlacklist=IniKeyBlacklist +IniKeyBlacklist=IniSectionBlacklist --IniSectionBlacklist=HordeStorageServers -+IniSectionBlacklist=HordeStorageServers +MapsToCook=(FilePath="/Game/Maps/Lobby") +MapsToCook=(FilePath="/Game/Maps/GameStartupMap") ++MapsToCook=(FilePath="/Game/Maps/TransitionMap") +MapsToCook=(FilePath="/Game/Maps/BlasterMap") PerPlatformBuildConfig=() -PerPlatformTargetFlavorName=(("Android", "Android_ASTC")) -PerPlatformBuildTarget=() +PerPlatformTargetPlatformName=() diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini index e2688b2..331730b 100644 --- a/Config/DefaultInput.ini +++ b/Config/DefaultInput.ini @@ -18,6 +18,10 @@ +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) @@ -65,7 +69,6 @@ bUseMouseForTouch=False bEnableMouseSmoothing=True bEnableFOVScaling=True bCaptureMouseOnLaunch=True -bEnableLegacyInputScales=True bAlwaysShowTouchInterface=False bShowConsoleOnFourFingerTap=True bEnableGestureRecognizer=False diff --git a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp index f366c88..0dfd851 100644 --- a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp +++ b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp @@ -18,11 +18,12 @@ UMultiplayerSessionsSubsystem::UMultiplayerSessionsSubsystem(): { SessionInterface = Subsystem->GetSessionInterface(); } - } void UMultiplayerSessionsSubsystem::CreateSession(int32 NumPublicConnections, FString MatchType) { + DesiredNumPublicConnections = NumPublicConnections; + DesiredMatchType = MatchType; if (!SessionInterface.IsValid()) { return; @@ -48,10 +49,10 @@ void UMultiplayerSessionsSubsystem::CreateSession(int32 NumPublicConnections, FS LastSessionSettings->bAllowJoinViaPresence = true; LastSessionSettings->bShouldAdvertise = true; LastSessionSettings->bUsesPresence = true; - LastSessionSettings->bUseLobbiesIfAvailable = true; // For UE5 when not finding sessions - LastSessionSettings->Set(FName("MatchType"), FString(MatchType), EOnlineDataAdvertisementType::ViaOnlineServiceAndPing); + LastSessionSettings->Set(FName("MatchType"), MatchType, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing); LastSessionSettings->BuildUniqueId = 1; - + LastSessionSettings->bUseLobbiesIfAvailable = true; + const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); if (!SessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *LastSessionSettings)) { @@ -69,9 +70,9 @@ void UMultiplayerSessionsSubsystem::FindSessions(int32 MaxSearchResults) return; } - FindSessionCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate); + FindSessionsCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate); - LastSessionSearch = MakeShareable(new FOnlineSessionSearch); + LastSessionSearch = MakeShareable(new FOnlineSessionSearch()); LastSessionSearch->MaxSearchResults = MaxSearchResults; LastSessionSearch->bIsLanQuery = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false; LastSessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals); @@ -79,7 +80,7 @@ void UMultiplayerSessionsSubsystem::FindSessions(int32 MaxSearchResults) const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); if (!SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef())) { - SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionCompleteDelegateHandle); + SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle); MultiplayerOnFindSessionsComplete.Broadcast(TArray(), false); } @@ -123,19 +124,19 @@ void UMultiplayerSessionsSubsystem::DestroySession() void UMultiplayerSessionsSubsystem::StartSession() { - if (!SessionInterface.IsValid()) - { - return; - } - - StartSessionCompleteDelegateHandle = SessionInterface->AddOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegate); - - if (!SessionInterface->StartSession(NAME_GameSession)) - { - SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); - - MultiplayerOnStartSessionComplete.Broadcast(false); - } + // if (!SessionInterface.IsValid()) + // { + // return; + // } + // + // StartSessionCompleteDelegateHandle = SessionInterface->AddOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegate); + // + // if (!SessionInterface->StartSession(NAME_GameSession)) + // { + // SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); + // + // MultiplayerOnStartSessionComplete.Broadcast(false); + // } } void UMultiplayerSessionsSubsystem::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful) @@ -152,7 +153,7 @@ void UMultiplayerSessionsSubsystem::OnFindSessionsComplete(bool bWasSuccessful) { if (SessionInterface) { - SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionCompleteDelegateHandle); + SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle); } if (LastSessionSearch->SearchResults.Num() <= 0) @@ -180,22 +181,20 @@ void UMultiplayerSessionsSubsystem::OnDestroySessionComplete(FName SessionName, { SessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegateHandle); } - if (bWasSuccessful && bCreateSessionOnDestroy) { bCreateSessionOnDestroy = false; CreateSession(LastNumPublicConnections, LastMatchType); } - MultiplayerOnDestroySessionComplete.Broadcast(bWasSuccessful); } -void UMultiplayerSessionsSubsystem::OnStartSessionComplete(FName SessionNAme, bool bWasSuccessful) +void UMultiplayerSessionsSubsystem::OnStartSessionComplete(FName SessionName, bool bWasSuccessful) { - if (SessionInterface) - { - SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); - } - - MultiplayerOnStartSessionComplete.Broadcast(bWasSuccessful); + // if (SessionInterface) + // { + // SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); + // } + // + // MultiplayerOnStartSessionComplete.Broadcast(bWasSuccessful); } diff --git a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h index c975cae..11deff0 100644 --- a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h +++ b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h @@ -8,8 +8,9 @@ #include "Interfaces/OnlineSessionInterface.h" #include "MultiplayerSessionsSubsystem.generated.h" - -// Declaring our own custom delegates for the Menu class to bind callbacks to +// +// Delcaring our own custom delegates for the Menu class to bind callbacks to +// DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnCreateSessionComplete, bool, bWasSuccessful); DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiplayerOnFindSessionsComplete, const TArray& SessionResults, bool bWasSuccessful); DECLARE_MULTICAST_DELEGATE_OneParam(FMultiplayerOnJoinSessionComplete, EOnJoinSessionCompleteResult::Type Result) @@ -45,7 +46,9 @@ public: FMultiplayerOnJoinSessionComplete MultiplayerOnJoinSessionComplete; FMultiplayerOnDestroySessionComplete MultiplayerOnDestroySessionComplete; FMultiplayerOnStartSessionComplete MultiplayerOnStartSessionComplete; - + + int32 DesiredNumPublicConnections{}; + FString DesiredMatchType{}; protected: /* @@ -56,7 +59,7 @@ protected: void OnFindSessionsComplete(bool bWasSuccessful); void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result); void OnDestroySessionComplete(FName SessionName, bool bWasSuccessful); - void OnStartSessionComplete(FName SessionNAme, bool bWasSuccessful); + void OnStartSessionComplete(FName SessionName, bool bWasSuccessful); private: @@ -66,12 +69,12 @@ private: /* * To add to the Online Session Interface delegate list. - * We'll bind our MultiplayerSessionSubsystem internal callbacks to these. + * We'll bind our MultiplayerSessionsSubsystem internal callbacks to these. */ FOnCreateSessionCompleteDelegate CreateSessionCompleteDelegate; FDelegateHandle CreateSessionCompleteDelegateHandle; FOnFindSessionsCompleteDelegate FindSessionsCompleteDelegate; - FDelegateHandle FindSessionCompleteDelegateHandle; + FDelegateHandle FindSessionsCompleteDelegateHandle; FOnJoinSessionCompleteDelegate JoinSessionCompleteDelegate; FDelegateHandle JoinSessionCompleteDelegateHandle; FOnDestroySessionCompleteDelegate DestroySessionCompleteDelegate; diff --git a/Source/Blaster/Blaster.Build.cs b/Source/Blaster/Blaster.Build.cs index eb8febb..7e92c8c 100644 --- a/Source/Blaster/Blaster.Build.cs +++ b/Source/Blaster/Blaster.Build.cs @@ -8,7 +8,7 @@ public class Blaster : ModuleRules { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "MultiplayerSessions", "OnlineSubsystem", "OnlineSubsystemSteam" }); PrivateDependencyModuleNames.AddRange(new string[] { }); diff --git a/Source/Blaster/Character/BlasterAnimInstance.cpp b/Source/Blaster/Character/BlasterAnimInstance.cpp index fb8c071..fd9afa0 100644 --- a/Source/Blaster/Character/BlasterAnimInstance.cpp +++ b/Source/Blaster/Character/BlasterAnimInstance.cpp @@ -2,11 +2,11 @@ #include "BlasterAnimInstance.h" - #include "BlasterCharacter.h" -#include "Blaster/Weapon/Weapon.h" #include "GameFramework/CharacterMovementComponent.h" #include "Kismet/KismetMathLibrary.h" +#include "Blaster/Weapon/Weapon.h" +#include "Blaster/Types/CombatState.h" void UBlasterAnimInstance::NativeInitializeAnimation() { @@ -15,15 +15,14 @@ void UBlasterAnimInstance::NativeInitializeAnimation() BlasterCharacter = Cast(TryGetPawnOwner()); } -void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaSeconds) +void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaTime) { - Super::NativeUpdateAnimation(DeltaSeconds); + Super::NativeUpdateAnimation(DeltaTime); if (BlasterCharacter == nullptr) { BlasterCharacter = Cast(TryGetPawnOwner()); } - if (BlasterCharacter == nullptr) return; FVector Velocity = BlasterCharacter->GetVelocity(); @@ -31,7 +30,7 @@ void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaSeconds) Speed = Velocity.Size(); bIsInAir = BlasterCharacter->GetCharacterMovement()->IsFalling(); - bIsAccelerating = BlasterCharacter->GetCharacterMovement()->GetCurrentAcceleration().Size() > 0.f; + bIsAccelerating = BlasterCharacter->GetCharacterMovement()->GetCurrentAcceleration().Size() > 0.f ? true : false; bWeaponEquipped = BlasterCharacter->IsWeaponEquipped(); EquippedWeapon = BlasterCharacter->GetEquippedWeapon(); bIsCrouched = BlasterCharacter->bIsCrouched; @@ -43,23 +42,22 @@ void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaSeconds) FRotator AimRotation = BlasterCharacter->GetBaseAimRotation(); FRotator MovementRotation = UKismetMathLibrary::MakeRotFromX(BlasterCharacter->GetVelocity()); FRotator DeltaRot = UKismetMathLibrary::NormalizedDeltaRotator(MovementRotation, AimRotation); - DeltaRotation = FMath::RInterpTo(DeltaRotation, DeltaRot, DeltaSeconds, 6.f); + DeltaRotation = FMath::RInterpTo(DeltaRotation, DeltaRot, DeltaTime, 6.f); YawOffset = DeltaRotation.Yaw; - + CharacterRotationLastFrame = CharacterRotation; CharacterRotation = BlasterCharacter->GetActorRotation(); const FRotator Delta = UKismetMathLibrary::NormalizedDeltaRotator(CharacterRotation, CharacterRotationLastFrame); - const float Target = Delta.Yaw / DeltaSeconds; - const float Interp = FMath::FInterpTo(Lean, Target, DeltaSeconds, 6.f); + const float Target = Delta.Yaw / DeltaTime; + const float Interp = FMath::FInterpTo(Lean, Target, DeltaTime, 6.f); Lean = FMath::Clamp(Interp, -90.f, 90.f); - // Aim offset AO_Yaw = BlasterCharacter->GetAO_Yaw(); AO_Pitch = BlasterCharacter->GetAO_Pitch(); - + if (bWeaponEquipped && EquippedWeapon && EquippedWeapon->GetWeaponMesh() && BlasterCharacter->GetMesh()) { - LeftHandTransform = EquippedWeapon->GetWeaponMesh()->GetSocketTransform(FName("LeftHandSocket"), RTS_World); + LeftHandTransform = EquippedWeapon->GetWeaponMesh()->GetSocketTransform(FName("LeftHandSocket"), ERelativeTransformSpace::RTS_World); FVector OutPosition; FRotator OutRotation; BlasterCharacter->GetMesh()->TransformToBoneSpace(FName("hand_r"), LeftHandTransform.GetLocation(), FRotator::ZeroRotator, OutPosition, OutRotation); @@ -69,9 +67,9 @@ void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaSeconds) if (BlasterCharacter->IsLocallyControlled()) { bLocallyControlled = true; - const FTransform RightHandTransform = EquippedWeapon->GetWeaponMesh()->GetSocketTransform(FName("Hand_R"), RTS_World); + FTransform RightHandTransform = EquippedWeapon->GetWeaponMesh()->GetSocketTransform(FName("Hand_R"), ERelativeTransformSpace::RTS_World); FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(RightHandTransform.GetLocation(), RightHandTransform.GetLocation() + (RightHandTransform.GetLocation() - BlasterCharacter->GetHitTarget())); - RightHandRotation = FMath::RInterpTo(RightHandRotation, LookAtRotation, DeltaSeconds, 30.f); + RightHandRotation = FMath::RInterpTo(RightHandRotation, LookAtRotation, DeltaTime, 30.f); } } diff --git a/Source/Blaster/Character/BlasterAnimInstance.h b/Source/Blaster/Character/BlasterAnimInstance.h index 64293c1..27d6245 100644 --- a/Source/Blaster/Character/BlasterAnimInstance.h +++ b/Source/Blaster/Character/BlasterAnimInstance.h @@ -18,7 +18,7 @@ class BLASTER_API UBlasterAnimInstance : public UAnimInstance public: virtual void NativeInitializeAnimation() override; - virtual void NativeUpdateAnimation(float DeltaSeconds) override; + virtual void NativeUpdateAnimation(float DeltaTime) override; private: diff --git a/Source/Blaster/Character/BlasterCharacter.cpp b/Source/Blaster/Character/BlasterCharacter.cpp index 4d07751..0aca14f 100644 --- a/Source/Blaster/Character/BlasterCharacter.cpp +++ b/Source/Blaster/Character/BlasterCharacter.cpp @@ -27,7 +27,7 @@ ABlasterCharacter::ABlasterCharacter() SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom")); CameraBoom->SetupAttachment(GetMesh()); - CameraBoom->TargetArmLength = 350.f; + CameraBoom->TargetArmLength = 600.f; CameraBoom->bUsePawnControlRotation = true; FollowCamera = CreateDefaultSubobject(TEXT("FollowCamera")); @@ -44,11 +44,12 @@ ABlasterCharacter::ABlasterCharacter() Combat->SetIsReplicated(true); GetCharacterMovement()->NavAgentProps.bCanCrouch = true; - GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); + GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore); GetMesh()->SetCollisionObjectType(ECC_SkeletalMesh); - GetMesh()->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); - GetMesh()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + GetMesh()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore); + GetMesh()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Visibility, ECollisionResponse::ECR_Block); GetCharacterMovement()->RotationRate = FRotator(0.f, 850.f, 0.f); + TurningInPlace = ETurningInPlace::ETIP_NotTurning; NetUpdateFrequency = 66.f; MinNetUpdateFrequency = 33.f; @@ -56,17 +57,6 @@ ABlasterCharacter::ABlasterCharacter() DissolveTimeline = CreateDefaultSubobject(TEXT("DissolveTimelineComponent")); } -void ABlasterCharacter::BeginPlay() -{ - Super::BeginPlay(); - - UpdateHUDHealth(); - if (HasAuthority()) - { - OnTakeAnyDamage.AddDynamic(this, &ABlasterCharacter::ReceiveDamage); - } -} - void ABlasterCharacter::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); @@ -79,11 +69,81 @@ void ABlasterCharacter::GetLifetimeReplicatedProps(TArray& Ou void ABlasterCharacter::OnRep_ReplicatedMovement() { Super::OnRep_ReplicatedMovement(); - SimProxiesTurn(); TimeSinceLastMovementReplication = 0.f; } +void ABlasterCharacter::Eliminated() +{ + if (Combat && Combat->EquippedWeapon) + { + Combat->EquippedWeapon->Dropped(); + } + MulticastEliminated(); + GetWorldTimerManager().SetTimer( + EliminationTimer, + this, + &ABlasterCharacter::EliminationTimerFinished, + EliminationDelay + ); +} + +void ABlasterCharacter::MulticastEliminated_Implementation() +{ + if (BlasterPlayerController) + { + BlasterPlayerController->SetHUDWeaponAmmo(0); + } + bEliminated = true; + PlayEliminatedMontage(); + // Start dissolve effect + if (DissolveMaterialInstance) + { + DynamicDissolveMaterialInstance = UMaterialInstanceDynamic::Create(DissolveMaterialInstance, this); + GetMesh()->SetMaterial(0, DynamicDissolveMaterialInstance); + DynamicDissolveMaterialInstance->SetScalarParameterValue(TEXT("Dissolve"), 0.55f); + DynamicDissolveMaterialInstance->SetScalarParameterValue(TEXT("Glow"), 200.f); + } + StartDissolve(); + + // Disable character movement + GetCharacterMovement()->DisableMovement(); + GetCharacterMovement()->StopMovementImmediately(); + bDisableGameplay = true; + // Disable collision + GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision); + GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision); + + // Elimination bot + if (EliminationBotEffect) + { + const FVector EliminationBotSpawnPoint(GetActorLocation().X, GetActorLocation().Y, GetActorLocation().Z + 200.f); + EliminationBotComponent = UGameplayStatics::SpawnEmitterAtLocation( + GetWorld(), + EliminationBotEffect, + EliminationBotSpawnPoint, + GetActorRotation() + ); + } + if (EliminationBotSound) + { + UGameplayStatics::SpawnSoundAtLocation( + this, + EliminationBotSound, + GetActorLocation() + ); + } +} + +void ABlasterCharacter::EliminationTimerFinished() +{ + ABlasterGameMode* BlasterGameMode = GetWorld()->GetAuthGameMode(); + if (BlasterGameMode) + { + BlasterGameMode->RequestRespawn(this, Controller); + } +} + void ABlasterCharacter::Destroyed() { Super::Destroyed(); @@ -98,151 +158,24 @@ void ABlasterCharacter::Destroyed() } } -void ABlasterCharacter::PostInitializeComponents() +void ABlasterCharacter::BeginPlay() { - Super::PostInitializeComponents(); - - if (Combat) - { - Combat->Character = this; - } -} - -void ABlasterCharacter::PlayFireMontage(bool bAiming) -{ - if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; - - UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); - if (AnimInstance && FireWeaponMontage) - { - AnimInstance->Montage_Play(FireWeaponMontage); - const FName SectionName = bAiming ? FName("RifleADS") : FName("RifleHip"); - AnimInstance->Montage_JumpToSection(SectionName); - } -} - -void ABlasterCharacter::PlayReloadMontage() -{ - if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; - - UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); - if (AnimInstance && ReloadMontage) - { - AnimInstance->Montage_Play(ReloadMontage); - FName SectionName; - switch (Combat->EquippedWeapon->GetWeaponType()) - { - case EWeaponType::EWT_AssaultRifle: - SectionName = FName("Rifle"); - break; - } - AnimInstance->Montage_JumpToSection(SectionName); - } -} - -void ABlasterCharacter::PlayEliminatedMontage() -{ - UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); - if (AnimInstance && EliminatedMontage) - { - AnimInstance->Montage_Play(EliminatedMontage); - } -} - -void ABlasterCharacter::Eliminated() -{ - if (Combat && Combat->EquippedWeapon) - { - Combat->EquippedWeapon->Dropped(); - } - MulticastEliminated(); - GetWorldTimerManager().SetTimer(EliminationTimer, this, &ABlasterCharacter::EliminationTimerFinished, EliminationDelay); -} - -void ABlasterCharacter::MulticastEliminated_Implementation() -{ - if (BlasterPlayerController) - { - BlasterPlayerController->SetHUDWeaponAmmo(0); - } - bEliminated = true; - PlayEliminatedMontage(); - - // Start dissolve effect - if (DissolveMaterialInstance) - { - DynamicDissolveMaterialInstance = UMaterialInstanceDynamic::Create(DissolveMaterialInstance, this); - GetMesh()->SetMaterial(0, DynamicDissolveMaterialInstance); - DynamicDissolveMaterialInstance->SetScalarParameterValue(TEXT("Dissolve"), 0.55f); - DynamicDissolveMaterialInstance->SetScalarParameterValue(TEXT("Glow"), 200.f); - } - StartDissolve(); - - // Disable character movement - GetCharacterMovement()->DisableMovement(); - GetCharacterMovement()->StopMovementImmediately(); - bDisableGameplay = true; - - // Disable collision - GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision); - GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision); - - // Elimination bot - if (EliminationBotEffect) - { - const FVector EliminationBotSpawnPoint(GetActorLocation().X, GetActorLocation().Y, GetActorLocation().Z + 200.f); - EliminationBotComponent = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), EliminationBotEffect, EliminationBotSpawnPoint, GetActorRotation()); - - } - if (EliminationBotSound) - { - UGameplayStatics::SpawnSoundAtLocation(this, EliminationBotSound, GetActorLocation()); - } -} - -void ABlasterCharacter::EliminationTimerFinished() -{ - ABlasterGameMode* GameMode = GetWorld()->GetAuthGameMode(); - if (GameMode) - { - GameMode->RequestRespawn(this, Controller); - } -} - -void ABlasterCharacter::PlayHitReactMontage() -{ - if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; - - UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); - if (AnimInstance && HitReactMontage) - { - AnimInstance->Montage_Play(HitReactMontage); - const FName SectionName = FName("FromFront"); - AnimInstance->Montage_JumpToSection(SectionName); - } -} - -void ABlasterCharacter::ReceiveDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatorController, - AActor* DamageCauser) -{ - Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth); + Super::BeginPlay(); UpdateHUDHealth(); - if (Health > 0.f) + if (HasAuthority()) { - PlayHitReactMontage(); + OnTakeAnyDamage.AddDynamic(this, &ABlasterCharacter::ReceiveDamage); } +} - if (Health == 0.f) - { - ABlasterGameMode* GameMode = GetWorld()->GetAuthGameMode(); - if (GameMode) - { - BlasterPlayerController = BlasterPlayerController == nullptr ? Cast(Controller) : BlasterPlayerController; - ABlasterPlayerController* AttackerController = Cast(InstigatorController); - GameMode->PlayerEliminated(this, BlasterPlayerController, AttackerController); - } - } +void ABlasterCharacter::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + RotateInPlace(DeltaTime); + HideCameraIfCharacterClose(); + PollInit(); } void ABlasterCharacter::RotateInPlace(float DeltaTime) @@ -253,7 +186,7 @@ void ABlasterCharacter::RotateInPlace(float DeltaTime) TurningInPlace = ETurningInPlace::ETIP_NotTurning; return; } - if (GetLocalRole() > ROLE_SimulatedProxy && IsLocallyControlled()) + if (GetLocalRole() > ENetRole::ROLE_SimulatedProxy && IsLocallyControlled()) { AimOffset(DeltaTime); } @@ -268,46 +201,111 @@ void ABlasterCharacter::RotateInPlace(float DeltaTime) } } -void ABlasterCharacter::Tick(float DeltaTime) -{ - Super::Tick(DeltaTime); - - RotateInPlace(DeltaTime); - HideCameraIfCharacterClose(); - PollInit(); -} - -void ABlasterCharacter::PollInit() -{ - if (BlasterPlayerState == nullptr) - { - BlasterPlayerState = GetPlayerState(); - if (BlasterPlayerState) - { - // Initialize Score now we have the PlayerState - BlasterPlayerState->IncreaseScore(0.f); - BlasterPlayerState->IncreaseDefeats(0); - } - } -} - void ABlasterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ABlasterCharacter::Jump); - PlayerInputComponent->BindAction("Equip", IE_Pressed, this, &ABlasterCharacter::EquipButtonPressed); - PlayerInputComponent->BindAction("Reload", IE_Pressed, this, &ABlasterCharacter::ReloadButtonPressed); - PlayerInputComponent->BindAction("Crouch", IE_Pressed, this, &ABlasterCharacter::CrouchButtonPressed); - PlayerInputComponent->BindAction("Aim", IE_Pressed, this, &ABlasterCharacter::AimButtonPressed); - PlayerInputComponent->BindAction("Aim", IE_Released, this, &ABlasterCharacter::AimButtonReleased); - PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &ABlasterCharacter::FireButtonPressed); - PlayerInputComponent->BindAction("Fire", IE_Released, this, &ABlasterCharacter::FireButtonReleased); PlayerInputComponent->BindAxis("MoveForward", this, &ABlasterCharacter::MoveForward); PlayerInputComponent->BindAxis("MoveRight", this, &ABlasterCharacter::MoveRight); PlayerInputComponent->BindAxis("Turn", this, &ABlasterCharacter::Turn); PlayerInputComponent->BindAxis("LookUp", this, &ABlasterCharacter::LookUp); + + PlayerInputComponent->BindAction("Equip", IE_Pressed, this, &ABlasterCharacter::EquipButtonPressed); + PlayerInputComponent->BindAction("Crouch", IE_Pressed, this, &ABlasterCharacter::CrouchButtonPressed); + PlayerInputComponent->BindAction("Aim", IE_Pressed, this, &ABlasterCharacter::AimButtonPressed); + PlayerInputComponent->BindAction("Aim", IE_Released, this, &ABlasterCharacter::AimButtonReleased); + PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &ABlasterCharacter::FireButtonPressed); + PlayerInputComponent->BindAction("Fire", IE_Released, this, &ABlasterCharacter::FireButtonReleased); + PlayerInputComponent->BindAction("Reload", IE_Pressed, this, &ABlasterCharacter::ReloadButtonPressed); +} + +void ABlasterCharacter::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + if (Combat) + { + Combat->Character = this; + } +} + +void ABlasterCharacter::PlayFireMontage(bool bAiming) +{ + if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; + + UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); + if (AnimInstance && FireWeaponMontage) + { + AnimInstance->Montage_Play(FireWeaponMontage); + FName SectionName; + SectionName = bAiming ? FName("RifleADS") : FName("RifleHip"); + AnimInstance->Montage_JumpToSection(SectionName); + } +} + +void ABlasterCharacter::PlayReloadMontage() +{ + if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; + + UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); + if (AnimInstance && ReloadMontage) + { + AnimInstance->Montage_Play(ReloadMontage); + FName SectionName; + + switch (Combat->EquippedWeapon->GetWeaponType()) + { + case EWeaponType::EWT_AssaultRifle: + SectionName = FName("Rifle"); + break; + } + + AnimInstance->Montage_JumpToSection(SectionName); + } +} + +void ABlasterCharacter::PlayEliminatedMontage() +{ + UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); + if (AnimInstance && EliminatedMontage) + { + AnimInstance->Montage_Play(EliminatedMontage); + } +} + +void ABlasterCharacter::PlayHitReactMontage() +{ + if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; + + UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); + if (AnimInstance && HitReactMontage) + { + AnimInstance->Montage_Play(HitReactMontage); + FName SectionName("FromFront"); + AnimInstance->Montage_JumpToSection(SectionName); + } +} + +void ABlasterCharacter::ReceiveDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatorController, AActor* DamageCauser) +{ + Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth); + UpdateHUDHealth(); + if (Health > 0.f) + { + PlayHitReactMontage(); + } + + if (Health == 0.f) + { + ABlasterGameMode* BlasterGameMode = GetWorld()->GetAuthGameMode(); + if (BlasterGameMode) + { + BlasterPlayerController = BlasterPlayerController == nullptr ? Cast(Controller) : BlasterPlayerController; + ABlasterPlayerController* AttackerController = Cast(InstigatorController); + BlasterGameMode->PlayerEliminated(this, BlasterPlayerController, AttackerController); + } + } } void ABlasterCharacter::MoveForward(float Value) @@ -317,7 +315,6 @@ void ABlasterCharacter::MoveForward(float Value) { const FRotator YawRotation(0.f, Controller->GetControlRotation().Yaw, 0.f); const FVector Direction(FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X)); - AddMovementInput(Direction, Value); } } @@ -329,7 +326,6 @@ void ABlasterCharacter::MoveRight(float Value) { const FRotator YawRotation(0.f, Controller->GetControlRotation().Yaw, 0.f); const FVector Direction(FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y)); - AddMovementInput(Direction, Value); } } @@ -360,13 +356,9 @@ void ABlasterCharacter::EquipButtonPressed() } } -void ABlasterCharacter::ReloadButtonPressed() +void ABlasterCharacter::ServerEquipButtonPressed_Implementation() { - if (bDisableGameplay) return; - if (Combat) - { - Combat->Reload(); - } + EquipButtonPressed(); } void ABlasterCharacter::CrouchButtonPressed() @@ -382,6 +374,15 @@ void ABlasterCharacter::CrouchButtonPressed() } } +void ABlasterCharacter::ReloadButtonPressed() +{ + if (bDisableGameplay) return; + if (Combat) + { + Combat->Reload(); + } +} + void ABlasterCharacter::AimButtonPressed() { if (bDisableGameplay) return; @@ -400,47 +401,24 @@ void ABlasterCharacter::AimButtonReleased() } } -void ABlasterCharacter::Jump() +float ABlasterCharacter::CalculateSpeed() { - if (bDisableGameplay) return; - if (bIsCrouched) - { - UnCrouch(); - } - else - { - Super::Jump(); - } -} - -void ABlasterCharacter::FireButtonPressed() -{ - if (Combat) - { - Combat->FireButtonPressed(true); - } -} - -void ABlasterCharacter::FireButtonReleased() -{ - if (Combat) - { - Combat->FireButtonPressed(false); - } + FVector Velocity = GetVelocity(); + Velocity.Z = 0.f; + return Velocity.Size(); } void ABlasterCharacter::AimOffset(float DeltaTime) { if (Combat && Combat->EquippedWeapon == nullptr) return; - - const float Speed = CalculateSpeed(); - const bool bIsInAir = GetCharacterMovement()->IsFalling(); + float Speed = CalculateSpeed(); + bool bIsInAir = GetCharacterMovement()->IsFalling(); if (Speed == 0.f && !bIsInAir) // Standing still, not jumping { bRotateRootBone = true; - const FRotator CurrentAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f); - const FRotator DeltaAimRotation = UKismetMathLibrary::NormalizedDeltaRotator(CurrentAimRotation, StartingAimRotation); + FRotator CurrentAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f); + FRotator DeltaAimRotation = UKismetMathLibrary::NormalizedDeltaRotator(CurrentAimRotation, StartingAimRotation); AO_Yaw = DeltaAimRotation.Yaw; if (TurningInPlace == ETurningInPlace::ETIP_NotTurning) { @@ -464,13 +442,11 @@ void ABlasterCharacter::AimOffset(float DeltaTime) void ABlasterCharacter::CalculateAO_Pitch() { AO_Pitch = GetBaseAimRotation().Pitch; - - // Fix pitch/yaw compression if (AO_Pitch > 90.f && !IsLocallyControlled()) { // map pitch from [270, 360) to [-90, 0) - const FVector2d InRange(270.f, 360.f); - const FVector2d OutRange(-90.f, 0.f); + FVector2D InRange(270.f, 360.f); + FVector2D OutRange(-90.f, 0.f); AO_Pitch = FMath::GetMappedRangeValueClamped(InRange, OutRange, AO_Pitch); } } @@ -478,20 +454,18 @@ void ABlasterCharacter::CalculateAO_Pitch() void ABlasterCharacter::SimProxiesTurn() { if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; - bRotateRootBone = false; - - const float Speed = CalculateSpeed(); + float Speed = CalculateSpeed(); if (Speed > 0.f) { TurningInPlace = ETurningInPlace::ETIP_NotTurning; return; } - + ProxyRotationLastFrame = ProxyRotation; ProxyRotation = GetActorRotation(); ProxyYaw = UKismetMathLibrary::NormalizedDeltaRotator(ProxyRotation, ProxyRotationLastFrame).Yaw; - + if (FMath::Abs(ProxyYaw) > TurnThreshold) { if (ProxyYaw > TurnThreshold) @@ -508,13 +482,39 @@ void ABlasterCharacter::SimProxiesTurn() } return; } - TurningInPlace = ETurningInPlace::ETIP_NotTurning; + } -void ABlasterCharacter::ServerEquipButtonPressed_Implementation() +void ABlasterCharacter::Jump() { - EquipButtonPressed(); + if (bDisableGameplay) return; + if (bIsCrouched) + { + UnCrouch(); + } + else + { + Super::Jump(); + } +} + +void ABlasterCharacter::FireButtonPressed() +{ + if (bDisableGameplay) return; + if (Combat) + { + Combat->FireButtonPressed(true); + } +} + +void ABlasterCharacter::FireButtonReleased() +{ + if (bDisableGameplay) return; + if (Combat) + { + Combat->FireButtonPressed(false); + } } void ABlasterCharacter::TurnInPlace(float DeltaTime) @@ -537,18 +537,6 @@ void ABlasterCharacter::TurnInPlace(float DeltaTime) StartingAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f); } } - // switch (TurningInPlace) - // { - // case ETurningInPlace::ETIP_Left: - // UE_LOG(LogTemp, Warning, TEXT("TurningInPlace: Left")); - // break; - // case ETurningInPlace::ETIP_Right: - // UE_LOG(LogTemp, Warning, TEXT("TurningInPlace: Right")); - // break; - // case ETurningInPlace::ETIP_NotTurning: - // UE_LOG(LogTemp, Warning, TEXT("TurningInPlace: NotTurning")); - // break; - // } } void ABlasterCharacter::HideCameraIfCharacterClose() @@ -572,13 +560,6 @@ void ABlasterCharacter::HideCameraIfCharacterClose() } } -float ABlasterCharacter::CalculateSpeed() -{ - FVector Velocity = GetVelocity(); - Velocity.Z = 0.f; - return Velocity.Size(); -} - void ABlasterCharacter::OnRep_Health() { UpdateHUDHealth(); @@ -597,6 +578,20 @@ void ABlasterCharacter::UpdateHUDHealth() } } +void ABlasterCharacter::PollInit() +{ + if (BlasterPlayerState == nullptr) + { + BlasterPlayerState = GetPlayerState(); + if (BlasterPlayerState) + { + // Initialize Score now we have the PlayerState + BlasterPlayerState->IncreaseScore(0.f); + BlasterPlayerState->IncreaseDefeats(0); + } + } +} + void ABlasterCharacter::UpdateDissolveMaterial(float DissolveValue) { if (DynamicDissolveMaterialInstance) @@ -621,7 +616,6 @@ void ABlasterCharacter::SetOverlappingWeapon(AWeapon* Weapon) { OverlappingWeapon->ShowPickupWidget(false); } - OverlappingWeapon = Weapon; if (IsLocallyControlled()) { @@ -632,6 +626,18 @@ void ABlasterCharacter::SetOverlappingWeapon(AWeapon* Weapon) } } +void ABlasterCharacter::OnRep_OverlappingWeapon(AWeapon* LastWeapon) +{ + if (OverlappingWeapon) + { + OverlappingWeapon->ShowPickupWidget(true); + } + if (LastWeapon) + { + LastWeapon->ShowPickupWidget(false); + } +} + bool ABlasterCharacter::IsWeaponEquipped() { return Combat && Combat->EquippedWeapon; @@ -645,14 +651,12 @@ bool ABlasterCharacter::IsAiming() AWeapon* ABlasterCharacter::GetEquippedWeapon() { if (Combat == nullptr) return nullptr; - return Combat->EquippedWeapon; } FVector ABlasterCharacter::GetHitTarget() const { if (Combat == nullptr) return FVector(); - return Combat->HitTarget; } @@ -660,16 +664,4 @@ ECombatState ABlasterCharacter::GetCombatState() const { if (Combat == nullptr) return ECombatState::ECS_MAX; return Combat->CombatState; -} - -void ABlasterCharacter::OnRep_OverlappingWeapon(AWeapon* LastWeapon) -{ - if (OverlappingWeapon) - { - OverlappingWeapon->ShowPickupWidget(true); - } - if (LastWeapon) - { - LastWeapon->ShowPickupWidget(false); - } -} +} \ No newline at end of file diff --git a/Source/Blaster/Components/CombatComponent.cpp b/Source/Blaster/Components/CombatComponent.cpp index 6cfba8e..0deca32 100644 --- a/Source/Blaster/Components/CombatComponent.cpp +++ b/Source/Blaster/Components/CombatComponent.cpp @@ -25,8 +25,8 @@ void UCombatComponent::GetLifetimeReplicatedProps(TArray& Out Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(UCombatComponent, EquippedWeapon); - DOREPLIFETIME_CONDITION(UCombatComponent, CarriedAmmo, COND_OwnerOnly); DOREPLIFETIME(UCombatComponent, bAiming); + DOREPLIFETIME_CONDITION(UCombatComponent, CarriedAmmo, COND_OwnerOnly); DOREPLIFETIME(UCombatComponent, CombatState); } @@ -65,6 +65,273 @@ void UCombatComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActo } } +void UCombatComponent::FireButtonPressed(bool bPressed) +{ + bFireButtonPressed = bPressed; + if (bFireButtonPressed && EquippedWeapon) + { + Fire(); + } +} + +void UCombatComponent::Fire() +{ + if (CanFire()) + { + bCanFire = false; + ServerFire(HitTarget); + if (EquippedWeapon) + { + CrosshairShootingFactor = .75f; + } + StartFireTimer(); + } +} + +void UCombatComponent::StartFireTimer() +{ + if (EquippedWeapon == nullptr || Character == nullptr) return; + Character->GetWorldTimerManager().SetTimer( + FireTimer, + this, + &UCombatComponent::FireTimerFinished, + EquippedWeapon->FireDelay + ); +} + +void UCombatComponent::FireTimerFinished() +{ + if (EquippedWeapon == nullptr) return; + bCanFire = true; + if (bFireButtonPressed && EquippedWeapon->bAutomatic) + { + Fire(); + } + if (EquippedWeapon->IsEmpty()) + { + Reload(); + } +} + +void UCombatComponent::ServerFire_Implementation(const FVector_NetQuantize& TraceHitTarget) +{ + MulticastFire(TraceHitTarget); +} + +void UCombatComponent::MulticastFire_Implementation(const FVector_NetQuantize& TraceHitTarget) +{ + if (EquippedWeapon == nullptr) return; + if (Character && CombatState == ECombatState::ECS_Unoccupied) + { + Character->PlayFireMontage(bAiming); + EquippedWeapon->Fire(TraceHitTarget); + } +} + +void UCombatComponent::EquipWeapon(AWeapon* WeaponToEquip) +{ + if (Character == nullptr || WeaponToEquip == nullptr) return; + if (EquippedWeapon) + { + EquippedWeapon->Dropped(); + } + EquippedWeapon = WeaponToEquip; + EquippedWeapon->SetWeaponState(EWeaponState::EWS_Equipped); + const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("RightHandSocket")); + if (HandSocket) + { + HandSocket->AttachActor(EquippedWeapon, Character->GetMesh()); + } + EquippedWeapon->SetOwner(Character); + EquippedWeapon->SetHUDAmmo(); + + if (CarriedAmmoMap.Contains(EquippedWeapon->GetWeaponType())) + { + CarriedAmmo = CarriedAmmoMap[EquippedWeapon->GetWeaponType()]; + } + + Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; + if (Controller) + { + Controller->SetHUDCarriedAmmo(CarriedAmmo); + } + + if (EquippedWeapon->EquipSound) + { + UGameplayStatics::PlaySoundAtLocation( + this, + EquippedWeapon->EquipSound, + Character->GetActorLocation() + ); + } + + if (EquippedWeapon->IsEmpty()) + { + Reload(); + } + + Character->GetCharacterMovement()->bOrientRotationToMovement = false; + Character->bUseControllerRotationYaw = true; +} + +void UCombatComponent::Reload() +{ + if (CarriedAmmo > 0 && CombatState != ECombatState::ECS_Reloading) + { + ServerReload(); + } +} + +void UCombatComponent::ServerReload_Implementation() +{ + if (Character == nullptr || EquippedWeapon == nullptr) return; + + // return if weapon mag is at max capacity + if (EquippedWeapon->GetAmmo() == EquippedWeapon->GetMagCapacity()) return; + + CombatState = ECombatState::ECS_Reloading; + HandleReload(); +} + +void UCombatComponent::FinishedReloading() +{ + if (Character == nullptr) return; + if (Character->HasAuthority()) + { + CombatState = ECombatState::ECS_Unoccupied; + UpdateAmmoValues(); + } + if (bFireButtonPressed) + { + Fire(); + } +} + +void UCombatComponent::UpdateAmmoValues() +{ + if (Character == nullptr || EquippedWeapon == nullptr) return; + int32 ReloadAmount = AmountToReload(); + if (CarriedAmmoMap.Contains(EquippedWeapon->GetWeaponType())) + { + CarriedAmmoMap[EquippedWeapon->GetWeaponType()] -= ReloadAmount; + CarriedAmmo = CarriedAmmoMap[EquippedWeapon->GetWeaponType()]; + } + Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; + if (Controller) + { + Controller->SetHUDCarriedAmmo(CarriedAmmo); + } + EquippedWeapon->AddAmmo(-ReloadAmount); +} + +void UCombatComponent::OnRep_CombatState() +{ + switch (CombatState) + { + case ECombatState::ECS_Reloading: + HandleReload(); + break; + case ECombatState::ECS_Unoccupied: + if (bFireButtonPressed) + { + Fire(); + } + break; + } +} + +void UCombatComponent::HandleReload() +{ + Character->PlayReloadMontage(); +} + +int32 UCombatComponent::AmountToReload() +{ + if (EquippedWeapon == nullptr) return 0; + int32 RoomInMag = EquippedWeapon->GetMagCapacity() - EquippedWeapon->GetAmmo(); + + if (CarriedAmmoMap.Contains(EquippedWeapon->GetWeaponType())) + { + int32 AmountCarried = CarriedAmmoMap[EquippedWeapon->GetWeaponType()]; + int32 Least = FMath::Min(RoomInMag, AmountCarried); + return FMath::Clamp(RoomInMag, 0, Least); + } + return 0; +} + +void UCombatComponent::OnRep_EquippedWeapon() +{ + if (EquippedWeapon && Character) + { + EquippedWeapon->SetWeaponState(EWeaponState::EWS_Equipped); + const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("RightHandSocket")); + if (HandSocket) + { + HandSocket->AttachActor(EquippedWeapon, Character->GetMesh()); + } + Character->GetCharacterMovement()->bOrientRotationToMovement = false; + Character->bUseControllerRotationYaw = true; + + if (EquippedWeapon->EquipSound) + { + UGameplayStatics::PlaySoundAtLocation( + this, + EquippedWeapon->EquipSound, + Character->GetActorLocation() + ); + } + } +} + +void UCombatComponent::TraceUnderCrosshairs(FHitResult& TraceHitResult) +{ + FVector2D ViewportSize; + if (GEngine && GEngine->GameViewport) + { + GEngine->GameViewport->GetViewportSize(ViewportSize); + } + + FVector2D CrosshairLocation(ViewportSize.X / 2.f, ViewportSize.Y / 2.f); + FVector CrosshairWorldPosition; + FVector CrosshairWorldDirection; + bool bScreenToWorld = UGameplayStatics::DeprojectScreenToWorld( + UGameplayStatics::GetPlayerController(this, 0), + CrosshairLocation, + CrosshairWorldPosition, + CrosshairWorldDirection + ); + + if (bScreenToWorld) + { + FVector Start = CrosshairWorldPosition; + + if (Character) + { + float DistanceToCharacter = (Character->GetActorLocation() - Start).Size(); + Start += CrosshairWorldDirection * (DistanceToCharacter + 100.f); + } + + FVector End = Start + CrosshairWorldDirection * TRACE_LENGTH; + + GetWorld()->LineTraceSingleByChannel( + TraceHitResult, + Start, + End, + ECollisionChannel::ECC_Visibility + ); + if (TraceHitResult.GetActor() && TraceHitResult.GetActor()->Implements()) + { + HUDPackage.CrosshairsColor = FLinearColor::Red; + } + else + { + HUDPackage.CrosshairsColor = FLinearColor::White; + } + + if (!TraceHitResult.bBlockingHit) TraceHitResult.ImpactPoint = End; + } +} + void UCombatComponent::SetHUDCrosshairs(float DeltaTime) { if (Character == nullptr || Character->Controller == nullptr) return; @@ -80,22 +347,22 @@ void UCombatComponent::SetHUDCrosshairs(float DeltaTime) HUDPackage.CrosshairsCenter = EquippedWeapon->CrosshairsCenter; HUDPackage.CrosshairsLeft = EquippedWeapon->CrosshairsLeft; HUDPackage.CrosshairsRight = EquippedWeapon->CrosshairsRight; - HUDPackage.CrosshairsTop = EquippedWeapon->CrosshairsTop; HUDPackage.CrosshairsBottom = EquippedWeapon->CrosshairsBottom; + HUDPackage.CrosshairsTop = EquippedWeapon->CrosshairsTop; } else { HUDPackage.CrosshairsCenter = nullptr; HUDPackage.CrosshairsLeft = nullptr; HUDPackage.CrosshairsRight = nullptr; - HUDPackage.CrosshairsTop = nullptr; HUDPackage.CrosshairsBottom = nullptr; + HUDPackage.CrosshairsTop = nullptr; } - // Calculate crosshair spread - // Velocity [0, 600] -> Spread [0, 1] - const FVector2D WalkSpeedRange(0.f, Character->GetCharacterMovement()->MaxWalkSpeed); - const FVector2D VelocityMultiplierRange(0.f, 1.f); + + // [0, 600] -> [0, 1] + FVector2D WalkSpeedRange(0.f, Character->GetCharacterMovement()->MaxWalkSpeed); + FVector2D VelocityMultiplierRange(0.f, 1.f); FVector Velocity = Character->GetVelocity(); Velocity.Z = 0.f; @@ -170,59 +437,9 @@ void UCombatComponent::ServerSetAiming_Implementation(bool bIsAiming) } } -void UCombatComponent::Fire() -{ - if (CanFire()) - { - bCanFire = false; - ServerFire(HitTarget); - - if (EquippedWeapon) - { - CrosshairShootingFactor = 0.75f; - } - - StartFireTimer(); - } - -} - -void UCombatComponent::FireButtonPressed(bool bPressed) -{ - bFireButtonPressed = bPressed; - - if (bFireButtonPressed && EquippedWeapon) - { - Fire(); - } -} - -void UCombatComponent::StartFireTimer() -{ - if (EquippedWeapon == nullptr || Character == nullptr) return; - - Character->GetWorldTimerManager().SetTimer(FireTimer, this, &UCombatComponent::FireTimerFinished, EquippedWeapon->FireDelay); -} - -void UCombatComponent::FireTimerFinished() -{ - if (EquippedWeapon == nullptr) return; - - bCanFire = true; - if (bFireButtonPressed && EquippedWeapon->bAutomatic) - { - Fire(); - } - if (EquippedWeapon->IsEmpty()) - { - Reload(); - } -} - bool UCombatComponent::CanFire() { if (EquippedWeapon == nullptr) return false; - return !EquippedWeapon->IsEmpty() && bCanFire && CombatState == ECombatState::ECS_Unoccupied; } @@ -239,212 +456,3 @@ void UCombatComponent::InitializeCarriedAmmo() { CarriedAmmoMap.Emplace(EWeaponType::EWT_AssaultRifle, StartingARAmmo); } - -void UCombatComponent::ServerFire_Implementation(const FVector_NetQuantize& TraceHitTarget) -{ - MulticastFire(TraceHitTarget); -} - -void UCombatComponent::MulticastFire_Implementation(const FVector_NetQuantize& TraceHitTarget) -{ - if (EquippedWeapon == nullptr) return; - if (Character && CombatState == ECombatState::ECS_Unoccupied) - { - Character->PlayFireMontage(bAiming); - EquippedWeapon->Fire(TraceHitTarget); - } -} - -void UCombatComponent::TraceUnderCrosshairs(FHitResult& TraceHitResult) -{ - FVector2d ViewPortSize; - if (GEngine && GEngine->GameViewport) - { - GEngine->GameViewport->GetViewportSize(ViewPortSize); - } - - FVector2d CrosshairLocation(ViewPortSize.X / 2.f, ViewPortSize.Y / 2.0f); - FVector CrosshairWorldPosition; - FVector CrosshairWorldDirection; - bool bScreenToWorld = UGameplayStatics::DeprojectScreenToWorld( - UGameplayStatics::GetPlayerController(this, 0), - CrosshairLocation, - CrosshairWorldPosition, - CrosshairWorldDirection - ); - - if (bScreenToWorld) - { - FVector Start = CrosshairWorldPosition; - if (Character) - { - float DistanceToCharacter = (Character->GetActorLocation() - Start).Size(); - Start += CrosshairWorldDirection * (DistanceToCharacter + 60.f); - } - FVector End = Start + CrosshairWorldDirection * TRACE_LENGTH; - - GetWorld()->LineTraceSingleByChannel( - TraceHitResult, - Start, - End, - ECollisionChannel::ECC_Visibility - ); - if (TraceHitResult.GetActor() && TraceHitResult.GetActor()->Implements()) - { - HUDPackage.CrosshairColor = FLinearColor::Red; - } - else - { - HUDPackage.CrosshairColor = FLinearColor::White; - } - - if (!TraceHitResult.bBlockingHit) TraceHitResult.ImpactPoint = End; - } -} - -void UCombatComponent::EquipWeapon(AWeapon* WeaponToEquip) -{ - if (Character == nullptr || WeaponToEquip == nullptr) return; - if (EquippedWeapon) - { - EquippedWeapon->Dropped(); - } - EquippedWeapon = WeaponToEquip; - EquippedWeapon->SetWeaponState(EWeaponState::EWS_Equipped); - const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("RightHandSocket")); - if (HandSocket) - { - HandSocket->AttachActor(EquippedWeapon, Character->GetMesh()); - } - EquippedWeapon->SetOwner(Character); - EquippedWeapon->SetHUDAmmo(); - - if (CarriedAmmoMap.Contains(EquippedWeapon->GetWeaponType())) - { - CarriedAmmo = CarriedAmmoMap[EquippedWeapon->GetWeaponType()]; - } - - Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; - if (Controller) - { - Controller->SetHUDCarriedAmmo(CarriedAmmo); - } - - if (EquippedWeapon->EquipSound) - { - UGameplayStatics::PlaySoundAtLocation(this, EquippedWeapon->EquipSound, Character->GetActorLocation()); - } - - if (EquippedWeapon->IsEmpty()) - { - Reload(); - } - Character->GetCharacterMovement()->bOrientRotationToMovement = false; - Character->bUseControllerRotationYaw = true; -} - -void UCombatComponent::Reload() -{ - if (CarriedAmmo > 0 && CombatState != ECombatState::ECS_Reloading) - { - ServerReload(); - } -} - -void UCombatComponent::FinishedReloading() -{ - if (Character == nullptr) return; - if (Character->HasAuthority()) - { - CombatState = ECombatState::ECS_Unoccupied; - UpdateAmmoValues(); - } - if (bFireButtonPressed) - { - Fire(); - } -} - -void UCombatComponent::UpdateAmmoValues() -{ - if (Character == nullptr || EquippedWeapon == nullptr) return; - const int32 ReloadAmount = AmountToReload(); - if (CarriedAmmoMap.Contains(EquippedWeapon->GetWeaponType())) - { - CarriedAmmoMap[EquippedWeapon->GetWeaponType()] -= ReloadAmount; - CarriedAmmo = CarriedAmmoMap[EquippedWeapon->GetWeaponType()]; - } - Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; - if (Controller) - { - Controller->SetHUDCarriedAmmo(CarriedAmmo); - } - EquippedWeapon->AddAmmo(-ReloadAmount); -} - -void UCombatComponent::ServerReload_Implementation() -{ - if (Character == nullptr || EquippedWeapon == nullptr) return; - - // return if weapon mag is at max capacity - if (EquippedWeapon->GetAmmo() == EquippedWeapon->GetMagCapacity()) return; - - CombatState = ECombatState::ECS_Reloading; - HandleReload(); -} - -void UCombatComponent::OnRep_CombatState() -{ - switch (CombatState) - { - case ECombatState::ECS_Reloading: - HandleReload(); - break; - case ECombatState::ECS_Unoccupied: - if (bFireButtonPressed) - { - Fire(); - } - break; - } -} - -void UCombatComponent::HandleReload() -{ - Character->PlayReloadMontage(); -} - -int32 UCombatComponent::AmountToReload() -{ - if (EquippedWeapon == nullptr) return 0; - - const int32 RoomInMag = EquippedWeapon->GetMagCapacity() - EquippedWeapon->GetAmmo(); - - if (CarriedAmmoMap.Contains(EquippedWeapon->GetWeaponType())) - { - const int32 AmountCarried = CarriedAmmoMap[EquippedWeapon->GetWeaponType()]; - const int32 Least = FMath::Min(RoomInMag, AmountCarried); - return FMath::Clamp(RoomInMag, 0, Least); - } - - return 0; -} - -void UCombatComponent::OnRep_EquippedWeapon() -{ - if (EquippedWeapon && Character) - { - EquippedWeapon->SetWeaponState(EWeaponState::EWS_Equipped); - const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("RightHandSocket")); - if (HandSocket) - { - HandSocket->AttachActor(EquippedWeapon, Character->GetMesh()); - } - if (EquippedWeapon->EquipSound) - { - UGameplayStatics::PlaySoundAtLocation(this, EquippedWeapon->EquipSound, Character->GetActorLocation()); - } - Character->GetCharacterMovement()->bOrientRotationToMovement = false; - Character->bUseControllerRotationYaw = true; - } -} diff --git a/Source/Blaster/GameMode/BlasterGameMode.cpp b/Source/Blaster/GameMode/BlasterGameMode.cpp index bc78cf4..315b502 100644 --- a/Source/Blaster/GameMode/BlasterGameMode.cpp +++ b/Source/Blaster/GameMode/BlasterGameMode.cpp @@ -27,30 +27,30 @@ void ABlasterGameMode::BeginPlay() LevelStartingTime = GetWorld()->GetTimeSeconds(); } -void ABlasterGameMode::Tick(float DeltaSeconds) +void ABlasterGameMode::Tick(float DeltaTime) { - Super::Tick(DeltaSeconds); + Super::Tick(DeltaTime); if (MatchState == MatchState::WaitingToStart) { - CountDownTime = WarmupTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; - if (CountDownTime <= 0.f) + CountdownTime = WarmupTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; + if (CountdownTime <= 0.f) { StartMatch(); } } else if (MatchState == MatchState::InProgress) { - CountDownTime = WarmupTime + MatchTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; - if (CountDownTime <= 0.f) + CountdownTime = WarmupTime + MatchTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; + if (CountdownTime <= 0.f) { SetMatchState(MatchState::Cooldown); } } else if (MatchState == MatchState::Cooldown) { - CountDownTime = WarmupTime + MatchTime + CooldownTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; - if (CountDownTime <= 0.f) + CountdownTime = CooldownTime + WarmupTime + MatchTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; + if (CountdownTime <= 0.f) { RestartGame(); } @@ -61,19 +61,20 @@ void ABlasterGameMode::OnMatchStateSet() { Super::OnMatchStateSet(); - for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator) + for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It) { - ABlasterPlayerController* PlayerController = Cast(*Iterator); - if (PlayerController) + ABlasterPlayerController* BlasterPlayer = Cast(*It); + if (BlasterPlayer) { - PlayerController->OnMatchStateSet(MatchState); + BlasterPlayer->OnMatchStateSet(MatchState); } } } -void ABlasterGameMode::PlayerEliminated(ABlasterCharacter* EliminatedCharacter, ABlasterPlayerController* VictimController, - ABlasterPlayerController* AttackerController) +void ABlasterGameMode::PlayerEliminated(class ABlasterCharacter* EliminatedCharacter, class ABlasterPlayerController* VictimController, ABlasterPlayerController* AttackerController) { + if (AttackerController == nullptr || AttackerController->PlayerState == nullptr) return; + if (VictimController == nullptr || VictimController->PlayerState == nullptr) return; ABlasterPlayerState* AttackerPlayerState = AttackerController ? Cast(AttackerController->PlayerState) : nullptr; ABlasterPlayerState* VictimPlayerState = VictimController ? Cast(VictimController->PlayerState) : nullptr; @@ -99,12 +100,11 @@ void ABlasterGameMode::RequestRespawn(ACharacter* EliminatedCharacter, AControll EliminatedCharacter->Reset(); EliminatedCharacter->Destroy(); } - if (EliminatedController) { TArray PlayerStarts; UGameplayStatics::GetAllActorsOfClass(this, APlayerStart::StaticClass(), PlayerStarts); - const int32 Selection = FMath::RandRange(0, PlayerStarts.Num() - 1); + int32 Selection = FMath::RandRange(0, PlayerStarts.Num() - 1); RestartPlayerAtPlayerStart(EliminatedController, PlayerStarts[Selection]); } } diff --git a/Source/Blaster/GameMode/BlasterGameMode.h b/Source/Blaster/GameMode/BlasterGameMode.h index 1cf19f4..ff28489 100644 --- a/Source/Blaster/GameMode/BlasterGameMode.h +++ b/Source/Blaster/GameMode/BlasterGameMode.h @@ -22,7 +22,7 @@ class BLASTER_API ABlasterGameMode : public AGameMode public: ABlasterGameMode(); - virtual void Tick(float DeltaSeconds) override; + virtual void Tick(float DeltaTime) override; virtual void PlayerEliminated(class ABlasterCharacter* EliminatedCharacter, class ABlasterPlayerController* VictimController, class ABlasterPlayerController* AttackerController); virtual void RequestRespawn(ACharacter* EliminatedCharacter, AController* EliminatedController); @@ -43,8 +43,8 @@ protected: virtual void OnMatchStateSet() override; private: - float CountDownTime = 0.f; + float CountdownTime = 0.f; public: - FORCEINLINE float GetCountdownTime() const { return CountDownTime; } + FORCEINLINE float GetCountdownTime() const { return CountdownTime; } }; diff --git a/Source/Blaster/HUD/BlasterHUD.cpp b/Source/Blaster/HUD/BlasterHUD.cpp index 9a7424a..032525b 100644 --- a/Source/Blaster/HUD/BlasterHUD.cpp +++ b/Source/Blaster/HUD/BlasterHUD.cpp @@ -46,27 +46,27 @@ void ABlasterHUD::DrawHUD() if (HUDPackage.CrosshairsCenter) { const FVector2D Spread(0.f, 0.f); - DrawCrosshair(HUDPackage.CrosshairsCenter, ViewportCenter, Spread, HUDPackage.CrosshairColor); + DrawCrosshair(HUDPackage.CrosshairsCenter, ViewportCenter, Spread, HUDPackage.CrosshairsColor); } if (HUDPackage.CrosshairsLeft) { const FVector2D Spread(-SpreadScaled, 0.f); - DrawCrosshair(HUDPackage.CrosshairsLeft, ViewportCenter, Spread, HUDPackage.CrosshairColor); + DrawCrosshair(HUDPackage.CrosshairsLeft, ViewportCenter, Spread, HUDPackage.CrosshairsColor); } if (HUDPackage.CrosshairsRight) { const FVector2D Spread(SpreadScaled, 0.f); - DrawCrosshair(HUDPackage.CrosshairsRight, ViewportCenter, Spread, HUDPackage.CrosshairColor); + DrawCrosshair(HUDPackage.CrosshairsRight, ViewportCenter, Spread, HUDPackage.CrosshairsColor); } if (HUDPackage.CrosshairsTop) { const FVector2D Spread(0.f, -SpreadScaled); - DrawCrosshair(HUDPackage.CrosshairsTop, ViewportCenter, Spread, HUDPackage.CrosshairColor); + DrawCrosshair(HUDPackage.CrosshairsTop, ViewportCenter, Spread, HUDPackage.CrosshairsColor); } if (HUDPackage.CrosshairsBottom) { const FVector2D Spread(0.f, SpreadScaled); - DrawCrosshair(HUDPackage.CrosshairsBottom, ViewportCenter, Spread, HUDPackage.CrosshairColor); + DrawCrosshair(HUDPackage.CrosshairsBottom, ViewportCenter, Spread, HUDPackage.CrosshairsColor); } } } diff --git a/Source/Blaster/HUD/BlasterHUD.h b/Source/Blaster/HUD/BlasterHUD.h index 00e1e73..7688097 100644 --- a/Source/Blaster/HUD/BlasterHUD.h +++ b/Source/Blaster/HUD/BlasterHUD.h @@ -18,7 +18,7 @@ public: UTexture2D* CrosshairsTop; UTexture2D* CrosshairsBottom; float CrosshairSpread; - FLinearColor CrosshairColor; + FLinearColor CrosshairsColor; }; /** diff --git a/Source/Blaster/PlayerController/BlasterPlayerController.cpp b/Source/Blaster/PlayerController/BlasterPlayerController.cpp index 9906af8..859cb0a 100644 --- a/Source/Blaster/PlayerController/BlasterPlayerController.cpp +++ b/Source/Blaster/PlayerController/BlasterPlayerController.cpp @@ -31,38 +31,18 @@ void ABlasterPlayerController::GetLifetimeReplicatedProps(TArray(InPawn)) - { - SetHUDHealth(BlasterCharacter->GetHealth(), BlasterCharacter->GetMaxHealth()); - } -} - -void ABlasterPlayerController::ReceivedPlayer() -{ - Super::ReceivedPlayer(); - - if (IsLocalController()) - { - ServerRequestServerTime(GetWorld()->GetTimeSeconds()); - } -} - -void ABlasterPlayerController::CheckTimeSync(float DeltaSeconds) -{ - TimeSyncRunningTime += DeltaSeconds; + TimeSyncRunningTime += DeltaTime; if (IsLocalController() && TimeSyncRunningTime > TimeSyncFrequency) { ServerRequestServerTime(GetWorld()->GetTimeSeconds()); @@ -70,51 +50,248 @@ void ABlasterPlayerController::CheckTimeSync(float DeltaSeconds) } } +void ABlasterPlayerController::ServerCheckMatchState_Implementation() +{ + ABlasterGameMode* GameMode = Cast(UGameplayStatics::GetGameMode(this)); + if (GameMode) + { + WarmupTime = GameMode->WarmupTime; + MatchTime = GameMode->MatchTime; + CooldownTime = GameMode->CooldownTime; + LevelStartingTime = GameMode->LevelStartingTime; + MatchState = GameMode->GetMatchState(); + ClientJoinMidgame(MatchState, WarmupTime, MatchTime, CooldownTime, LevelStartingTime); + } +} + +void ABlasterPlayerController::ClientJoinMidgame_Implementation(FName StateOfMatch, float Warmup, float Match, float Cooldown, float StartingTime) +{ + WarmupTime = Warmup; + MatchTime = Match; + CooldownTime = Cooldown; + LevelStartingTime = StartingTime; + MatchState = StateOfMatch; + OnMatchStateSet(MatchState); + if (BlasterHUD && MatchState == MatchState::WaitingToStart) + { + BlasterHUD->AddAnnouncementOverlay(); + } +} + +void ABlasterPlayerController::OnPossess(APawn* InPawn) +{ + Super::OnPossess(InPawn); + ABlasterCharacter* BlasterCharacter = Cast(InPawn); + if (BlasterCharacter) + { + SetHUDHealth(BlasterCharacter->GetHealth(), BlasterCharacter->GetMaxHealth()); + } +} + +void ABlasterPlayerController::SetHUDHealth(float Health, float MaxHealth) +{ + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + bool bHUDValid = BlasterHUD && + BlasterHUD->CharacterOverlay && + BlasterHUD->CharacterOverlay->HealthBar && + BlasterHUD->CharacterOverlay->HealthText; + if (bHUDValid) + { + const float HealthPercent = Health / MaxHealth; + BlasterHUD->CharacterOverlay->HealthBar->SetPercent(HealthPercent); + FString HealthText = FString::Printf(TEXT("%d/%d"), FMath::CeilToInt(Health), FMath::CeilToInt(MaxHealth)); + BlasterHUD->CharacterOverlay->HealthText->SetText(FText::FromString(HealthText)); + } + else + { + bInitializeCharacterOverlay = true; + HUDHealth = Health; + HUDMaxHealth = MaxHealth; + } +} + +void ABlasterPlayerController::SetHUDScore(float Score) +{ + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + bool bHUDValid = BlasterHUD && + BlasterHUD->CharacterOverlay && + BlasterHUD->CharacterOverlay->ScoreValue; + + if (bHUDValid) + { + FString ScoreText = FString::Printf(TEXT("%d"), FMath::FloorToInt(Score)); + BlasterHUD->CharacterOverlay->ScoreValue->SetText(FText::FromString(ScoreText)); + } + else + { + bInitializeCharacterOverlay = true; + HUDScore = Score; + } +} + +void ABlasterPlayerController::SetHUDDefeats(int32 Defeats) +{ + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + bool bHUDValid = BlasterHUD && + BlasterHUD->CharacterOverlay && + BlasterHUD->CharacterOverlay->DefeatsValue; + if (bHUDValid) + { + FString DefeatsText = FString::Printf(TEXT("%d"), Defeats); + BlasterHUD->CharacterOverlay->DefeatsValue->SetText(FText::FromString(DefeatsText)); + } + else + { + bInitializeCharacterOverlay = true; + HUDDefeats = Defeats; + } +} + +void ABlasterPlayerController::SetHUDWeaponAmmo(int32 Ammo) +{ + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + bool bHUDValid = BlasterHUD && + BlasterHUD->CharacterOverlay && + BlasterHUD->CharacterOverlay->WeaponAmmoValue; + if (bHUDValid) + { + FString AmmoText = FString::Printf(TEXT("%d"), Ammo); + BlasterHUD->CharacterOverlay->WeaponAmmoValue->SetText(FText::FromString(AmmoText)); + } +} + +void ABlasterPlayerController::SetHUDCarriedAmmo(int32 Ammo) +{ + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + bool bHUDValid = BlasterHUD && + BlasterHUD->CharacterOverlay && + BlasterHUD->CharacterOverlay->CarriedAmmoValue; + if (bHUDValid) + { + FString AmmoText = FString::Printf(TEXT("%d"), Ammo); + BlasterHUD->CharacterOverlay->CarriedAmmoValue->SetText(FText::FromString(AmmoText)); + } +} + +void ABlasterPlayerController::SetHUDMatchCountdown(float CountdownTime) +{ + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + bool bHUDValid = BlasterHUD && + BlasterHUD->CharacterOverlay && + BlasterHUD->CharacterOverlay->MatchCountdownText; + if (bHUDValid) + { + if (CountdownTime < 0.f) + { + BlasterHUD->CharacterOverlay->MatchCountdownText->SetText(FText()); + return; + } + + int32 Minutes = FMath::FloorToInt(CountdownTime / 60.f); + int32 Seconds = CountdownTime - Minutes * 60; + + FString CountdownText = FString::Printf(TEXT("%02d:%02d"), Minutes, Seconds); + BlasterHUD->CharacterOverlay->MatchCountdownText->SetText(FText::FromString(CountdownText)); + } +} + +void ABlasterPlayerController::SetHUDAnnouncementCountdown(float CountdownTime) +{ + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + bool bHUDValid = BlasterHUD && + BlasterHUD->Announcement && + BlasterHUD->Announcement->CountdownText; + if (bHUDValid) + { + if (CountdownTime < 0.f) + { + BlasterHUD->Announcement->CountdownText->SetText(FText()); + return; + } + + int32 Minutes = FMath::FloorToInt(CountdownTime / 60.f); + int32 Seconds = CountdownTime - Minutes * 60; + + FString CountdownText = FString::Printf(TEXT("%02d:%02d"), Minutes, Seconds); + BlasterHUD->Announcement->CountdownText->SetText(FText::FromString(CountdownText)); + } +} + +void ABlasterPlayerController::SetHUDTime() +{ + float TimeLeft = 0.f; + if (MatchState == MatchState::WaitingToStart) TimeLeft = WarmupTime - GetServerTime() + LevelStartingTime; + else if (MatchState == MatchState::InProgress) TimeLeft = WarmupTime + MatchTime - GetServerTime() + LevelStartingTime; + else if (MatchState == MatchState::Cooldown) TimeLeft = CooldownTime +WarmupTime + MatchTime - GetServerTime() + LevelStartingTime; + + uint32 SecondsLeft = FMath::CeilToInt(TimeLeft); + + if (HasAuthority()) + { + BlasterGameMode = BlasterGameMode == nullptr ? Cast(UGameplayStatics::GetGameMode(this)) : BlasterGameMode; + if (BlasterGameMode) + { + SecondsLeft = FMath::CeilToInt(BlasterGameMode->GetCountdownTime() + LevelStartingTime); + } + } + + if (CountdownInt != SecondsLeft) + { + if (MatchState == MatchState::WaitingToStart || MatchState == MatchState::Cooldown) + { + SetHUDAnnouncementCountdown(TimeLeft); + } + if (MatchState == MatchState::InProgress) + { + SetHUDMatchCountdown(TimeLeft); + } + } + + CountdownInt = SecondsLeft; +} + +void ABlasterPlayerController::PollInit() +{ + if (CharacterOverlay == nullptr) + { + if (BlasterHUD && BlasterHUD->CharacterOverlay) + { + CharacterOverlay = BlasterHUD->CharacterOverlay; + if (CharacterOverlay) + { + SetHUDHealth(HUDHealth, HUDMaxHealth); + SetHUDScore(HUDScore); + SetHUDDefeats(HUDDefeats); + } + } + } +} + +void ABlasterPlayerController::ServerRequestServerTime_Implementation(float TimeOfClientRequest) +{ + float ServerTimeOfReceipt = GetWorld()->GetTimeSeconds(); + ClientReportServerTime(TimeOfClientRequest, ServerTimeOfReceipt); +} + +void ABlasterPlayerController::ClientReportServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest) +{ + float RoundTripTime = GetWorld()->GetTimeSeconds() - TimeOfClientRequest; + float CurrentServerTime = TimeServerReceivedClientRequest + 0.5f * RoundTripTime; + ClientServerDelta = CurrentServerTime - GetWorld()->GetTimeSeconds(); +} + float ABlasterPlayerController::GetServerTime() { if (HasAuthority()) return GetWorld()->GetTimeSeconds(); - return GetWorld()->GetTimeSeconds() + ClientServerDelta; } -void ABlasterPlayerController::HandleMatchHasStarted() +void ABlasterPlayerController::ReceivedPlayer() { - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - if (BlasterHUD) + Super::ReceivedPlayer(); + if (IsLocalController()) { - BlasterHUD->AddCharacterOverlay(); - if (BlasterHUD->Announcement) - { - BlasterHUD->Announcement->SetVisibility(ESlateVisibility::Hidden); - } - } -} - -void ABlasterPlayerController::HandleCooldown() -{ - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - if (BlasterHUD) - { - BlasterHUD->CharacterOverlay->RemoveFromParent(); - bool bHUDValid = - BlasterHUD->Announcement && - BlasterHUD->Announcement->AnnouncementText && - BlasterHUD->Announcement->AnnouncementMessage; - - if (bHUDValid) - { - const FString AnnouncementText("New match starts in:"); - BlasterHUD->Announcement->AnnouncementText->SetText(FText::FromString(AnnouncementText)); - BlasterHUD->Announcement->AnnouncementMessage->SetText(FText()); - BlasterHUD->Announcement->SetVisibility(ESlateVisibility::Visible); - } - } - - ABlasterCharacter* BlasterCharacter = Cast(GetPawn()); - if (BlasterCharacter && BlasterCharacter->GetCombat()) - { - BlasterCharacter->bDisableGameplay = true; - BlasterCharacter->GetCombat()->FireButtonPressed(false); + ServerRequestServerTime(GetWorld()->GetTimeSeconds()); } } @@ -144,211 +321,41 @@ void ABlasterPlayerController::OnRep_MatchState() } } -void ABlasterPlayerController::ServerCheckMatchState_Implementation() +void ABlasterPlayerController::HandleMatchHasStarted() { - ABlasterGameMode* GameMode = Cast(UGameplayStatics::GetGameMode(this)); - if (GameMode) + BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; + if (BlasterHUD) { - LevelStartingTime = GameMode->LevelStartingTime; - WarmupTime = GameMode->WarmupTime; - MatchTime = GameMode->MatchTime; - CooldownTime = GameMode->CooldownTime; - MatchState = GameMode->GetMatchState(); - ClientJoinMidGame(MatchState, WarmupTime, MatchTime, CooldownTime, LevelStartingTime); - } -} - - -void ABlasterPlayerController::ClientJoinMidGame_Implementation(FName StateOfMatch, float Warmup, float Match, float Cooldown, float StartingTime) -{ - LevelStartingTime = StartingTime; - WarmupTime = Warmup; - MatchTime = Match; - CooldownTime = Cooldown; - MatchState = StateOfMatch; - OnMatchStateSet(MatchState); - - if (BlasterHUD && MatchState == MatchState::WaitingToStart) - { - BlasterHUD->AddAnnouncementOverlay(); - } -} - -void ABlasterPlayerController::ServerRequestServerTime_Implementation(float TimeOfClientRequest) -{ - const float ServerTimeOfReceipt = GetWorld()->GetTimeSeconds(); - ClientReportServerTime(TimeOfClientRequest, ServerTimeOfReceipt); -} - -void ABlasterPlayerController::ClientReportServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest) -{ - const float RoundTripTime = GetWorld()->GetTimeSeconds() - TimeOfClientRequest; - const float CurrentServerTime = TimeServerReceivedClientRequest + 0.5f * RoundTripTime; - - ClientServerDelta = CurrentServerTime - GetWorld()->GetTimeSeconds(); -} - -void ABlasterPlayerController::SetHUDTime() -{ - float TimeLeft = 0.f; - if (MatchState == MatchState::WaitingToStart) TimeLeft = WarmupTime - GetServerTime() + LevelStartingTime; - else if (MatchState == MatchState::InProgress) TimeLeft = WarmupTime + MatchTime - GetServerTime() + LevelStartingTime; - else if (MatchState == MatchState::Cooldown) TimeLeft = WarmupTime + MatchTime + CooldownTime - GetServerTime() + LevelStartingTime; - uint32 SecondsLeft = FMath::CeilToInt(TimeLeft); - - if (HasAuthority()) - { - BlasterGameMode = BlasterGameMode == nullptr ? Cast(UGameplayStatics::GetGameMode(this)) : BlasterGameMode; - if (BlasterGameMode) + BlasterHUD->AddCharacterOverlay(); + if (BlasterHUD->Announcement) { - SecondsLeft = FMath::CeilToInt(BlasterGameMode->GetCountdownTime() + LevelStartingTime); - } - } - - if (CountdownInt != SecondsLeft) - { - if (MatchState == MatchState::WaitingToStart || MatchState == MatchState::Cooldown) SetHUDAnnouncementCountdown(TimeLeft); - if (MatchState == MatchState::InProgress) SetHUDMatchCountdown(TimeLeft); - } - - CountdownInt = SecondsLeft; -} - -void ABlasterPlayerController::PollInit() -{ - if (CharacterOverlay == nullptr) - { - if (BlasterHUD && BlasterHUD->CharacterOverlay) - { - CharacterOverlay = BlasterHUD->CharacterOverlay; - if (CharacterOverlay) - { - SetHUDHealth(HUDHealth, HUDMaxHealth); - SetHUDScore(HUDScore); - SetHUDDefeats(HUDDefeats); - } + BlasterHUD->Announcement->SetVisibility(ESlateVisibility::Hidden); } } } -void ABlasterPlayerController::SetHUDHealth(float Health, float MaxHealth) +void ABlasterPlayerController::HandleCooldown() { BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - bool bHUDValid = CharacterOverlay && CharacterOverlay->HealthBar && CharacterOverlay->HealthText; - - if (bHUDValid) + if (BlasterHUD) { - const float HealthPercent = Health / MaxHealth; - BlasterHUD->CharacterOverlay->HealthBar->SetPercent(HealthPercent); - const FString HealthText = FString::Printf(TEXT("%d/%d"), FMath::CeilToInt(Health), FMath::CeilToInt(MaxHealth)); - BlasterHUD->CharacterOverlay->HealthText->SetText(FText::FromString(HealthText)); - } - else - { - bInitializeCharacterOverlay = true; - HUDHealth = Health; - HUDMaxHealth = MaxHealth; - } -} + BlasterHUD->CharacterOverlay->RemoveFromParent(); + bool bHUDValid =BlasterHUD->Announcement && + BlasterHUD->Announcement->AnnouncementText && + BlasterHUD->Announcement->AnnouncementMessage; -void ABlasterPlayerController::SetHUDScore(float Score) -{ - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - bool bHUDValid = CharacterOverlay && CharacterOverlay->ScoreValue; - - if (bHUDValid) - { - const FString ScoreAmount = FString::Printf(TEXT("%d"), FMath::FloorToInt(Score)); - BlasterHUD->CharacterOverlay->ScoreValue->SetText(FText::FromString(ScoreAmount)); - } - else - { - bInitializeCharacterOverlay = true; - HUDScore = Score; - } -} - -void ABlasterPlayerController::SetHUDDefeats(int32 Defeats) -{ - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - bool bHUDValid = CharacterOverlay && CharacterOverlay->DefeatsValue; - - if (bHUDValid) - { - const FString DefeatsAmount = FString::Printf(TEXT("%d"), Defeats); - BlasterHUD->CharacterOverlay->DefeatsValue->SetText(FText::FromString(DefeatsAmount)); - } - else - { - bInitializeCharacterOverlay = true; - HUDDefeats = Defeats; - } -} - -void ABlasterPlayerController::SetHUDWeaponAmmo(int32 Ammo) -{ - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - bool bHUDValid = CharacterOverlay && CharacterOverlay->WeaponAmmoValue; - - if (bHUDValid) - { - const FString WeaponAmmoAmount = FString::Printf(TEXT("%d"), Ammo); - BlasterHUD->CharacterOverlay->WeaponAmmoValue->SetText(FText::FromString(WeaponAmmoAmount)); - } -} - -void ABlasterPlayerController::SetHUDCarriedAmmo(int32 Ammo) -{ - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - bool bHUDValid = CharacterOverlay && CharacterOverlay->CarriedAmmoValue; - - if (bHUDValid) - { - const FString CarriedAmmoAmount = FString::Printf(TEXT("%d"), Ammo); - BlasterHUD->CharacterOverlay->CarriedAmmoValue->SetText(FText::FromString(CarriedAmmoAmount)); - } -} - -void ABlasterPlayerController::SetHUDMatchCountdown(float CountdownTime) -{ - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - bool bHUDValid = CharacterOverlay && CharacterOverlay->MatchCountdownText; - - if (bHUDValid) - { - if (CountdownTime < 0.f) + if (bHUDValid) { - BlasterHUD->CharacterOverlay->MatchCountdownText->SetText(FText()); - return; + BlasterHUD->Announcement->SetVisibility(ESlateVisibility::Visible); + FString AnnouncementText("New match starts in:"); + BlasterHUD->Announcement->AnnouncementText->SetText(FText::FromString(AnnouncementText)); + BlasterHUD->Announcement->AnnouncementMessage->SetText(FText()); } - const int32 Minutes = FMath::FloorToInt(CountdownTime / 60); - const int32 Seconds = CountdownTime - Minutes * 60; - - const FString CountdownText = FString::Printf(TEXT("%02d:%02d"), Minutes, Seconds); - BlasterHUD->CharacterOverlay->MatchCountdownText->SetText(FText::FromString(CountdownText)); } -} - -void ABlasterPlayerController::SetHUDAnnouncementCountdown(float CountdownTime) -{ - BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; - bool bHUDValid = - BlasterHUD && - BlasterHUD->Announcement && - BlasterHUD->Announcement->AnnouncementText && - BlasterHUD->Announcement->CountdownText; - - if (bHUDValid) + ABlasterCharacter* BlasterCharacter = Cast(GetPawn()); + if (BlasterCharacter && BlasterCharacter->GetCombat()) { - if (CountdownTime < 0.f) - { - BlasterHUD->Announcement->CountdownText->SetText(FText()); - return; - } - const int32 Minutes = FMath::FloorToInt(CountdownTime / 60); - const int32 Seconds = CountdownTime - Minutes * 60; - - const FString CountdownText = FString::Printf(TEXT("%02d:%02d"), Minutes, Seconds); - BlasterHUD->Announcement->CountdownText->SetText(FText::FromString(CountdownText)); + BlasterCharacter->bDisableGameplay = true; + BlasterCharacter->GetCombat()->FireButtonPressed(false); } } diff --git a/Source/Blaster/PlayerController/BlasterPlayerController.h b/Source/Blaster/PlayerController/BlasterPlayerController.h index 245955a..a995292 100644 --- a/Source/Blaster/PlayerController/BlasterPlayerController.h +++ b/Source/Blaster/PlayerController/BlasterPlayerController.h @@ -16,7 +16,7 @@ class BLASTER_API ABlasterPlayerController : public APlayerController public: - virtual void Tick(float DeltaSeconds) override; + virtual void Tick(float DeltaTime) override; virtual void OnPossess(APawn* InPawn) override; virtual void ReceivedPlayer() override; // Sync with server clock as soon as possible virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; @@ -38,7 +38,7 @@ public: protected: virtual void BeginPlay() override; - void CheckTimeSync(float DeltaSeconds); + void CheckTimeSync(float DeltaTime); void HandleMatchHasStarted(); void SetHUDTime(); void PollInit(); @@ -65,7 +65,7 @@ protected: void ServerCheckMatchState(); UFUNCTION(Client, Reliable) - void ClientJoinMidGame(FName StateOfMatch, float Warmup, float Match, float Cooldown, float StartingTime); + void ClientJoinMidgame(FName StateOfMatch, float Warmup, float Match, float Cooldown, float StartingTime); private: UPROPERTY() diff --git a/Source/Blaster/PlayerState/BlasterPlayerState.cpp b/Source/Blaster/PlayerState/BlasterPlayerState.cpp index b09d7f4..c2b710e 100644 --- a/Source/Blaster/PlayerState/BlasterPlayerState.cpp +++ b/Source/Blaster/PlayerState/BlasterPlayerState.cpp @@ -7,21 +7,6 @@ #include "Blaster/PlayerController/BlasterPlayerController.h" #include "Net/UnrealNetwork.h" -void ABlasterPlayerState::IncreaseScore(float ScoreAmount) -{ - SetScore(GetScore() + ScoreAmount); - - Character = GetCharacter(); - if (Character) - { - Controller = GetController(); - if (Controller) - { - Controller->SetHUDScore(GetScore()); - } - } -} - void ABlasterPlayerState::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); @@ -29,14 +14,28 @@ void ABlasterPlayerState::GetLifetimeReplicatedProps(TArray& DOREPLIFETIME(ABlasterPlayerState, Defeats); } +void ABlasterPlayerState::IncreaseScore(float ScoreAmount) +{ + SetScore(GetScore() + ScoreAmount); + Character = Character == nullptr ? Cast(GetPawn()) : Character; + if (Character) + { + Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; + if (Controller) + { + Controller->SetHUDScore(GetScore()); + } + } +} + void ABlasterPlayerState::OnRep_Score() { Super::OnRep_Score(); - Character = GetCharacter(); + Character = Character == nullptr ? Cast(GetPawn()) : Character; if (Character) { - Controller = GetController(); + Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; if (Controller) { Controller->SetHUDScore(GetScore()); @@ -47,11 +46,10 @@ void ABlasterPlayerState::OnRep_Score() void ABlasterPlayerState::IncreaseDefeats(int32 DefeatsAmount) { Defeats += DefeatsAmount; - - Character = GetCharacter(); + Character = Character == nullptr ? Cast(GetPawn()) : Character; if (Character) { - Controller = GetController(); + Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; if (Controller) { Controller->SetHUDDefeats(Defeats); @@ -61,29 +59,13 @@ void ABlasterPlayerState::IncreaseDefeats(int32 DefeatsAmount) void ABlasterPlayerState::OnRep_Defeats() { - Character = GetCharacter(); + Character = Character == nullptr ? Cast(GetPawn()) : Character; if (Character) { - Controller = GetController(); + Controller = Controller == nullptr ? Cast(Character->Controller) : Controller; if (Controller) { Controller->SetHUDDefeats(Defeats); } } } - -ABlasterCharacter* ABlasterPlayerState::GetCharacter() const -{ - return Character == nullptr ? Cast(GetPawn()) : Character; -} - -ABlasterPlayerController* ABlasterPlayerState::GetController() const -{ - if (Character) - { - return Controller == nullptr ? Cast(Character->Controller) : Controller; - } - - return nullptr; -} - diff --git a/Source/Blaster/PlayerState/BlasterPlayerState.h b/Source/Blaster/PlayerState/BlasterPlayerState.h index 20b3fa6..10b74e0 100644 --- a/Source/Blaster/PlayerState/BlasterPlayerState.h +++ b/Source/Blaster/PlayerState/BlasterPlayerState.h @@ -28,8 +28,6 @@ private: class ABlasterCharacter* Character; UPROPERTY() class ABlasterPlayerController* Controller; - ABlasterCharacter* GetCharacter() const; - ABlasterPlayerController* GetController() const; UPROPERTY(ReplicatedUsing = OnRep_Defeats) int32 Defeats; -- 2.40.1 From 5775df5ec1f202184b8e0a0c8ba88f3adbf2e1cb Mon Sep 17 00:00:00 2001 From: Kingsmedia Date: Thu, 12 May 2022 17:05:30 +0200 Subject: [PATCH 2/3] Added debug widget --- Content/Blueprints/HUD/BP_BlasterHUD.uasset | Bin 22842 -> 22987 bytes Content/Blueprints/HUD/WBP_DebugWidget.uasset | Bin 0 -> 37767 bytes Source/Blaster/HUD/BlasterHUD.cpp | 12 ++++ Source/Blaster/HUD/BlasterHUD.h | 7 ++ Source/Blaster/HUD/DebugWidget.cpp | 5 ++ Source/Blaster/HUD/DebugWidget.h | 41 ++++++++++++ .../BlasterPlayerController.cpp | 63 ++++++++++++++++++ .../BlasterPlayerController.h | 12 +++- 8 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 Content/Blueprints/HUD/WBP_DebugWidget.uasset create mode 100644 Source/Blaster/HUD/DebugWidget.cpp create mode 100644 Source/Blaster/HUD/DebugWidget.h diff --git a/Content/Blueprints/HUD/BP_BlasterHUD.uasset b/Content/Blueprints/HUD/BP_BlasterHUD.uasset index a137d6554342d4dbeff2c3e15d910f1a8edef480..92481c8d3966a138254b3f5b49d1156c5cfe934b 100644 GIT binary patch delta 2800 zcma)84NwzD6y8e$5(t(puoN@r@B&h*EYf}*Ijl(y5c^zG%KAq`VEv-fu2_r34E zz1!Q%rSsG+2UXWoU86_H3n7$gFDU@SLsfA7g+eF{tTv9;#mAa78lA=*r#8h!tBty7 zO|r(QQ>)Zwtp))PRr@0p09GYNNC`F!CJH%XurpvULhpj!ckr*{uU@^CajvK4GhuE0 zkC%0qjw}g$ra8I(e)ImRov{r>j0jcX3hD*rTm?d(E`_N}8-Z4%$30Z4V?M{1qYwdR zDp;{;O?J_$H3j8#aei4DYOfincck%rys1b$$PX5}5Kv6}!H*m^-w!!TC>CWKZMiOJ zE{*iK6C(BrkqsV^`=GXQLZp-+ zBKO+*v423&e=6<=rBxo42vRU1QcMv3SPtwj4DqO4F`JQ;`Qix_-tJ+8|6Ol^5=P1{)e7^>dl5qSj2 zR*%R-Kq~PrQMhb1tKtXN%ymv+XLp%rVVgMkuvkc~!6LDQ>csWp+2EQZTgyhMz>e<9 z1IbvG7K4vVB)D5Fl$Ed)AFvnM@xyTxyhswrIca30!YMnxCXsM#^r%B-hcFIwB9X*% zY8*@%2a{?EY+>Qg7mO7#6eyH!0EoQFgi=Qa(Bumlid7m;mE(<43FowpS?XEo9N9W2 z1x10K&W<%XW0E?VP_`M|qmlxa^5VagW}sBKP9TThynkgj*9$6(k zj*&j-z0mLCi$N#6TH&CQW4l6$%j8n;>oBX-J8I<{d<x3tk$= zYUo5>%gDvUU6_WuLL(h{vr9P!6$FyJY6wv?bW&FeNYW5uVyaR>Dl_0ls4fAn@$e(E zS`_aP&D+J*tH7B6>S_oib&%3k5Uhd#@-s2@kRT(BVqorE99aeJ=v4Xo`H(|@q^*sM z?<7W2zW7jz9Ea$mC@vnz3HB$Oggjc(oE~!X$-zw;7dPk=DXybizn8*EVgbH1KN24@ zB=9FWaU{TP7Ac-)nyXkBa60NxV|!{-O_##IFTW>|Wgr}?55*Zt8fr4$nY5M?<3XdG z>cfw+gT2Ue<&Zh=E{t$ivI?r&lRT5pZe5ViHyVYi2fsbKG30A)uqp3l?igxdiKKi! zWEn>mebedv$G|ymTI2iS*E4GSHYVc7=2ASJ7V_ac$TR^q&Ty+)N(%R>+QlfOuHrLf8Ik4&r8JlVo4O?%*g3(O*qhNYV-TV-n6rDxhG!|&NC}; zKw22SZ7PWPdrPl(rLs5u?9txV_>rbJNrb!4k!zMvF12n;$$r{et_>KsWB=d;a#q87ecV{t{p%IWYyEkl9W~y23EABj%&fD^x||X|40}6z z@zv0@_x27iYnZWn4mK>5M{t`@a_n?_=E-BjvdEi#f0E7Y?qd>8%@lBb5yA$Wmh!{e zEF(7A)G(^B&8PUd(RW2!{X8rFUOY%REGy!hOj-mp|JM1+C@352^k*}r@3v)_@ jgWp<5`cHY8+v*a{3vY_7xHc!2Ie9|)?6-$Gb*%pY^J%jW delta 2840 zcma)82}~4c6#i#9WI5`<9xO;ybg@E(?k+6s;=(So7scgLQ7BOhNEPMK1-z=ZrbcZ& zO8rf2JZjOV*s5vEf?6-cCPrIq)v9T0A~mt5cw%EE)+l{%c0gR%)VG=W-+bTq-v8b| z^Uv&$F6Kxx({SV6E(0Ncgpiz?>T(!-$ucWagg;xHK|T1FQ$QzAD&NOBQe4zOOZE(5*6*d;*#XOz{|I2|RW z<&~r<+&RnjnkZ1@%JB{GcfB8YOhk)9&d7>|=u%<42N~wl#yp8^hJef&z?2Tj+=ZA* zuS~U9rUn^t|2*`ES4It*^$z4-IAn&~z?67p%DghOy)yH?GId^=MP3=lkjyQpy8Z>m zDXcFJMacB8h?9QBK3|c6x?&IAzg~1RJak#1L+5*ouV|J~$x>nLhFTX7$=nBK2{Pk) zz!T*BfO`ZDA3v)P?-F<-KefiuyJ1muXBaDg+l>-2jK>5&!9>*(luVFU2#_AeTEV|% zqV1U)dQPI0R|!x6jIDw{BSWBdqA*Tyr8Ha0%Bw+v+XmGJp`5nC5|JP=`GN!X#5yPo z24fb7AeorOOIFB~@*05qCTTziY;8_|hNm)C-q#3Dv|$*pAmMrZviuPi0F6m$g`Abo zb2B6s#&LqbXL^68%OI%-xNovxL2}kDi4RxG7Y(3q3iRdY84iRiMMn$!6GkY;hhjfi z7Tn0*zu@Si(-gb>6!3OYP&P7>N+V?cO1S#X;Cek`L4YxdXb4Ai@Z(4_NhMmMCn<1F zhQmT^#7dHgnIsZBoXx=N0oQ?N1BHgzT-TzHip_~2(*T?ZN%Y`pVdRJ!P%Q+ef;a_2 zEPx=d2RsGRB5vl>Fk}p6mGLg|gn1&f4oDjaHGDdEEq1;>J)k;BsDo4~@WW~_ksXp? zl~(wfVL8V`vKZIZghHP*oHY>wZ2Y`hsK6bdg8)pS=PO8sGshoTy&9@E1EX^-&;$mB z!eWu$wod*w;Vt0kd6UBL8LVv(9cfq4lWDPxFI`y_6@qJz_ZrQO;HM^xzedE6ZeV;} zOVd{~!EAZ!D^a#|_;d?bk1LY+*uLhuN3w z;alc5+MYI=-Z3RHERD{X$4K}J@$UPer|tqzc#uhs5F+i1Wx?U{W^t7zwRPpr%mPci zn$WGB8hVm)^o%WpvGq3LrtTzfmY##Uj3*ykasY}YNyHjmP zy0$&LP}-|9&{Z}kJ!EFZ_}N1Xq36x1WBuJ1gW6_@u(d!JZ*j3j%bL_bO`VAy=bkMPdo8!K*; zoog#dpR{pQ)5mXgbZD_zPpq%F0ffU^KU2b?|5$N{!lL-jN^@p1?7EewCMNfknf7gOox64tyO*BNRVdXG(T68W zM(4I3-!+xCooT@**Av4D>DM{IK7oYL#{5V|L=*D0OfbElm(KXpd-)1_DL@EGZ7i&254AL0&s9A%?{0i#4@x{WmX0dY@-L~; MV*EIu;R`DJKWX{4@c;k- diff --git a/Content/Blueprints/HUD/WBP_DebugWidget.uasset b/Content/Blueprints/HUD/WBP_DebugWidget.uasset new file mode 100644 index 0000000000000000000000000000000000000000..92017fb5cf2d3498b2eed3cd9395ed5fcfab788f GIT binary patch literal 37767 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, DebugWidgetClass); + DebugWidget->AddToViewport(); + } +} + void ABlasterHUD::AddCharacterOverlay() { APlayerController* PlayerController = GetOwningPlayerController(); diff --git a/Source/Blaster/HUD/BlasterHUD.h b/Source/Blaster/HUD/BlasterHUD.h index 7688097..b689d03 100644 --- a/Source/Blaster/HUD/BlasterHUD.h +++ b/Source/Blaster/HUD/BlasterHUD.h @@ -31,6 +31,7 @@ class BLASTER_API ABlasterHUD : public AHUD public: virtual void DrawHUD() override; + void AddDebugWidget(); void AddCharacterOverlay(); void AddAnnouncementOverlay(); @@ -40,6 +41,12 @@ public: UPROPERTY() class UCharacterOverlay* CharacterOverlay; + UPROPERTY() + class UDebugWidget* DebugWidget; + + UPROPERTY(EditAnywhere, Category = "Widget") + TSubclassOf DebugWidgetClass; + UPROPERTY(EditAnywhere, Category = "Announcements") TSubclassOf AnnouncementClass; diff --git a/Source/Blaster/HUD/DebugWidget.cpp b/Source/Blaster/HUD/DebugWidget.cpp new file mode 100644 index 0000000..54440c9 --- /dev/null +++ b/Source/Blaster/HUD/DebugWidget.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DebugWidget.h" + diff --git a/Source/Blaster/HUD/DebugWidget.h b/Source/Blaster/HUD/DebugWidget.h new file mode 100644 index 0000000..c556c31 --- /dev/null +++ b/Source/Blaster/HUD/DebugWidget.h @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "DebugWidget.generated.h" + +/** + * + */ +UCLASS() +class BLASTER_API UDebugWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + + UPROPERTY(meta = (BindWidget)) + class UTextBlock* DebugMsg1; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* DebugMsg2; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* DebugMsg3; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* DebugMsg4; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* DebugMsg5; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* DebugMsg6; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* DebugMsg7; + + +}; \ No newline at end of file diff --git a/Source/Blaster/PlayerController/BlasterPlayerController.cpp b/Source/Blaster/PlayerController/BlasterPlayerController.cpp index 859cb0a..f222fd0 100644 --- a/Source/Blaster/PlayerController/BlasterPlayerController.cpp +++ b/Source/Blaster/PlayerController/BlasterPlayerController.cpp @@ -9,6 +9,7 @@ #include "Blaster/HUD/Announcement.h" #include "Blaster/HUD/BlasterHUD.h" #include "Blaster/HUD/CharacterOverlay.h" +#include "Blaster/HUD/DebugWidget.h" #include "Components/ProgressBar.h" #include "Components/TextBlock.h" #include "GameFramework/GameMode.h" @@ -38,6 +39,59 @@ void ABlasterPlayerController::Tick(float DeltaTime) SetHUDTime(); CheckTimeSync(DeltaTime); PollInit(); + + if (DebugWidget) + { + SetDebugMsg1(TEXT("GetServerTime(): "), FString::Printf(TEXT("%02f"), GetServerTime())); + SetDebugMsg2(TEXT("ClientServerDelta: "), FString::Printf(TEXT("%f"), ClientServerDelta)); + SetDebugMsg3(TEXT("LevelStartingTime: "), FString::Printf(TEXT("%f"), LevelStartingTime)); + SetDebugMsg4(TEXT("WarmupTime: "), FString::Printf(TEXT("%f"), WarmupTime)); + SetDebugMsg5(TEXT("MatchTime: "), FString::Printf(TEXT("%f"), MatchTime)); + SetDebugMsg6(TEXT("CooldownTime: "), FString::Printf(TEXT("%f"), CooldownTime)); + SetDebugMsg7(TEXT("CountdownInt: "), FString::Printf(TEXT("%d"), CountdownInt)); + } +} + +void ABlasterPlayerController::SetDebugMsg1(FString Key, FString Value) +{ + bool bHUDValid = BlasterHUD && BlasterHUD->DebugWidget && BlasterHUD->DebugWidget->DebugMsg1; + if (bHUDValid) BlasterHUD->DebugWidget->DebugMsg1->SetText(FText::FromString(Key.Append(Value))); +} + +void ABlasterPlayerController::SetDebugMsg2(FString Key, FString Value) +{ + bool bHUDValid = BlasterHUD && BlasterHUD->DebugWidget && BlasterHUD->DebugWidget->DebugMsg2; + if (bHUDValid) BlasterHUD->DebugWidget->DebugMsg2->SetText(FText::FromString(Key.Append(Value))); +} + +void ABlasterPlayerController::SetDebugMsg3(FString Key, FString Value) +{ + bool bHUDValid = BlasterHUD && BlasterHUD->DebugWidget && BlasterHUD->DebugWidget->DebugMsg3; + if (bHUDValid) BlasterHUD->DebugWidget->DebugMsg3->SetText(FText::FromString(Key.Append(Value))); +} + +void ABlasterPlayerController::SetDebugMsg4(FString Key, FString Value) +{ + bool bHUDValid = BlasterHUD && BlasterHUD->DebugWidget && BlasterHUD->DebugWidget->DebugMsg4; + if (bHUDValid) BlasterHUD->DebugWidget->DebugMsg4->SetText(FText::FromString(Key.Append(Value))); +} + +void ABlasterPlayerController::SetDebugMsg5(FString Key, FString Value) +{ + bool bHUDValid = BlasterHUD && BlasterHUD->DebugWidget && BlasterHUD->DebugWidget->DebugMsg5; + if (bHUDValid) BlasterHUD->DebugWidget->DebugMsg5->SetText(FText::FromString(Key.Append(Value))); +} + +void ABlasterPlayerController::SetDebugMsg6(FString Key, FString Value) +{ + bool bHUDValid = BlasterHUD && BlasterHUD->DebugWidget && BlasterHUD->DebugWidget->DebugMsg6; + if (bHUDValid) BlasterHUD->DebugWidget->DebugMsg6->SetText(FText::FromString(Key.Append(Value))); +} + +void ABlasterPlayerController::SetDebugMsg7(FString Key, FString Value) +{ + bool bHUDValid = BlasterHUD && BlasterHUD->DebugWidget && BlasterHUD->DebugWidget->DebugMsg7; + if (bHUDValid) BlasterHUD->DebugWidget->DebugMsg7->SetText(FText::FromString(Key.Append(Value))); } void ABlasterPlayerController::CheckTimeSync(float DeltaTime) @@ -74,6 +128,7 @@ void ABlasterPlayerController::ClientJoinMidgame_Implementation(FName StateOfMat OnMatchStateSet(MatchState); if (BlasterHUD && MatchState == MatchState::WaitingToStart) { + BlasterHUD->AddDebugWidget(); BlasterHUD->AddAnnouncementOverlay(); } } @@ -252,6 +307,14 @@ void ABlasterPlayerController::SetHUDTime() void ABlasterPlayerController::PollInit() { + if (DebugWidget == nullptr) + { + if (BlasterHUD && BlasterHUD->DebugWidget) + { + DebugWidget = BlasterHUD->DebugWidget; + } + } + if (CharacterOverlay == nullptr) { if (BlasterHUD && BlasterHUD->CharacterOverlay) diff --git a/Source/Blaster/PlayerController/BlasterPlayerController.h b/Source/Blaster/PlayerController/BlasterPlayerController.h index a995292..daa9e22 100644 --- a/Source/Blaster/PlayerController/BlasterPlayerController.h +++ b/Source/Blaster/PlayerController/BlasterPlayerController.h @@ -20,7 +20,14 @@ public: virtual void OnPossess(APawn* InPawn) override; virtual void ReceivedPlayer() override; // Sync with server clock as soon as possible virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; - + + void SetDebugMsg1(FString Key, FString Value); + void SetDebugMsg2(FString Key, FString Value); + void SetDebugMsg3(FString Key, FString Value); + void SetDebugMsg4(FString Key, FString Value); + void SetDebugMsg5(FString Key, FString Value); + void SetDebugMsg6(FString Key, FString Value); + void SetDebugMsg7(FString Key, FString Value); void SetHUDHealth(float Health, float MaxHealth); void SetHUDScore(float Score); void SetHUDDefeats(int32 Defeats); @@ -68,6 +75,9 @@ protected: void ClientJoinMidgame(FName StateOfMatch, float Warmup, float Match, float Cooldown, float StartingTime); private: + UPROPERTY() + class UDebugWidget* DebugWidget; + UPROPERTY() class ABlasterHUD* BlasterHUD; -- 2.40.1 From 884caf99746a4cd70d5ad08cedfc1be36dbb3d18 Mon Sep 17 00:00:00 2001 From: Kingsmedia Date: Mon, 16 May 2022 23:51:15 +0200 Subject: [PATCH 3/3] Added EOS and some time sync adjustments --- .gitignore | 5 +- Blaster.uproject | 8 ++ Config/DefaultEngine.ini | 27 ++++- .../GameModes/BP_BlasterGameMode.uasset | Bin 20388 -> 20338 bytes Content/Maps/BlasterMap.umap | Bin 1331237 -> 1331237 bytes .../Content/WBP_Menu.uasset | Bin 41224 -> 43905 bytes .../MultiplayerSessions.uplugin | 4 + .../MultiplayerSessions.Build.cs | 1 + .../MultiplayerSessions/Private/Menu.cpp | 38 +++++- .../Private/MultiplayerSessionsSubsystem.cpp | 112 ++++++++++++++---- .../Source/MultiplayerSessions/Public/Menu.h | 11 +- .../Public/MultiplayerSessionsSubsystem.h | 22 +++- Source/Blaster/GameMode/BlasterGameMode.cpp | 6 +- .../BlasterPlayerController.cpp | 36 +++--- 14 files changed, 220 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index 917aee4..a7ed43d 100644 --- a/.gitignore +++ b/.gitignore @@ -75,4 +75,7 @@ Intermediate/* Plugins/*/Intermediate/* # Cache files for the editor to use -DerivedDataCache/* \ No newline at end of file +DerivedDataCache/* + +#Config files +Config/User*.ini \ No newline at end of file diff --git a/Blaster.uproject b/Blaster.uproject index 673f04d..a1b61fd 100644 --- a/Blaster.uproject +++ b/Blaster.uproject @@ -22,6 +22,14 @@ "Editor" ] }, + { + "Name": "OnlineSubsystemSteam", + "Enabled": true + }, + { + "Name": "OnlineSubsystemEOS", + "Enabled": true + }, { "Name": "Bridge", "Enabled": true, diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index d7bf3e8..6e1fcf4 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -45,19 +45,25 @@ bUseManualIPAddress=False ManualIPAddress= [/Script/Engine.GameEngine] -+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver") ++NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemEOS.NetDriverEOS",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver") [OnlineSubsystem] -DefaultPlatformService=Steam +DefaultPlatformService=EOS [OnlineSubsystemSteam] -bEnabled=true +bEnabled=false SteamDevAppId=480 bInitServerOnClient=true [/Script/OnlineSubsystemSteam.SteamNetDriver] NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection" +[OnlineSubsystemEOS] +bEnabled=true + +[/Script/OnlineSubsystemEOS.NetDriverEOS] +bIsUsingP2PSockets=true + [/Script/OnlineSubsystemUtils.IpNetDriver] NetServerMaxTickRate=120 @@ -118,3 +124,18 @@ NetServerMaxTickRate=120 +CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") +CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") +[/Script/OnlineSubsystemEOS.EOSSettings] +CacheDir=CacheDir +DefaultArtifactName=Blaster +TickBudgetInMilliseconds=0 +bEnableOverlay=True +bEnableSocialOverlay=True +bShouldEnforceBeingLaunchedByEGS=False +TitleStorageReadChunkLength=0 +bUseEAS=False +bUseEOSConnect=False +bMirrorStatsToEOS=False +bMirrorAchievementsToEOS=False +bUseEOSSessions=False +bMirrorPresenceToEAS=False + diff --git a/Content/Blueprints/GameModes/BP_BlasterGameMode.uasset b/Content/Blueprints/GameModes/BP_BlasterGameMode.uasset index 15b9ff2e66c2aa7ad19d81e89617e08a6e4e5702..55ad2cb0ea74020a47012244b46f23d508a80854 100644 GIT binary patch delta 3759 zcmcIlTTEPK5dKev$W{moyDYnuQZAL7b)hJ2mxTknz;a(osUU4dC}N?lyOjd9RkGR| zpBkl3YSL)hK4{~Mi47+DU}AjpO`nXhHEAREVo6(z)>tn%bI$zBf$lEbL?=0!fBx?~ zbNg@apHc3dR;FjuyIevf3L*T#z%Z%=F?HhZmhv6xLS&P?t`0ucb+_zwCl6UhwnnaVppc%kkY+&4&gv2@(r?pLV@~bt}3ooGnEP7n?n7J zKgaqSasoWAG5kBozArxjZZJ zUex`Gm3T*%^7Q%#cv7w%1%72IQL`fP+T-BOqQ93T^0@ymF-WQ}B2k|-@^;3_+ZBr^ z*Pl4g#3C|AN*>urtu`k3Y#jcy_ychei6ZZSl>+aM;BOw6fTuMzq8q8=5gar=q+4As zxa2K`FVZ(;jOsJOg!*$Hzw@@G*A`?0?=dmQ<83u?-s*#ORoc0SBI2Wb(ySGI$!~(Q77Ms-X4?TB5hbV#d3>P*25mhy+Qu?KGDC`A%jK3K z7kn@ToghiQ!qgOv3ZYE1jSh~4<>8pu;TxI9l^vj13gBIPNn!w>5Et{nL2UM> zG+B;J_Hil}-l{gYN@qlmxgF2%IvL##*Q;HNuf}b|o+IDMt6vwt;an*hDBN{z zY;;Yl`Ls4GL=o$7VM5&YJP{o{>#6rLDzggz`WMEoj|TpUV3`C#QLQGqLMr5_*?5fH zYDuXv%~r`~9ggz{x~hgZRobTKRHtJD6C$rbVy*c>-0Hi(x3)C4+sg7E?p#cn2>L(l zbA*O=midgCuV0pskL^vgB}|oUsW9DS&3}?DdDiF#)TEJh4XAZ_`RmxqXDw;DY>Pa2 ztS*0lvYB|DhqDHc&&pYKYhnxa=8*GmR^>0fv0V z${INCt1~rl)`hg_=$CHvw5CP}y(=VgQDn^#HitLr=8#XR+;y>6`ky8$EVbFa!Wv3% zlpbv``sfm|!9ufLX#;zUsyLyy<& zLt~={#>3qHj-yH)T<-9NUj>S5tjaI<*3JL` delta 3907 zcmb_eZA@EL7(RCcvnsG!`U%XHF<3!lY{;ZgT1qJl7%dY7Wjes})s3~tn7HXy=CZ{f zCPGePOjKN=zcfY-i$Byv{o@~X+2XPo6SKtNri>+?|IMj zocHsbbNOXndg~2ocF{26WGs_07KlcML1eHKbHCM8Mh%P=fVweU1 zRrqcG_{$%@JvZBNcE!PR&P6-;e#t7G;L}pqCdtOw*&y&$;~yC4sBht#qckZT*5h<2zlfpLSs^IDUei(fE|PNuqd?=A$H+fj36;biC`pJD8dm zW5VDP5XVy!72eOlJCuYsNNW+2n21P99bh6a#@IC=9@bzGc}Fzzp4Py-3ihVKzdTY$ z$QTlz(ZKr=ctaX_BgBiPbBqQ>d+x-W)adxxL|#1q#GWvQj*ll2Wi1)6Lj!LS>^+y1 zNA*8SAd0@E#Q36`_qayh6X|%O|H#>&jz}1aevP~V4LmtGQPyNVR%a~UfIIO*!Z8mT z@=amV?g8mvA6G_r%+PB-K!^_HCp3S@#djFX_}hkp;&rqn!~{*?U1K^au8i^v{wjXn zXyo&`dh--9Hh}zq<`Zsy(Z5rn^r$HB(!9_^l&xmG;Ak2xxG323i7mB=w2CPUY^Tb-BQTBo@brbRVBHSZ$k$D$pX*1CYI)e^Hxn zYhA_All~|l%yqciNOO=>ion>6N5s3@34Gt>5qC0Ew=unwc1=#Y$fzi*5((bBasl#u z*RFd-vLclVECoO2(zl6-k)F1NYT_SsM6s~KyNzFRIVCsGskg`OC+AdnSyS)F_B}?N zBz2tdKA0N3={2PWC%irY<#$@drBvlF2)c>3t^xl9urd|60wCN4d3gA8eSs9@I(KQK zI;D1jlT_UP4x8&~Q5;)wjXiwIU158G(4k{y z79MP<)76q1TODahi^9vjdN5w^t&)jJ@lN9>dTR2>FbKx**52Oo zjb)R(3jYRxY#bg9M>@v`kFpw`*Ip`h^QQLW2Ejlx-Qmzd1zc%gEEJ#-KwmU+82p5$ z!qJGmD>NCNm{Pdq9nVW%{$5A(K6N(gc`gOA<|f{RcB6S5Yd{QJFGGy&<(+|QTQ6y% zrvi{{q2y B=U4y$ diff --git a/Content/Maps/BlasterMap.umap b/Content/Maps/BlasterMap.umap index fe90ccdf07514656414e98b2db847d68bbec2f0d..bee711448e53a65b87881534e416c65415d20b1c 100644 GIT binary patch delta 254 zcmZ3wAaLn|zzy#h1wwj1N@Z=`=edDB;IQ~jMXk;M7!S5GGBh7(-+rK-(chfS#K2I` z&|fm_`-m?z!a z>!f?9ef|kXAZ7w$W*}w(Vpbq#17da{<^W<&Am##MZXo8_KK}%7UCQ+Lvl%%U&9e?&q l>5YCY64M)W`Hb4n8S()!KM)H5u^XWZupH7!S5Gax@=k-+rK-(chfS(#TZL z&|tdgenuxY19MY76Z7e@`x)iA4a{{7fjmQ?!1nh2j2xT-f~Mt(=_SX!wr%re^jq`z zRr~xCj6lo;#LPg<0>rF9%m&2lK+FNeoIuP4#N0s4vwi*v-ntY{GX@3*2OxIbuBOWu z!X)UirQTq1de(tME_IpD+BzL3PiOqZD7Ag5KHqspfekTC*?#pF4r~4Hl{uS6rcZD5 jW09ENpvz~}e$J2&i1~q70Eh*FSO|!Px1Tc<36}!^07ql{ diff --git a/Plugins/MultiplayerSessions/Content/WBP_Menu.uasset b/Plugins/MultiplayerSessions/Content/WBP_Menu.uasset index 22c4fa63cf0ef302df6aa75d9085dd7afe43025e..f5888d9824f72de3df2909fb0b980330c8e1d1f1 100644 GIT binary patch literal 43905 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 Q16GQOZ|1n|tWnqhFQ_p*#Q*>R delta 10464 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 diff --git a/Plugins/MultiplayerSessions/MultiplayerSessions.uplugin b/Plugins/MultiplayerSessions/MultiplayerSessions.uplugin index 6c4eb6f..a2ea13f 100644 --- a/Plugins/MultiplayerSessions/MultiplayerSessions.uplugin +++ b/Plugins/MultiplayerSessions/MultiplayerSessions.uplugin @@ -29,6 +29,10 @@ { "Name": "OnlineSubsystemSteam", "Enabled": true + }, + { + "Name": "OnlineSubsystemEOS", + "Enabled": true } ] } \ No newline at end of file diff --git a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/MultiplayerSessions.Build.cs b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/MultiplayerSessions.Build.cs index 2a4fa9f..a8c1dc3 100644 --- a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/MultiplayerSessions.Build.cs +++ b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/MultiplayerSessions.Build.cs @@ -28,6 +28,7 @@ public class MultiplayerSessions : ModuleRules "Core", "OnlineSubsystem", "OnlineSubsystemSteam", + "OnlineSubsystemEOS", "UMG", "Slate", "SlateCore" diff --git a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/Menu.cpp b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/Menu.cpp index bef43c3..e24016f 100644 --- a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/Menu.cpp +++ b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/Menu.cpp @@ -32,7 +32,7 @@ void UMenu::MenuSetup(int32 NumberOfPublicConnections, FString TypeOfMatch, FStr } UGameInstance* GameInstance = GetGameInstance(); - if (GetGameInstance()) + if (GameInstance) { MultiplayerSessionsSubsystem = GameInstance->GetSubsystem(); } @@ -44,6 +44,20 @@ void UMenu::MenuSetup(int32 NumberOfPublicConnections, FString TypeOfMatch, FStr MultiplayerSessionsSubsystem->MultiplayerOnJoinSessionComplete.AddUObject(this, &ThisClass::OnJoinSession); MultiplayerSessionsSubsystem->MultiplayerOnDestroySessionComplete.AddDynamic(this, &ThisClass::OnDestroySession); MultiplayerSessionsSubsystem->MultiplayerOnStartSessionComplete.AddDynamic(this, &ThisClass::OnStartSession); + MultiplayerSessionsSubsystem->MultiplayerOnLoginComplete.AddDynamic(this, &ThisClass::OnLoginComplete); + + if (!MultiplayerSessionsSubsystem->IsLoggedIn()) + { + LoginButton->SetIsEnabled(true); + HostButton->SetIsEnabled(false); + JoinButton->SetIsEnabled(false); + } + else + { + LoginButton->SetIsEnabled(false); + HostButton->SetIsEnabled(true); + JoinButton->SetIsEnabled(true); + } } } @@ -64,6 +78,11 @@ bool UMenu::Initialize() JoinButton->OnClicked.AddDynamic(this, &UMenu::JoinButtonClicked); } + if (LoginButton) + { + LoginButton->OnClicked.AddDynamic(this, &UMenu::LoginButtonClicked); + } + return true; } @@ -164,6 +183,13 @@ void UMenu::OnStartSession(bool bWasSuccessful) { } +void UMenu::OnLoginComplete(bool bWasSuccessful) +{ + JoinButton->SetIsEnabled(!bWasSuccessful); + HostButton->SetIsEnabled(bWasSuccessful); + JoinButton->SetIsEnabled(bWasSuccessful); +} + void UMenu::HostButtonClicked() { HostButton->SetIsEnabled(false); @@ -184,6 +210,16 @@ void UMenu::JoinButtonClicked() } } +void UMenu::LoginButtonClicked() +{ + LoginButton->SetIsEnabled(false); + + if (MultiplayerSessionsSubsystem) + { + MultiplayerSessionsSubsystem->Login(); + } +} + void UMenu::MenuTearDown() { RemoveFromParent(); diff --git a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp index 0dfd851..66cf9fd 100644 --- a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp +++ b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Private/MultiplayerSessionsSubsystem.cpp @@ -13,15 +13,74 @@ UMultiplayerSessionsSubsystem::UMultiplayerSessionsSubsystem(): DestroySessionCompleteDelegate(FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnDestroySessionComplete)), StartSessionCompleteDelegate(FOnStartSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnStartSessionComplete)) { - IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(); + Subsystem = IOnlineSubsystem::Get(); if (Subsystem) { + Identity = Subsystem->GetIdentityInterface(); SessionInterface = Subsystem->GetSessionInterface(); + if (GetSubsystemName() != EOS_SUBSYSTEM) // Non EOS OSS don't need to login + { + bIsLoggedIn = true; + } } } +FName UMultiplayerSessionsSubsystem::GetSubsystemName() +{ + return Subsystem ? Subsystem->GetSubsystemName() : FName("NULL"); +} + +void UMultiplayerSessionsSubsystem::Login() +{ + if (IsLoggedIn()) + { + MultiplayerOnLoginComplete.Broadcast(true); + return; + } + + EOSLogin(); +} + +void UMultiplayerSessionsSubsystem::EOSLogin() +{ + if (!Identity.IsValid()) return; + + LoginDelegateHandle = Identity->OnLoginCompleteDelegates->AddUObject(this, &UMultiplayerSessionsSubsystem::HandleLoginComplete); + + FOnlineAccountCredentials Credentials; + Credentials.Id = FString(); + Credentials.Token = FString(); + Credentials.Type = FString("accountportal"); + + if (!Identity->Login(0, Credentials)) + { + UE_LOG(LogTemp, Error, TEXT("Unable to perform EOS Login")); + } + else + { + UE_LOG(LogTemp, Display, TEXT("Performed EOS Login successful")); + } +} + +void UMultiplayerSessionsSubsystem::HandleLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error) +{ + bIsLoggedIn = bWasSuccessful; + + if (Identity) + { + // Deregister the event handler. + Identity->ClearOnLoginCompleteDelegate_Handle(LocalUserNum, LoginDelegateHandle); + LoginDelegateHandle.Reset(); + } + + // Broadcast our own custom delegate + MultiplayerOnLoginComplete.Broadcast(bWasSuccessful); +} + void UMultiplayerSessionsSubsystem::CreateSession(int32 NumPublicConnections, FString MatchType) { + if (!IsLoggedIn()) return; + DesiredNumPublicConnections = NumPublicConnections; DesiredMatchType = MatchType; if (!SessionInterface.IsValid()) @@ -43,12 +102,14 @@ void UMultiplayerSessionsSubsystem::CreateSession(int32 NumPublicConnections, FS CreateSessionCompleteDelegateHandle = SessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegate); LastSessionSettings = MakeShareable(new FOnlineSessionSettings()); - LastSessionSettings->bIsLANMatch = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false; + LastSessionSettings->bIsDedicated = false; + LastSessionSettings->bIsLANMatch = GetSubsystemName() == "NULL" || GetSubsystemName() == "LAN" ? true : false; LastSessionSettings->NumPublicConnections = NumPublicConnections; LastSessionSettings->bAllowJoinInProgress = true; LastSessionSettings->bAllowJoinViaPresence = true; LastSessionSettings->bShouldAdvertise = true; LastSessionSettings->bUsesPresence = true; + LastSessionSettings->Set(SEARCH_KEYWORDS, FString("BlasterLobby"), EOnlineDataAdvertisementType::ViaOnlineService); LastSessionSettings->Set(FName("MatchType"), MatchType, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing); LastSessionSettings->BuildUniqueId = 1; LastSessionSettings->bUseLobbiesIfAvailable = true; @@ -65,6 +126,11 @@ void UMultiplayerSessionsSubsystem::CreateSession(int32 NumPublicConnections, FS void UMultiplayerSessionsSubsystem::FindSessions(int32 MaxSearchResults) { + if (!IsLoggedIn()) + { + return; + } + if (!SessionInterface.IsValid()) { return; @@ -75,6 +141,8 @@ void UMultiplayerSessionsSubsystem::FindSessions(int32 MaxSearchResults) LastSessionSearch = MakeShareable(new FOnlineSessionSearch()); LastSessionSearch->MaxSearchResults = MaxSearchResults; LastSessionSearch->bIsLanQuery = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false; + LastSessionSearch->QuerySettings.Set(SEARCH_KEYWORDS, FString("BlasterLobby"), EOnlineComparisonOp::Equals); + LastSessionSearch->QuerySettings.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals); LastSessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals); const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); @@ -88,6 +156,8 @@ void UMultiplayerSessionsSubsystem::FindSessions(int32 MaxSearchResults) void UMultiplayerSessionsSubsystem::JoinSession(const FOnlineSessionSearchResult& SessionResult) { + if (!IsLoggedIn()) return; + if (!SessionInterface.IsValid()) { MultiplayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError); @@ -124,19 +194,19 @@ void UMultiplayerSessionsSubsystem::DestroySession() void UMultiplayerSessionsSubsystem::StartSession() { - // if (!SessionInterface.IsValid()) - // { - // return; - // } - // - // StartSessionCompleteDelegateHandle = SessionInterface->AddOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegate); - // - // if (!SessionInterface->StartSession(NAME_GameSession)) - // { - // SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); - // - // MultiplayerOnStartSessionComplete.Broadcast(false); - // } + if (!SessionInterface.IsValid()) + { + return; + } + + StartSessionCompleteDelegateHandle = SessionInterface->AddOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegate); + + if (!SessionInterface->StartSession(NAME_GameSession)) + { + SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); + + MultiplayerOnStartSessionComplete.Broadcast(false); + } } void UMultiplayerSessionsSubsystem::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful) @@ -191,10 +261,10 @@ void UMultiplayerSessionsSubsystem::OnDestroySessionComplete(FName SessionName, void UMultiplayerSessionsSubsystem::OnStartSessionComplete(FName SessionName, bool bWasSuccessful) { - // if (SessionInterface) - // { - // SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); - // } - // - // MultiplayerOnStartSessionComplete.Broadcast(bWasSuccessful); + if (SessionInterface) + { + SessionInterface->ClearOnStartSessionCompleteDelegate_Handle(StartSessionCompleteDelegateHandle); + } + + MultiplayerOnStartSessionComplete.Broadcast(bWasSuccessful); } diff --git a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/Menu.h b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/Menu.h index 93359b7..2845556 100644 --- a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/Menu.h +++ b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/Menu.h @@ -18,7 +18,7 @@ class MULTIPLAYERSESSIONS_API UMenu : public UUserWidget public: UFUNCTION(BlueprintCallable) - void MenuSetup(int32 NumberOfPublicConnections = 4, FString TypeOfMatch = FString(TEXT("FreeForAll")), FString LobbyPath = FString(TEXT("/Game/ThirdPerson/Maps/Lobby"))); + void MenuSetup(int32 NumberOfPublicConnections = 4, FString TypeOfMatch = FString(TEXT("FreeForAll")), FString LobbyPath = FString(TEXT("/Game/Maps/Lobby"))); protected: @@ -36,6 +36,8 @@ protected: void OnDestroySession(bool bWasSuccessful); UFUNCTION() void OnStartSession(bool bWasSuccessful); + UFUNCTION() + void OnLoginComplete(bool bWasSuccessful); private: @@ -44,6 +46,9 @@ private: UPROPERTY(meta=(BindWidget)) UButton* JoinButton; + + UPROPERTY(meta=(BindWidget)) + UButton* LoginButton; UFUNCTION() void HostButtonClicked(); @@ -51,9 +56,13 @@ private: UFUNCTION() void JoinButtonClicked(); + UFUNCTION() + void LoginButtonClicked(); + void MenuTearDown(); // The subsystem designed to handle all online session functionality + UPROPERTY() class UMultiplayerSessionsSubsystem* MultiplayerSessionsSubsystem; int32 NumPublicConnections {4}; diff --git a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h index 11deff0..28eddff 100644 --- a/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h +++ b/Plugins/MultiplayerSessions/Source/MultiplayerSessions/Public/MultiplayerSessionsSubsystem.h @@ -4,18 +4,21 @@ #include "CoreMinimal.h" #include "OnlineSessionSettings.h" +#include "OnlineSubsystem.h" +#include "Interfaces/OnlineIdentityInterface.h" #include "Subsystems/GameInstanceSubsystem.h" #include "Interfaces/OnlineSessionInterface.h" #include "MultiplayerSessionsSubsystem.generated.h" // -// Delcaring our own custom delegates for the Menu class to bind callbacks to +// Declaring our own custom delegates for the Menu class to bind callbacks to // DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnCreateSessionComplete, bool, bWasSuccessful); DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiplayerOnFindSessionsComplete, const TArray& SessionResults, bool bWasSuccessful); DECLARE_MULTICAST_DELEGATE_OneParam(FMultiplayerOnJoinSessionComplete, EOnJoinSessionCompleteResult::Type Result) DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnDestroySessionComplete, bool, bWasSuccessful); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnStartSessionComplete, bool, bWasSuccessful); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiplayerOnLoginComplete, bool, bWasSuccessful); /** * @@ -28,7 +31,9 @@ class MULTIPLAYERSESSIONS_API UMultiplayerSessionsSubsystem : public UGameInstan public: UMultiplayerSessionsSubsystem(); - + void Login(); + FORCEINLINE bool IsLoggedIn() const { return bIsLoggedIn; } + /* * To handle session functionality. The Menu class will call these */ @@ -46,6 +51,7 @@ public: FMultiplayerOnJoinSessionComplete MultiplayerOnJoinSessionComplete; FMultiplayerOnDestroySessionComplete MultiplayerOnDestroySessionComplete; FMultiplayerOnStartSessionComplete MultiplayerOnStartSessionComplete; + FMultiplayerOnLoginComplete MultiplayerOnLoginComplete; int32 DesiredNumPublicConnections{}; FString DesiredMatchType{}; @@ -62,7 +68,9 @@ protected: void OnStartSessionComplete(FName SessionName, bool bWasSuccessful); private: - + IOnlineSubsystem* Subsystem; + IOnlineIdentityPtr Identity; + IOnlineSessionPtr SessionInterface; TSharedPtr LastSessionSettings; TSharedPtr LastSessionSearch; @@ -85,4 +93,12 @@ private: bool bCreateSessionOnDestroy { false }; int32 LastNumPublicConnections; FString LastMatchType; + + void EOSLogin(); + + FDelegateHandle LoginDelegateHandle; + void HandleLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error); + + FName GetSubsystemName(); + bool bIsLoggedIn = false; }; diff --git a/Source/Blaster/GameMode/BlasterGameMode.cpp b/Source/Blaster/GameMode/BlasterGameMode.cpp index 315b502..8660896 100644 --- a/Source/Blaster/GameMode/BlasterGameMode.cpp +++ b/Source/Blaster/GameMode/BlasterGameMode.cpp @@ -33,7 +33,7 @@ void ABlasterGameMode::Tick(float DeltaTime) if (MatchState == MatchState::WaitingToStart) { - CountdownTime = WarmupTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; + CountdownTime = WarmupTime - GetWorld()->GetTimeSeconds(); // + LevelStartingTime; if (CountdownTime <= 0.f) { StartMatch(); @@ -41,7 +41,7 @@ void ABlasterGameMode::Tick(float DeltaTime) } else if (MatchState == MatchState::InProgress) { - CountdownTime = WarmupTime + MatchTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; + CountdownTime = WarmupTime + MatchTime - GetWorld()->GetTimeSeconds(); // + LevelStartingTime; if (CountdownTime <= 0.f) { SetMatchState(MatchState::Cooldown); @@ -49,7 +49,7 @@ void ABlasterGameMode::Tick(float DeltaTime) } else if (MatchState == MatchState::Cooldown) { - CountdownTime = CooldownTime + WarmupTime + MatchTime - GetWorld()->GetTimeSeconds() + LevelStartingTime; + CountdownTime = WarmupTime + MatchTime + CooldownTime - GetWorld()->GetTimeSeconds(); // + LevelStartingTime; if (CountdownTime <= 0.f) { RestartGame(); diff --git a/Source/Blaster/PlayerController/BlasterPlayerController.cpp b/Source/Blaster/PlayerController/BlasterPlayerController.cpp index f222fd0..8e494ed 100644 --- a/Source/Blaster/PlayerController/BlasterPlayerController.cpp +++ b/Source/Blaster/PlayerController/BlasterPlayerController.cpp @@ -42,13 +42,11 @@ void ABlasterPlayerController::Tick(float DeltaTime) if (DebugWidget) { - SetDebugMsg1(TEXT("GetServerTime(): "), FString::Printf(TEXT("%02f"), GetServerTime())); - SetDebugMsg2(TEXT("ClientServerDelta: "), FString::Printf(TEXT("%f"), ClientServerDelta)); - SetDebugMsg3(TEXT("LevelStartingTime: "), FString::Printf(TEXT("%f"), LevelStartingTime)); - SetDebugMsg4(TEXT("WarmupTime: "), FString::Printf(TEXT("%f"), WarmupTime)); - SetDebugMsg5(TEXT("MatchTime: "), FString::Printf(TEXT("%f"), MatchTime)); - SetDebugMsg6(TEXT("CooldownTime: "), FString::Printf(TEXT("%f"), CooldownTime)); - SetDebugMsg7(TEXT("CountdownInt: "), FString::Printf(TEXT("%d"), CountdownInt)); + SetDebugMsg1(TEXT("GetWorld()->GetTimeSeconds(): "), FString::Printf(TEXT("%02f"), GetWorld()->GetTimeSeconds())); + SetDebugMsg2(TEXT("GetServerTime(): "), FString::Printf(TEXT("%02f"), GetServerTime())); + SetDebugMsg3(TEXT("ClientServerDelta: "), FString::Printf(TEXT("%f"), ClientServerDelta)); + SetDebugMsg4(TEXT("LevelStartingTime: "), FString::Printf(TEXT("%f"), LevelStartingTime)); + SetDebugMsg5(TEXT("CountdownInt: "), FString::Printf(TEXT("%d"), CountdownInt)); } } @@ -128,7 +126,6 @@ void ABlasterPlayerController::ClientJoinMidgame_Implementation(FName StateOfMat OnMatchStateSet(MatchState); if (BlasterHUD && MatchState == MatchState::WaitingToStart) { - BlasterHUD->AddDebugWidget(); BlasterHUD->AddAnnouncementOverlay(); } } @@ -275,9 +272,9 @@ void ABlasterPlayerController::SetHUDAnnouncementCountdown(float CountdownTime) void ABlasterPlayerController::SetHUDTime() { float TimeLeft = 0.f; - if (MatchState == MatchState::WaitingToStart) TimeLeft = WarmupTime - GetServerTime() + LevelStartingTime; - else if (MatchState == MatchState::InProgress) TimeLeft = WarmupTime + MatchTime - GetServerTime() + LevelStartingTime; - else if (MatchState == MatchState::Cooldown) TimeLeft = CooldownTime +WarmupTime + MatchTime - GetServerTime() + LevelStartingTime; + if (MatchState == MatchState::WaitingToStart) TimeLeft = WarmupTime - GetServerTime(); // + LevelStartingTime; + else if (MatchState == MatchState::InProgress) TimeLeft = WarmupTime + MatchTime - GetServerTime(); // + LevelStartingTime; + else if (MatchState == MatchState::Cooldown) TimeLeft = WarmupTime + MatchTime + CooldownTime - GetServerTime(); // + LevelStartingTime; uint32 SecondsLeft = FMath::CeilToInt(TimeLeft); @@ -286,7 +283,7 @@ void ABlasterPlayerController::SetHUDTime() BlasterGameMode = BlasterGameMode == nullptr ? Cast(UGameplayStatics::GetGameMode(this)) : BlasterGameMode; if (BlasterGameMode) { - SecondsLeft = FMath::CeilToInt(BlasterGameMode->GetCountdownTime() + LevelStartingTime); + SecondsLeft = FMath::CeilToInt(BlasterGameMode->GetCountdownTime()); } } @@ -307,14 +304,15 @@ void ABlasterPlayerController::SetHUDTime() void ABlasterPlayerController::PollInit() { - if (DebugWidget == nullptr) + if (BlasterHUD && BlasterHUD->DebugWidget == nullptr) { - if (BlasterHUD && BlasterHUD->DebugWidget) + BlasterHUD->AddDebugWidget(); + if (BlasterHUD->DebugWidget) { DebugWidget = BlasterHUD->DebugWidget; } } - + if (CharacterOverlay == nullptr) { if (BlasterHUD && BlasterHUD->CharacterOverlay) @@ -402,8 +400,12 @@ void ABlasterPlayerController::HandleCooldown() BlasterHUD = BlasterHUD == nullptr ? Cast(GetHUD()) : BlasterHUD; if (BlasterHUD) { - BlasterHUD->CharacterOverlay->RemoveFromParent(); - bool bHUDValid =BlasterHUD->Announcement && + if (BlasterHUD->CharacterOverlay) + { + BlasterHUD->CharacterOverlay->RemoveFromParent(); + } + + bool bHUDValid = BlasterHUD->Announcement && BlasterHUD->Announcement->AnnouncementText && BlasterHUD->Announcement->AnnouncementMessage; -- 2.40.1