ExoWest/Source/Exo/Private/Characters/Components/ShootingComponent.cpp
Foxim 2b6bdf9f80 feat: add grenade pickup, throw, and drop mechanics
Implemented basic grenade interaction:
- Player can pick up, throw, and drop grenades.

To-do:
- Refine throwing mechanics (e.g. direction, force).
- Clean up and refactor related code.
2025-04-08 18:22:33 +02:00

310 lines
9.5 KiB
C++
Raw Blame History

// Fill out your copyright notice in the Description page of Project Settings.
#include "Characters/Components/ShootingComponent.h"
#include "Items/ThrowableBase.h"
// Sets default values for this component's properties
UShootingComponent::UShootingComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void UShootingComponent::BeginPlay()
{
Super::BeginPlay();
PlayerCharacter = Cast<AExoPlayerCharacter>(GetOwner());
CameraManager = GetWorld()->GetFirstPlayerController()->PlayerCameraManager;
DefaultFOV = CameraManager->GetFOVAngle();
TargetFOV = DefaultFOV;
}
void UShootingComponent::Shoot()
{
if (!IsValid(CurrentGun) || CurrentGun->CurrentAmmo == 0 || bIsReloading || !bCanShoot) return;
bCanShoot = false;
CurrentGun->CurrentAmmo--;
AActor* HitActor = ExecuteLineTrace(CurrentGun->MaxRange);
if (HitActor)
{
IDamageable::Execute_TakeDamage(HitActor, CurrentGun->DamageValue);
UE_LOG(LogTemp, Display, TEXT("Shoot. Ammo: %d/%d"), CurrentGun->CurrentAmmo, CurrentGun->MaxAmmo); // Docelowo tutaj wywołanie UI aktualizujące stan ammo
}
PlayerCharacter->GetWorldTimerManager().SetTimer(ShootCooldownTimer, this, &UShootingComponent::ResetFireCooldown, CurrentGun->FireRateCooldown, false);
// Odrzut
float RecoilPitch = FMath::RandRange(-1.0f, -0.5f);
float RecoilYaw = FMath::RandRange(-0.5f, 0.5f);
PlayerCharacter->AddControllerPitchInput(RecoilPitch * CurrentGun->RecoilForceMultiplier);
PlayerCharacter->AddControllerYawInput(RecoilYaw * CurrentGun->RecoilForceMultiplier);
}
void UShootingComponent::MeleAttack()
{
AActor* HitActor = ExecuteLineTrace(MeleRange);
if (HitActor)
{
IDamageable::Execute_TakeDamage(HitActor, MeleDamageValue);
}
}
AActor* UShootingComponent::ExecuteLineTrace(float LineRange)
{
FVector ForwardVector = PlayerCharacter->GetActorForwardVector();
FVector ViewLocation;
FRotator ViewRotation;
PlayerCharacter->GetController()->GetPlayerViewPoint(ViewLocation, ViewRotation);
FVector EndVector = ViewLocation + ForwardVector * LineRange;
FHitResult Hit;
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(PlayerCharacter);
// DEBUG
if (bShowDebugLine)
DrawDebugLine(GetWorld(), ViewLocation, EndVector, FColor::Red, false, 1.0f, 0, 1.0f);
if (GetWorld()->LineTraceSingleByChannel(Hit, ViewLocation, EndVector, ECC_Visibility, QueryParams) &&
IsValid(Hit.GetActor()) && Hit.GetActor()->Implements<UDamageable>())
{
return Hit.GetActor();
}
return nullptr;
}
void UShootingComponent::Reload()
{
if (!IsValid(CurrentGun) || bIsReloading) return;
bIsReloading = true;
StopAiming();
CurrentGun->CurrentAmmo = CurrentGun->MaxAmmo;
PlayerCharacter->GetWorldTimerManager().SetTimer(ReloadTimer, this, &UShootingComponent::ReloadCompleted, CurrentGun->ReloadTime, false);
UE_LOG(LogTemp, Display, TEXT("Reloaded. Ammo: %d/%d"), CurrentGun->CurrentAmmo, CurrentGun->MaxAmmo); // Docelowo tutaj wywo<77>anie UI aktualizuj<75>ce stan ammo
}
void UShootingComponent::PickUpGun(AGunBase* gunItem)
{
if (IsValid(CurrentGun) && IsValid(SecondaryGun))
DropGun();
AGunBase* NewGun = NewObject<AGunBase>();
NewGun->MaxRange = gunItem->MaxRange;
NewGun->DamageValue = gunItem->DamageValue;
NewGun->FireRateCooldown = gunItem->FireRateCooldown;
NewGun->RecoilForceMultiplier = gunItem->RecoilForceMultiplier;
NewGun->ReloadTime = gunItem->ReloadTime;
NewGun->CurrentAmmo = gunItem->CurrentAmmo;
NewGun->MaxAmmo = gunItem->MaxAmmo;
NewGun->SceneItemClass = gunItem->GetClass();
if (IsValid(CurrentGun))
SecondaryGun = NewGun;
else
CurrentGun = NewGun;
}
bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem)
{
AThrowableBase* NewThrowable = NewObject<AThrowableBase>();
if (IsValid(ThrowableItem))
{
if (ThrowableItem->GetClass() == NewThrowable->GetClass())
{
if (ThrowableItem->Quantity + ThrowItem->Quantity >= ThrowableItem->MaxQuantity)
{
ThrowableItem->Quantity = ThrowableItem->MaxQuantity;
return false;
}
ThrowableItem->Quantity += ThrowItem->Quantity;
return true;
}
DropThrow();
}
NewThrowable->Damage = ThrowItem->Damage;
NewThrowable->Quantity = ThrowItem->Quantity;
NewThrowable->Radius = ThrowItem->Radius;
NewThrowable->ThrowableType = ThrowItem->ThrowableType;
NewThrowable->SceneItemClass = ThrowItem->GetClass();
ThrowableItem = NewThrowable;
return true;
}
void UShootingComponent::DropGun()
{
if (!IsValid(CurrentGun)) return;
StopAiming();
FVector ForwardVector = PlayerCharacter->GetActorForwardVector();
FVector PlayerPos = PlayerCharacter->GetActorLocation();
FVector DroppedGunPos = PlayerPos + (ForwardVector * DropGunRange);
FTransform DroppedGunTransform = PlayerCharacter->GetActorTransform();
DroppedGunTransform.SetLocation(DroppedGunPos);
AGunBase* DroppedGun = GetWorld()->SpawnActor<AGunBase>(CurrentGun->SceneItemClass, DroppedGunTransform);
if (DroppedGun)
{
DroppedGun->MaxRange = CurrentGun->MaxRange;
DroppedGun->DamageValue = CurrentGun->DamageValue;
DroppedGun->FireRateCooldown = CurrentGun->FireRateCooldown;
DroppedGun->RecoilForceMultiplier = CurrentGun->RecoilForceMultiplier;
DroppedGun->ReloadTime = CurrentGun->ReloadTime;
DroppedGun->CurrentAmmo = CurrentGun->CurrentAmmo;
DroppedGun->MaxAmmo = CurrentGun->MaxAmmo;
CurrentGun->Destroy();
CurrentGun = nullptr;
}
}
void UShootingComponent::DropThrow()
{
if (!IsValid(ThrowableItem)) return;
FVector ForwardVector = PlayerCharacter->GetActorForwardVector();
FVector DroppedPos = PlayerCharacter->GetActorLocation() + (ForwardVector * 100.f);
FTransform DroppedTransform = PlayerCharacter->GetActorTransform();
DroppedTransform.SetLocation(DroppedPos);
AThrowableBase* DropedItem = GetWorld()->SpawnActor<AThrowableBase>(ThrowableItem->SceneItemClass, DroppedTransform);
if (DropedItem)
{
ThrowableItem->Quantity--;
DropedItem->Damage = ThrowableItem->Damage;
DropedItem->Radius = ThrowableItem->Radius;
DropedItem->Quantity = ThrowableItem->Quantity;
DropedItem->ThrowableType = ThrowableItem->ThrowableType;
}
}
void UShootingComponent::ResetFireCooldown()
{
bCanShoot = true;
}
void UShootingComponent::ReloadCompleted()
{
bIsReloading = false;
}
void UShootingComponent::SelectGun(bool bSelectFirstSlot)
{
if (bSelectFirstSlot != bIsFirstGunSelected)
SwitchGun();
}
void UShootingComponent::SwitchGun()
{
AGunBase* temp = CurrentGun;
CurrentGun = SecondaryGun;
SecondaryGun = temp;
bIsFirstGunSelected = !bIsFirstGunSelected;
StopAiming();
// Debug info
if (bIsFirstGunSelected)
{
UE_LOG(LogTemp, Display, TEXT("Zmiana broni na broń 1"));
}
else
{
UE_LOG(LogTemp, Display, TEXT("Zmiana broni na broń 2"));
}
}
void UShootingComponent::StartAiming()
{
if (IsValid(CurrentGun))
{
PlayerCharacter->bIsAimingMode = true;
PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed * AimingMoveSpeedScale;
TargetFOV = CurrentGun->AimingFOV;
}
}
void UShootingComponent::StopAiming()
{
PlayerCharacter->bIsAimingMode = false;
PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed;
TargetFOV = DefaultFOV;
}
void UShootingComponent::Throw() // Wymaga dopracowania
{
if (IsValid(ThrowableItem))
{
FVector ForwardVector = PlayerCharacter->GetActorForwardVector();
FVector DroppedPos = PlayerCharacter->GetActorLocation() + (ForwardVector * 100.f);
FTransform DroppedTransform = PlayerCharacter->GetActorTransform();
DroppedTransform.SetLocation(DroppedPos);
AThrowableBase* ThrowedItem = GetWorld()->SpawnActor<AThrowableBase>(ThrowableItem->SceneItemClass, DroppedTransform);
if (ThrowedItem)
{
ThrowableItem->Quantity--;
ThrowedItem->Damage = ThrowableItem->Damage;
ThrowedItem->Radius = ThrowableItem->Radius;
ThrowedItem->Quantity = 1;
ThrowedItem->ThrowableType = ThrowableItem->ThrowableType;
FVector ThrowVector = (ThrowedItem->GetActorLocation() - PlayerCharacter->GetActorLocation()).GetSafeNormal();
ThrowedItem->Mesh->SetPhysicsLinearVelocity(ThrowVector * 800.f);
if (ThrowableItem->Quantity <= 0)
{
ThrowableItem->Destroy();
ThrowableItem = nullptr;
}
}
}
}
// Called every frame
void UShootingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
float CurrentFOV = CameraManager->GetFOVAngle();
float NewFOV = FMath::FInterpTo(CurrentFOV, TargetFOV, DeltaTime, AimingAnimationSpeed);
CameraManager->SetFOV(NewFOV);
}