// Fill out your copyright notice in the Description page of Project Settings. #include "Characters/Components/ShootingComponent.h" #include "Items/ThrowableBase.h" UShootingComponent::UShootingComponent() { PrimaryComponentTick.bCanEverTick = true; } void UShootingComponent::BeginPlay() { Super::BeginPlay(); PlayerCharacter = Cast(GetOwner()); CameraManager = GetWorld()->GetFirstPlayerController()->PlayerCameraManager; DefaultFOV = CameraManager->GetFOVAngle(); TargetFOV = DefaultFOV; } void UShootingComponent::Shoot() { if (!IsValid(CurrentGunBase) || CurrentGunBase->CurrentAmmo == 0 || bIsReloading || !bCanShoot) return; bCanShoot = false; CurrentGunBase->CurrentAmmo--; AActor* HitActor = ExecuteLineTrace(CurrentGunBase->MaxRange); if (HitActor) { IDamageable::Execute_TakeDamage(HitActor, CurrentGunBase->DamageValue); UE_LOG(LogTemp, Display, TEXT("Shoot. Ammo: %d/%d"), CurrentGunBase->CurrentAmmo, CurrentGunBase->MaxAmmo); // Docelowo tutaj wywołanie UI aktualizujące stan ammo } PlayerCharacter->GetWorldTimerManager().SetTimer(ShootCooldownTimer, this, &UShootingComponent::ResetFireCooldown, CurrentGunBase->FireRateCooldown, false); // Odrzut float RecoilPitch = FMath::RandRange(-1.0f, -0.5f); float RecoilYaw = FMath::RandRange(-0.5f, 0.5f); PlayerCharacter->AddControllerPitchInput(RecoilPitch * CurrentGunBase->RecoilForceMultiplier); PlayerCharacter->AddControllerYawInput(RecoilYaw * CurrentGunBase->RecoilForceMultiplier); } void UShootingComponent::MeleAttack() { AActor* HitActor = ExecuteLineTrace(MeleRange); if (HitActor) { IDamageable::Execute_TakeDamage(HitActor, MeleDamageValue); } } AActor* UShootingComponent::ExecuteLineTrace(float LineRange) { FVector ForwardVector = CameraManager->GetActorForwardVector(); FVector ViewLocation = CameraManager->GetCameraLocation(); FVector EndVector = ViewLocation + ForwardVector * LineRange; FHitResult Hit; FCollisionQueryParams QueryParams; QueryParams.AddIgnoredActor(PlayerCharacter); // DEBUG if (bShowDebugLine) DrawDebugLine(GetWorld(), ViewLocation, EndVector, FColor::Red, false, 5.0f, 0, 1.0f); if (GetWorld()->LineTraceSingleByChannel(Hit, ViewLocation, EndVector, ECC_Visibility, QueryParams) && IsValid(Hit.GetActor()) && Hit.GetActor()->Implements()) { return Hit.GetActor(); } return nullptr; } void UShootingComponent::Reload() { if (!IsValid(CurrentGunBase) || bIsReloading) return; bIsReloading = true; StopAiming(); CurrentGunBase->CurrentAmmo = CurrentGunBase->MaxAmmo; PlayerCharacter->GetWorldTimerManager().SetTimer(ReloadTimer, this, &UShootingComponent::ReloadCompleted, CurrentGunBase->ReloadTime, false); UE_LOG(LogTemp, Display, TEXT("Reloaded. Ammo: %d/%d"), CurrentGunBase->CurrentAmmo, CurrentGunBase->MaxAmmo); // Docelowo tutaj wywo�anie UI aktualizuj�ce stan ammo } bool UShootingComponent::AddAmmo(EAmmoType CheckingAmmo, int AddValue) { // Adding ammo only if type is compatible if (CurrentGunBase && CurrentGunBase->AmmoType == CheckingAmmo) { CurrentGunBase->AddAmmo(AddValue); return true; } if (SecondaryGunBase && SecondaryGunBase->AmmoType == CheckingAmmo) { SecondaryGunBase->AddAmmo(AddValue); return true; } return false; } void UShootingComponent::PickUpGun(UStaticMeshComponent* PickedGunMesh) { if (IsValid(CurrentGunBase) && IsValid(SecondaryGunBase)) DropGun(); FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, EAttachmentRule::KeepRelative, true); PickedGunMesh->SetSimulatePhysics(false); PickedGunMesh->SetCastShadow(false); PickedGunMesh->AttachToComponent(PlayerCharacter->Weapon, AttachmentRules, FName(TEXT("GripPoint"))); AGunBase* PickedGunBase = Cast(PickedGunMesh->GetOwner()); if (IsValid(CurrentGunBase)) { SecondaryGunBase = PickedGunBase; SecondaryGunMesh = PickedGunMesh; SecondaryGunMesh->SetVisibility(false); } else { CurrentGunBase = PickedGunBase; CurrentGunMesh = PickedGunMesh; } } bool UShootingComponent::PickUpThrow(AThrowableBase* ThrowItem) { AThrowableBase* NewThrowable = NewObject(); 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(CurrentGunBase)) return; StopAiming(); FDetachmentTransformRules DetachmentRules(EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, false); CurrentGunMesh->DetachFromComponent(DetachmentRules); CurrentGunMesh->SetSimulatePhysics(true); CurrentGunMesh->bCastDynamicShadow = true; CurrentGunMesh->CastShadow = true; CurrentGunMesh = nullptr; CurrentGunBase = 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(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* tempBase = CurrentGunBase; CurrentGunBase = SecondaryGunBase; SecondaryGunBase = tempBase; UStaticMeshComponent* tempMesh = CurrentGunMesh; CurrentGunMesh = SecondaryGunMesh; SecondaryGunMesh = tempMesh; if (IsValid(SecondaryGunMesh)) SecondaryGunMesh->SetVisibility(false); if (IsValid(CurrentGunMesh)) CurrentGunMesh->SetVisibility(true); 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(CurrentGunBase)) { PlayerCharacter->bIsAimingMode = true; PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed * AimingMoveSpeedScale; TargetFOV = CurrentGunBase->AimingFOV; } } void UShootingComponent::StopAiming() { PlayerCharacter->bIsAimingMode = false; PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed; TargetFOV = DefaultFOV; } void UShootingComponent::Throw() { if (IsValid(ThrowableItem)) { FVector DroppedPos = CameraManager->GetCameraLocation() + CameraManager->GetActorForwardVector() * 100.f; FTransform DroppedTransform = PlayerCharacter->GetActorTransform(); DroppedTransform.SetLocation(DroppedPos); AThrowableBase* ThrowedItem = GetWorld()->SpawnActor(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 * 1000.f); // Need force to change to the game if (ThrowableItem->Quantity <= 0) { ThrowableItem->Destroy(); ThrowableItem = nullptr; } } } } bool UShootingComponent::IsHoldingGun() { if (IsValid(CurrentGunBase)) return true; return false; } 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); }