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);
|
|
|
|
}
|