RealtimeStyleTransferRuntime/Source/LyraGame/Weapons/LyraWeaponStateComponent.cpp

168 lines
5.6 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraWeaponStateComponent.h"
2022-09-13 07:18:28 +00:00
#include "Abilities/GameplayAbilityTargetTypes.h"
#include "Engine/HitResult.h"
#include "Engine/NetSerialization.h"
2022-05-23 18:41:30 +00:00
#include "Engine/World.h"
2022-09-13 07:18:28 +00:00
#include "Equipment/LyraEquipmentManagerComponent.h"
#include "GameFramework/Actor.h"
#include "GameFramework/Pawn.h"
2022-05-23 18:41:30 +00:00
#include "GameFramework/PlayerController.h"
2022-09-13 07:18:28 +00:00
#include "GameplayEffectTypes.h"
2022-05-23 18:41:30 +00:00
#include "Kismet/GameplayStatics.h"
#include "NativeGameplayTags.h"
2022-09-13 07:18:28 +00:00
#include "PhysicalMaterials/PhysicalMaterial.h"
2022-05-23 18:41:30 +00:00
#include "Physics/PhysicalMaterialWithTags.h"
2022-09-13 07:18:28 +00:00
#include "Teams/LyraTeamSubsystem.h"
#include "Templates/Casts.h"
#include "UObject/NameTypes.h"
#include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h"
#include "Weapons/LyraRangedWeaponInstance.h"
2022-05-23 18:41:30 +00:00
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Gameplay_Zone, "Gameplay.Zone");
ULyraWeaponStateComponent::ULyraWeaponStateComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SetIsReplicatedByDefault(true);
PrimaryComponentTick.bStartWithTickEnabled = true;
PrimaryComponentTick.bCanEverTick = true;
}
void ULyraWeaponStateComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (APawn* Pawn = GetPawn<APawn>())
{
if (ULyraEquipmentManagerComponent* EquipmentManager = Pawn->FindComponentByClass<ULyraEquipmentManagerComponent>())
{
if (ULyraRangedWeaponInstance* CurrentWeapon = Cast<ULyraRangedWeaponInstance>(EquipmentManager->GetFirstInstanceOfType(ULyraRangedWeaponInstance::StaticClass())))
{
CurrentWeapon->Tick(DeltaTime);
}
}
}
}
bool ULyraWeaponStateComponent::ShouldShowHitAsSuccess(const FHitResult& Hit) const
{
AActor* HitActor = Hit.GetActor();
//@TODO: Don't treat a hit that dealt no damage (due to invulnerability or similar) as a success
UWorld* World = GetWorld();
if (ULyraTeamSubsystem* TeamSubsystem = UWorld::GetSubsystem<ULyraTeamSubsystem>(GetWorld()))
{
return TeamSubsystem->CanCauseDamage(GetController<APlayerController>(), Hit.GetActor());
}
return false;
}
bool ULyraWeaponStateComponent::ShouldUpdateDamageInstigatedTime(const FGameplayEffectContextHandle& EffectContext) const
{
//@TODO: Implement me, for the purposes of this component we really only care about damage caused by a weapon
// or projectile fired from a weapon, and should filter to that
// (or perhaps see if the causer is also the source of our active reticle config)
return EffectContext.GetEffectCauser() != nullptr;
}
void ULyraWeaponStateComponent::ClientConfirmTargetData_Implementation(uint16 UniqueId, bool bSuccess, const TArray<uint8>& HitReplaces)
{
for (int i = 0; i < UnconfirmedServerSideHitMarkers.Num(); i++)
{
FLyraServerSideHitMarkerBatch& Batch = UnconfirmedServerSideHitMarkers[i];
if (Batch.UniqueId == UniqueId)
{
if (bSuccess && (HitReplaces.Num() != Batch.Markers.Num()))
{
UWorld* World = GetWorld();
bool bFoundShowAsSuccessHit = false;
int32 HitLocationIndex = 0;
for (const FLyraScreenSpaceHitLocation& Entry : Batch.Markers)
{
if (!HitReplaces.Contains(HitLocationIndex) && Entry.bShowAsSuccess)
{
// Only need to do this once
if (!bFoundShowAsSuccessHit)
{
ActuallyUpdateDamageInstigatedTime();
}
bFoundShowAsSuccessHit = true;
LastWeaponDamageScreenLocations.Add(Entry);
}
++HitLocationIndex;
}
}
UnconfirmedServerSideHitMarkers.RemoveAt(i);
break;
}
}
}
void ULyraWeaponStateComponent::AddUnconfirmedServerSideHitMarkers(const FGameplayAbilityTargetDataHandle& InTargetData, const TArray<FHitResult>& FoundHits)
{
FLyraServerSideHitMarkerBatch& NewUnconfirmedHitMarker = UnconfirmedServerSideHitMarkers.Emplace_GetRef(InTargetData.UniqueId);
if (APlayerController* OwnerPC = GetController<APlayerController>())
{
for (const FHitResult& Hit : FoundHits)
{
FVector2D HitScreenLocation;
if (UGameplayStatics::ProjectWorldToScreen(OwnerPC, Hit.Location, /*out*/ HitScreenLocation, /*bPlayerViewportRelative=*/ false))
{
FLyraScreenSpaceHitLocation& Entry = NewUnconfirmedHitMarker.Markers.AddDefaulted_GetRef();
Entry.Location = HitScreenLocation;
Entry.bShowAsSuccess = ShouldShowHitAsSuccess(Hit);
// Determine the hit zone
FGameplayTag HitZone;
if (const UPhysicalMaterialWithTags* PhysMatWithTags = Cast<const UPhysicalMaterialWithTags>(Hit.PhysMaterial.Get()))
{
for (const FGameplayTag MaterialTag : PhysMatWithTags->Tags)
{
if (MaterialTag.MatchesTag(TAG_Gameplay_Zone))
{
Entry.HitZone = MaterialTag;
break;
}
}
}
}
}
}
}
void ULyraWeaponStateComponent::UpdateDamageInstigatedTime(const FGameplayEffectContextHandle& EffectContext)
{
if (ShouldUpdateDamageInstigatedTime(EffectContext))
{
ActuallyUpdateDamageInstigatedTime();
}
}
void ULyraWeaponStateComponent::ActuallyUpdateDamageInstigatedTime()
{
// If our LastWeaponDamageInstigatedTime was not very recent, clear our LastWeaponDamageScreenLocations array
UWorld* World = GetWorld();
if (World->GetTimeSeconds() - LastWeaponDamageInstigatedTime > 0.1)
{
LastWeaponDamageScreenLocations.Reset();
}
LastWeaponDamageInstigatedTime = World->GetTimeSeconds();
}
double ULyraWeaponStateComponent::GetTimeSinceLastHitNotification() const
{
UWorld* World = GetWorld();
return World->TimeSince(LastWeaponDamageInstigatedTime);
}