blaster/Source/Blaster/Weapon/Weapon.cpp

374 lines
10 KiB
C++
Raw Normal View History

2022-04-29 22:26:35 +00:00
// Fill out your copyright notice in the Description page of Project Settings.
#include "Weapon.h"
2022-05-05 13:49:24 +00:00
#include "Casing.h"
2022-04-29 23:19:55 +00:00
#include "Blaster/Character/BlasterCharacter.h"
2022-05-20 23:33:02 +00:00
#include "Blaster/Components/CombatComponent.h"
2022-05-09 16:39:41 +00:00
#include "Blaster/PlayerController/BlasterPlayerController.h"
2022-04-29 22:26:35 +00:00
#include "Components/SphereComponent.h"
2022-04-29 23:19:55 +00:00
#include "Components/WidgetComponent.h"
2022-05-05 13:49:24 +00:00
#include "Engine/SkeletalMeshSocket.h"
2022-05-26 12:03:51 +00:00
#include "Kismet/KismetMathLibrary.h"
2022-04-30 14:08:00 +00:00
#include "Net/UnrealNetwork.h"
2022-04-29 22:26:35 +00:00
AWeapon::AWeapon()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
2022-05-19 09:10:41 +00:00
SetReplicateMovement(true);
2022-04-29 23:19:55 +00:00
2022-04-29 22:26:35 +00:00
WeaponMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WeaponMesh"));
2022-04-29 23:19:55 +00:00
SetRootComponent(WeaponMesh);
2022-04-29 22:26:35 +00:00
WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);
WeaponMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
2022-05-20 23:58:19 +00:00
WeaponMesh->SetCustomDepthStencilValue(CUSTOM_DEPTH_BLUE);
WeaponMesh->MarkRenderStateDirty();
EnableCustomDepth(true);
2022-04-29 22:26:35 +00:00
AreaSphere = CreateDefaultSubobject<USphereComponent>(TEXT("AreaSphere"));
AreaSphere->SetupAttachment(RootComponent);
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
2022-04-29 23:19:55 +00:00
PickupWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("PickupWidget"));
PickupWidget->SetupAttachment(RootComponent);
2022-04-29 22:26:35 +00:00
}
2022-04-30 14:08:00 +00:00
void AWeapon::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AWeapon, WeaponState);
2022-05-28 09:16:42 +00:00
DOREPLIFETIME_CONDITION(AWeapon, bUseServerSideRewind, COND_OwnerOnly);
2022-04-30 14:08:00 +00:00
}
2022-05-20 23:58:19 +00:00
void AWeapon::EnableCustomDepth(bool bEnabled)
{
if (WeaponMesh)
{
WeaponMesh->SetRenderCustomDepth(bEnabled);
}
}
2022-04-29 22:26:35 +00:00
void AWeapon::BeginPlay()
{
Super::BeginPlay();
2022-05-26 11:34:58 +00:00
AreaSphere->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
AreaSphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
AreaSphere->OnComponentBeginOverlap.AddDynamic(this, &AWeapon::OnSphereOverlap);
AreaSphere->OnComponentEndOverlap.AddDynamic(this, &AWeapon::OnSphereEndOverlap);
2022-04-29 22:26:35 +00:00
2022-04-29 23:19:55 +00:00
if (PickupWidget)
{
PickupWidget->SetVisibility(false);
}
}
2022-04-29 22:26:35 +00:00
2022-04-29 23:19:55 +00:00
void AWeapon::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(OtherActor);
2022-04-30 10:26:25 +00:00
if (BlasterCharacter)
2022-04-29 23:19:55 +00:00
{
2022-04-30 10:26:25 +00:00
BlasterCharacter->SetOverlappingWeapon(this);
}
}
void AWeapon::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
2022-04-30 14:08:00 +00:00
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
2022-04-30 10:26:25 +00:00
{
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(OtherActor);
if (BlasterCharacter)
{
BlasterCharacter->SetOverlappingWeapon(nullptr);
}
}
2022-05-09 16:39:41 +00:00
void AWeapon::OnRep_Owner()
{
Super::OnRep_Owner();
if (Owner == nullptr)
{
OwnerCharacter = nullptr;
OwnerController = nullptr;
2022-05-24 10:46:30 +00:00
}
else
2022-05-09 16:39:41 +00:00
{
2022-05-24 10:46:30 +00:00
OwnerCharacter = OwnerCharacter == nullptr ? Cast<ABlasterCharacter>(Owner) : OwnerCharacter;
if (OwnerCharacter && OwnerCharacter->GetPrimaryWeapon() && OwnerCharacter->GetPrimaryWeapon() == this)
{
SetHUDAmmo();
}
2022-05-09 16:39:41 +00:00
}
}
void AWeapon::SetHUDAmmo()
{
OwnerCharacter = OwnerCharacter == nullptr ? Cast<ABlasterCharacter>(GetOwner()) : OwnerCharacter;
if (OwnerCharacter)
{
OwnerController = OwnerController == nullptr ? Cast<ABlasterPlayerController>(OwnerCharacter->Controller) : OwnerController;
if (OwnerController)
{
OwnerController->SetHUDWeaponAmmo(Ammo);
}
}
}
void AWeapon::SpendRound()
{
2022-05-09 16:48:14 +00:00
Ammo = FMath::Clamp(Ammo - 1, 0, MagCapacity);
2022-05-09 16:39:41 +00:00
SetHUDAmmo();
2022-05-26 14:37:53 +00:00
if (HasAuthority())
{
ClientUpdateAmmo(Ammo);
}
else
{
++Sequence;
}
}
void AWeapon::ClientUpdateAmmo_Implementation(int32 ServerAmmo)
{
if (HasAuthority()) return;
Ammo = ServerAmmo;
--Sequence;
Ammo -= Sequence;
SetHUDAmmo();
}
void AWeapon::AddAmmo(int32 AmmoToAdd)
{
Ammo = FMath::Clamp(Ammo + AmmoToAdd, 0, MagCapacity);
SetHUDAmmo();
ClientAddAmmo(AmmoToAdd);
2022-05-09 16:39:41 +00:00
}
2022-05-26 14:37:53 +00:00
void AWeapon::ClientAddAmmo_Implementation(int32 AmmoToAdd)
2022-05-09 16:39:41 +00:00
{
2022-05-26 14:37:53 +00:00
if (HasAuthority()) return;
Ammo = FMath::Clamp(Ammo + AmmoToAdd, 0, MagCapacity);
2022-05-20 23:33:02 +00:00
OwnerCharacter = OwnerCharacter == nullptr ? Cast<ABlasterCharacter>(GetOwner()) : OwnerCharacter;
if (OwnerCharacter && OwnerCharacter->GetCombat() && IsFull())
{
OwnerCharacter->GetCombat()->JumpToShotgunEnd();
2022-05-26 14:37:53 +00:00
}
2022-05-09 16:39:41 +00:00
SetHUDAmmo();
}
2022-04-30 14:08:00 +00:00
void AWeapon::SetWeaponState(EWeaponState State)
{
WeaponState = State;
2022-05-25 13:24:29 +00:00
OnWeaponStateSet();
}
void AWeapon::OnWeaponStateSet()
{
2022-04-30 14:08:00 +00:00
switch (WeaponState)
{
case EWeaponState::EWS_Equipped:
2022-05-25 13:24:29 +00:00
OnEquipped();
break;
case EWeaponState::EWS_EquippedSecondary:
OnEquippedSecondary();
2022-05-08 17:31:58 +00:00
break;
case EWeaponState::EWS_Dropped:
2022-05-25 13:24:29 +00:00
OnDropped();
2022-04-30 14:08:00 +00:00
break;
}
}
2022-05-28 09:16:42 +00:00
void AWeapon::OnPingTooHigh(bool bPingTooHigh)
{
bUseServerSideRewind = !bPingTooHigh;
}
2022-05-25 13:24:29 +00:00
void AWeapon::OnRep_WeaponState()
2022-05-09 16:48:14 +00:00
{
2022-05-25 13:24:29 +00:00
OnWeaponStateSet();
2022-05-09 16:48:14 +00:00
}
2022-05-25 13:24:29 +00:00
void AWeapon::OnEquipped()
2022-05-20 23:33:02 +00:00
{
2022-05-25 13:24:29 +00:00
ShowPickupWidget(false);
GetAreaSphere()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
WeaponMesh->SetSimulatePhysics(false);
WeaponMesh->SetEnableGravity(false);
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
if (IsSMG())
{
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
WeaponMesh->SetEnableGravity(true);
WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
}
EnableCustomDepth(false);
2022-05-28 09:16:42 +00:00
OwnerCharacter = OwnerCharacter == nullptr ? Cast<ABlasterCharacter>(GetOwner()) : OwnerCharacter;
if (OwnerCharacter && bUseServerSideRewind)
{
OwnerController = OwnerController == nullptr ? Cast<ABlasterPlayerController>(OwnerCharacter->Controller) : OwnerController;
if (OwnerController && HasAuthority() && !OwnerController->HighPingDelegate.IsBound())
{
OwnerController->HighPingDelegate.AddDynamic(this, &AWeapon::OnPingTooHigh);
}
}
2022-05-20 23:33:02 +00:00
}
2022-05-25 13:24:29 +00:00
void AWeapon::OnEquippedSecondary()
2022-04-30 14:08:00 +00:00
{
2022-05-25 13:24:29 +00:00
ShowPickupWidget(false);
GetAreaSphere()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
WeaponMesh->SetSimulatePhysics(false);
WeaponMesh->SetEnableGravity(false);
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
if (IsSMG())
2022-04-30 14:08:00 +00:00
{
2022-05-08 17:31:58 +00:00
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
2022-05-25 13:24:29 +00:00
WeaponMesh->SetEnableGravity(true);
WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
}
if (WeaponMesh)
{
WeaponMesh->SetCustomDepthStencilValue(CUSTOM_DEPTH_TAN);
2022-05-20 23:58:19 +00:00
WeaponMesh->MarkRenderStateDirty();
2022-04-30 14:08:00 +00:00
}
2022-05-28 09:16:42 +00:00
OwnerCharacter = OwnerCharacter == nullptr ? Cast<ABlasterCharacter>(GetOwner()) : OwnerCharacter;
if (OwnerCharacter && bUseServerSideRewind)
{
OwnerController = OwnerController == nullptr ? Cast<ABlasterPlayerController>(OwnerCharacter->Controller) : OwnerController;
if (OwnerController && HasAuthority() && OwnerController->HighPingDelegate.IsBound())
{
OwnerController->HighPingDelegate.RemoveDynamic(this, &AWeapon::OnPingTooHigh);
}
}
2022-04-30 14:08:00 +00:00
}
2022-05-25 13:24:29 +00:00
void AWeapon::OnDropped()
{
if (HasAuthority())
{
GetAreaSphere()->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
WeaponMesh->SetSimulatePhysics(true);
WeaponMesh->SetEnableGravity(true);
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);
WeaponMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);
WeaponMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);
WeaponMesh->SetCustomDepthStencilValue(CUSTOM_DEPTH_BLUE);
WeaponMesh->MarkRenderStateDirty();
EnableCustomDepth(true);
2022-05-28 09:16:42 +00:00
OwnerCharacter = OwnerCharacter == nullptr ? Cast<ABlasterCharacter>(GetOwner()) : OwnerCharacter;
if (OwnerCharacter && bUseServerSideRewind)
{
OwnerController = OwnerController == nullptr ? Cast<ABlasterPlayerController>(OwnerCharacter->Controller) : OwnerController;
if (OwnerController && HasAuthority() && OwnerController->HighPingDelegate.IsBound())
{
OwnerController->HighPingDelegate.RemoveDynamic(this, &AWeapon::OnPingTooHigh);
}
}
2022-05-25 13:24:29 +00:00
}
bool AWeapon::IsEmpty()
{
return Ammo <= 0;
}
bool AWeapon::IsFull()
{
return Ammo == MagCapacity;
}
2022-04-30 10:26:25 +00:00
void AWeapon::ShowPickupWidget(bool bShowWidget)
{
if (PickupWidget)
{
PickupWidget->SetVisibility(bShowWidget);
2022-04-29 23:19:55 +00:00
}
}
2022-05-04 20:27:26 +00:00
2022-05-05 10:51:14 +00:00
void AWeapon::Fire(const FVector& HitTarget)
2022-05-04 20:27:26 +00:00
{
if (FireAnimation)
{
WeaponMesh->PlayAnimation(FireAnimation, false);
}
2022-05-05 13:49:24 +00:00
if (CasingClass)
{
const USkeletalMeshSocket* AmmoEjectSocket = WeaponMesh->GetSocketByName(FName("AmmoEject"));
if (AmmoEjectSocket)
{
FTransform SocketTransform = AmmoEjectSocket->GetSocketTransform(WeaponMesh);
UWorld* World = GetWorld();
if (World)
{
World->SpawnActor<ACasing>(
CasingClass,
SocketTransform.GetLocation(),
SocketTransform.GetRotation().Rotator()
);
}
}
}
2022-05-26 14:37:53 +00:00
SpendRound();
2022-05-04 20:27:26 +00:00
}
2022-05-08 17:31:58 +00:00
void AWeapon::Dropped()
{
2022-05-23 23:16:06 +00:00
if (bDestroyWeapon)
{
SetLifeSpan(30.f);
}
2022-05-08 17:31:58 +00:00
SetWeaponState(EWeaponState::EWS_Dropped);
const FDetachmentTransformRules DetachRules(EDetachmentRule::KeepWorld, true);
WeaponMesh->DetachFromComponent(DetachRules);
SetOwner(nullptr);
2022-05-09 16:39:41 +00:00
OwnerCharacter = nullptr;
OwnerController = nullptr;
2022-05-08 17:31:58 +00:00
}
2022-05-09 22:00:06 +00:00
2022-05-26 12:03:51 +00:00
FVector AWeapon::TraceEndWithScatter(const FVector& HitTarget)
{
const USkeletalMeshSocket* MuzzleFlashSocket = GetWeaponMesh()->GetSocketByName("MuzzleFlash");
if (MuzzleFlashSocket == nullptr) return FVector();
2022-05-26 12:29:39 +00:00
const FTransform SocketTransform = MuzzleFlashSocket->GetSocketTransform(GetWeaponMesh());
const FVector TraceStart = SocketTransform.GetLocation();
const FVector ToTargetNormalized = (HitTarget - TraceStart).GetSafeNormal();
const FVector SphereCenter = TraceStart + ToTargetNormalized * DistanceToSphere;
const FVector RandVec = UKismetMathLibrary::RandomUnitVector() * FMath::FRandRange(0.f, SphereRadius);
const FVector EndLoc = SphereCenter + RandVec;
const FVector ToEndLoc = EndLoc - TraceStart;
2022-05-26 12:03:51 +00:00
/*
DrawDebugSphere(GetWorld(), SphereCenter, SphereRadius, 12, FColor::Red, true);
DrawDebugSphere(GetWorld(), ToEndLoc, 4.f, 12, FColor::Blue, true);
DrawDebugLine(
GetWorld(),
TraceStart,
FVector(TraceStart + ToEndLoc * TRACE_LENGTH / ToEndLoc.Size()),
FColor::Cyan,
true
);
*/
return FVector(TraceStart + ToEndLoc * TRACE_LENGTH / ToEndLoc.Size());
}