diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index e41b932e..603c2fe2 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -56,7 +56,7 @@ NearClipPlane=0.010000 +Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="MyCharacter",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.") +Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.") +Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap),(Channel="MyCharacter",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") -+Profiles=(Name="MyCharacter",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="MyCharacter",CustomResponses=((Channel="MyCollectable",Response=ECR_Overlap),(Channel="MyC4",Response=ECR_Overlap),(Channel="BuyCheck",Response=ECR_Overlap)),HelpMessage="Needs description") ++Profiles=(Name="MyCharacter",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="MyCharacter",CustomResponses=((Channel="MyCollectable",Response=ECR_Overlap),(Channel="MyC4",Response=ECR_Overlap),(Channel="BuyCheck",Response=ECR_Overlap),(Channel="ItemCheck",Response=ECR_Overlap)),HelpMessage="Needs description") +Profiles=(Name="MyCollectable",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="MyCollectable",CustomResponses=((Channel="MyCharacter",Response=ECR_Overlap),(Channel="ItemCheck")),HelpMessage="Needs description") +Profiles=(Name="MyC4",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="MyC4",CustomResponses=((Channel="MyCharacter",Response=ECR_Overlap),(Channel="MyPlantableArea",Response=ECR_Overlap),(Channel="ItemCheck",Response=ECR_Overlap)),HelpMessage="Needs description") +Profiles=(Name="MyPlantableArea",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="MyPlantableArea",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore),(Channel="MyCharacter",Response=ECR_Ignore),(Channel="Attack",Response=ECR_Ignore),(Channel="MyCollectable",Response=ECR_Ignore),(Channel="PlantCheck")),HelpMessage="Needs description") @@ -133,6 +133,9 @@ NearClipPlane=0.010000 +PropertyRedirects=(OldName="/Script/MyProject.MyAimableWeapon.MuzzleFlash",NewName="/Script/MyProject.MyAimableWeapon.BulletTrail") +ClassRedirects=(OldName="/Script/MyProject.MySkeletalMeshCollectable",NewName="/Script/MyProject.MySkeletalMeshCollectable") +PropertyRedirects=(OldName="/Script/MyProject.MyCollectable.Mesh",NewName="/Script/MyProject.MyCollectable.MeshComponent") ++ClassRedirects=(OldName="/Script/MyProject.MyFragGrenadeItem",NewName="/Script/MyProject.MyFragGrenade") ++PropertyRedirects=(OldName="/Script/MyProject.MyCharacter.HandWeapon",NewName="/Script/MyProject.MyCharacter.HandCollectable") ++FunctionRedirects=(OldName="/Script/MyProject.MyCharacter.Server_AttachArmWeapon",NewName="/Script/MyProject.MyCharacter.Server_AttachArmCollectable") [/Script/Engine.AudioSettings] DefaultSoundConcurrencyName=/Game/Blueprints/NewSoundConcurrency.NewSoundConcurrency diff --git a/Content/Blueprints/BPMyC4.uasset b/Content/Blueprints/BPMyC4.uasset index 1a745667..b1be63be 100644 Binary files a/Content/Blueprints/BPMyC4.uasset and b/Content/Blueprints/BPMyC4.uasset differ diff --git a/Source/MyProject/MyAmmoWidget.cpp b/Source/MyProject/MyAmmoWidget.cpp index b84239a3..71aeb7ba 100644 --- a/Source/MyProject/MyAmmoWidget.cpp +++ b/Source/MyProject/MyAmmoWidget.cpp @@ -23,7 +23,7 @@ void UMyAmmoWidget::BindPlayerState(AMyPlayerState* PlayerState) const if (IsValid(PlayerState)) { PlayerState->BindOnStateChanged(this, &UMyAmmoWidget::HandleStateChanged); - PlayerState->BindOnWeaponChanged(this, &UMyAmmoWidget::HandleWeaponChanged); + PlayerState->BindOnHandChanged(this, &UMyAmmoWidget::HandleWeaponChanged); } } @@ -35,7 +35,7 @@ void UMyAmmoWidget::HandleStateChanged(AMyPlayerState* PlayerState, const EMyCha } else { - if (PlayerState->GetWeapon() == nullptr) + if (Cast(PlayerState->GetCurrentHand()) == nullptr) { AmmoText->SetText(FText::FromString(TEXT(""))); } @@ -46,10 +46,12 @@ void UMyAmmoWidget::HandleStateChanged(AMyPlayerState* PlayerState, const EMyCha void UMyAmmoWidget::HandleWeaponChanged(AMyPlayerState* PlayerState) const { - if (IsValid(PlayerState->GetWeapon())) + const auto& Weapon = Cast(PlayerState->GetCurrentHand()); + + if (IsValid(Weapon)) { LOG_FUNC(LogTemp, Warning, "Weapon changed, update ammo count"); - const auto& StatComponent = PlayerState->GetWeapon()->GetWeaponStatComponent(); + const auto& StatComponent = Weapon->GetWeaponStatComponent(); UpdateAmmo(StatComponent->GetCurrentAmmoCount(), StatComponent->GetRemainingAmmoCount()); } else diff --git a/Source/MyProject/MyAnimInstance.cpp b/Source/MyProject/MyAnimInstance.cpp index 8cb291bb..471174ff 100644 --- a/Source/MyProject/MyAnimInstance.cpp +++ b/Source/MyProject/MyAnimInstance.cpp @@ -73,7 +73,7 @@ void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds) Horizontal = FVector::DotProduct(Velocity, Character->GetActorRightVector()); Yaw = Character->GetActorRotation().Yaw; Pitch = Character->GetPitchInput(); - bHasWeapon = IsValid(Cast(Character->GetWeapon())); + bHasWeapon = IsValid(Character->TryGetWeapon()); } FName UMyAnimInstance::GetAttackMontageSectionName(const int32 NewIndex) diff --git a/Source/MyProject/MyC4.cpp b/Source/MyProject/MyC4.cpp index 07ac5ac0..78767cb7 100644 --- a/Source/MyProject/MyC4.cpp +++ b/Source/MyProject/MyC4.cpp @@ -126,12 +126,12 @@ bool AMyC4::IsDefusable(const bool bCheckSpeed) const TArray OverlapResult; - const auto& DefuserCheck = GetWorld()->OverlapMultiByObjectType + const auto& DefuserCheck = GetWorld()->OverlapMultiByChannel ( OverlapResult, GetActorLocation(), FQuat::Identity, - ObjectParams, + ECC_GameTraceChannel1, FCollisionShape::MakeSphere(50.f), Params ); @@ -506,6 +506,7 @@ bool AMyC4::TryDefuse(AMyCharacter* Character) if (!IsDefusable(false)) { LOG_FUNC(LogTemp, Warning, "Bomb is not defusable"); + DefusingCharacter = nullptr; return false; } diff --git a/Source/MyProject/MyCharacter.cpp b/Source/MyProject/MyCharacter.cpp index 7a31ed3b..4d4a3fd6 100644 --- a/Source/MyProject/MyCharacter.cpp +++ b/Source/MyProject/MyCharacter.cpp @@ -92,40 +92,44 @@ AMyCharacter::AMyCharacter() : CanAttack(true) ArmMeshComponent->SetCastShadow(false); } -UMyInventoryComponent* AMyCharacter::GetInventory() const +AMyWeapon* AMyCharacter::TryGetWeapon() const { - const auto& State = GetPlayerState(); - return State->GetInventoryComponent(); -} + const auto& MyPlayerState = GetPlayerState(); -UMyStatComponent* AMyCharacter::GetStatComponent() const -{ - const auto& State = GetPlayerState(); - return State->GetStatComponent(); + if (IsValid(MyPlayerState)) + { + return Cast(MyPlayerState->GetCurrentHand()); + } + else + { + return nullptr; + } } -AMyWeapon* AMyCharacter::GetWeapon() const +AMyItem* AMyCharacter::TryGetItem() const { - const auto& State = GetPlayerState(); + const auto& MyPlayerState = GetPlayerState(); - if (!IsValid(State)) + if (IsValid(MyPlayerState)) + { + return Cast(MyPlayerState->GetCurrentHand()); + } + else { return nullptr; } - - return State->GetWeapon(); } -AMyCollectable* AMyCharacter::GetCurrentItem() const +UMyInventoryComponent* AMyCharacter::GetInventory() const { const auto& State = GetPlayerState(); + return State->GetInventoryComponent(); +} - if (IsValid(State)) - { - return State->GetCurrentItem(); - } - - return nullptr; +UMyStatComponent* AMyCharacter::GetStatComponent() const +{ + const auto& State = GetPlayerState(); + return State->GetStatComponent(); } // Called when the game starts or when spawned @@ -160,7 +164,7 @@ void AMyCharacter::GetLifetimeReplicatedProps(TArray& OutLife { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(AMyCharacter, PitchInput); - DOREPLIFETIME(AMyCharacter, HandWeapon); + DOREPLIFETIME(AMyCharacter, HandCollectable); } float AMyCharacter::TakeDamage( @@ -225,7 +229,7 @@ void AMyCharacter::Server_Attack_Implementation(const float Value) return; } - if (IsValid(GetWeapon()) && !GetWeapon()->CanDoAttack()) + if (IsValid(TryGetWeapon()) && !TryGetWeapon()->CanDoAttack()) { return; } @@ -259,12 +263,12 @@ void AMyCharacter::Reload() void AMyCharacter::Server_Reload_Implementation() { - if (!IsValid(GetWeapon())) + if (!IsValid(TryGetWeapon())) { return; } - if (!GetWeapon()->CanBeReloaded()) + if (!TryGetWeapon()->CanBeReloaded()) { return; } @@ -278,7 +282,7 @@ void AMyCharacter::NotifyDamage(AMyCharacter* const Target) const { UE_LOG(LogTemp, Warning , TEXT("Hit Actor: %s"), *Target->GetName()); const FDamageEvent DamageEvent; - Target->TakeDamage(GetDamage(), DamageEvent, GetController(), GetWeapon()); + Target->TakeDamage(GetDamage(), DamageEvent, GetController(), TryGetWeapon()); } } @@ -291,7 +295,7 @@ bool AMyCharacter::HitscanAttack(OUT FHitResult& OutHitResult) const FVector EndVector = Camera->GetComponentLocation() + Camera->GetForwardVector() * - GetWeapon()->GetWeaponStatComponent()->GetRange(); + TryGetWeapon()->GetWeaponStatComponent()->GetRange(); const auto& Result = GetWorld()->LineTraceSingleByChannel ( @@ -327,17 +331,20 @@ void AMyCharacter::Multi_Reload_Implementation() void AMyCharacter::ReloadStart() const { - if (!IsValid(GetWeapon())) + if (!IsValid(TryGetWeapon())) { return; } UE_LOG(LogTemp, Warning, TEXT("Reload")); - GetWeapon()->Reload(); + TryGetWeapon()->Reload(); - if (IsLocallyControlled() && IsValid(HandWeapon)) + if (IsLocallyControlled() && IsValid(HandCollectable)) { - HandWeapon->Reload(); + if (const auto& HandWeapon = Cast(HandCollectable)) + { + HandWeapon->Reload(); + } } } @@ -361,12 +368,12 @@ void AMyCharacter::Aim() return; } - if (!IsValid(GetWeapon())) + if (!IsValid(TryGetWeapon())) { return; } - if (!GetWeapon()->IsA()) + if (!TryGetWeapon()->IsA()) { return; } @@ -449,32 +456,35 @@ void AMyCharacter::AttackStart(const float Value) constexpr int32 MaxAttackIndex = 3; - if (IsValid(GetWeapon())) + if (IsValid(TryGetWeapon())) { LOG_FUNC_RAW(LogTemp, Warning, *FString::Printf(TEXT("Attack with weapon, Is Client? : %d"), !HasAuthority())); - if (!GetWeapon()->Attack()) + if (!TryGetWeapon()->Attack()) { LOG_FUNC(LogTemp, Error, "Failed to attack"); return; } - if (IsLocallyControlled() && IsValid(HandWeapon)) + if (IsLocallyControlled() && IsValid(HandCollectable)) { - HandWeapon->Attack(); - OnHandWeaponAttackEndedHandle = HandWeapon->BindOnFireReady(this, &AMyCharacter::ResetAttack); + if (const auto& HandWeapon = Cast(HandCollectable)) + { + HandWeapon->Attack(); + OnHandWeaponAttackEndedHandle = HandWeapon->BindOnFireReady(this, &AMyCharacter::ResetAttack); + } } // todo: process client before sending rpc to server. - switch (GetWeapon()->GetWeaponStatComponent()->GetWeaponType()) + switch (TryGetWeapon()->GetWeaponStatComponent()->GetWeaponType()) { case EMyWeaponType::Range: UE_LOG(LogTemp, Warning, TEXT("Range Attack")); // todo: unbind the fire when player drops. - OnAttackEndedHandle = GetWeapon()->BindOnFireReady(this, &AMyCharacter::ResetAttack); + OnAttackEndedHandle = TryGetWeapon()->BindOnFireReady(this, &AMyCharacter::ResetAttack); - if (GetWeapon()->GetWeaponStatComponent()->IsHitscan()) + if (TryGetWeapon()->GetWeaponStatComponent()->IsHitscan()) { UE_LOG(LogTemp, Warning, TEXT("Hitscan Attack")); @@ -513,7 +523,7 @@ void AMyCharacter::ResetAttack() OnAttackEnded.Broadcast(); GetCharacterMovement()->MaxWalkSpeed = 600.f; - GetWeapon()->UnbindOnFireReady(OnAttackEndedHandle); + TryGetWeapon()->UnbindOnFireReady(OnAttackEndedHandle); } void AMyCharacter::Server_Interactive_Implementation() @@ -599,9 +609,9 @@ void AMyCharacter::Server_Use_Implementation() void AMyCharacter::UseImpl() { - if (IsValid(GetCurrentItem())) + if (IsValid(TryGetItem())) { - GetCurrentItem()->Use(this); + TryGetItem()->Use(this); } else { @@ -630,51 +640,53 @@ void AMyCharacter::UseInterruptImpl() const OnUseInterrupted.Broadcast(); } -void AMyCharacter::OnWeaponChanged(AMyPlayerState* ThisPlayerState) +void AMyCharacter::OnHandChanged(AMyPlayerState* ThisPlayerState) { LOG_FUNC(LogTemp, Warning, "Weapon change caught"); - const auto& Weapon = ThisPlayerState->GetWeapon(); + const auto& Collectable = ThisPlayerState->GetCurrentHand(); - if (IsValid(Weapon)) + if (IsValid(Collectable)) { - Weapon->AttachToComponent + Collectable->AttachToComponent ( GetMesh(), - FAttachmentTransformRules::SnapToTargetIncludingScale, + FAttachmentTransformRules::SnapToTargetNotIncludingScale, AMyCharacter::RightHandSocketName ); ExecuteServer ( this, - &AMyCharacter::Server_AttachArmWeapon, - &AMyCharacter::AttachArmWeaponImpl + &AMyCharacter::Server_AttachArmCollectable, + &AMyCharacter::AttachArmCollectableImpl ); } else { - for (const auto& Child : GetMesh()->GetAttachChildren()) + const auto& List = GetMesh()->GetAttachChildren(); + + for (size_t i = 0; i < List.Num(); ++i) { - if (Child->GetAttachSocketName() == AMyCharacter::RightHandSocketName) + if (List[i]->GetAttachSocketName() == AMyCharacter::RightHandSocketName) { - Child->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform); + List[i]->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform); } } if (IsLocallyControlled()) { - if (IsValid(HandWeapon)) + if (IsValid(HandCollectable)) { - HandWeapon->Destroy(); - HandWeapon = nullptr; + HandCollectable->Destroy(); + HandCollectable = nullptr; } } } } -void AMyCharacter::Server_AttachArmWeapon_Implementation() +void AMyCharacter::Server_AttachArmCollectable_Implementation() { - AttachArmWeaponImpl(); + AttachArmCollectableImpl(); } int32 AMyCharacter::GetDamage() const @@ -686,9 +698,9 @@ int32 AMyCharacter::GetDamage() const return 0; } - if (IsValid(GetWeapon())) + if (IsValid(TryGetWeapon())) { - return State->GetDamage() + GetWeapon()->GetDamage(); + return State->GetDamage() + TryGetWeapon()->GetDamage(); } return State->GetDamage(); @@ -748,32 +760,32 @@ void AMyCharacter::OnAttackAnimNotify() ); } -void AMyCharacter::AttachArmWeaponImpl() +void AMyCharacter::AttachArmCollectableImpl() { - LOG_FUNC(LogTemp, Warning, "AttachArmWeaponImpl"); + LOG_FUNC(LogTemp, Warning, "AttachArmCollectableImpl"); const auto& MyPlayerState = GetPlayerState(); if (IsValid(MyPlayerState)) { - const auto& Weapon = GetWeapon(); + const auto& Collectable = MyPlayerState->GetCurrentHand(); - HandWeapon = GetWorld()->SpawnActor(Weapon->GetClass()); - HandWeapon->GetMesh()->SetSimulatePhysics(false); + HandCollectable = GetWorld()->SpawnActor(Collectable->GetClass()); + HandCollectable->GetMesh()->SetSimulatePhysics(false); - if (HandWeapon->GetMesh()->AttachToComponent + if (HandCollectable->GetMesh()->AttachToComponent ( ArmMeshComponent, - FAttachmentTransformRules::SnapToTargetIncludingScale, + FAttachmentTransformRules::SnapToTargetNotIncludingScale, AMyCharacter::RightHandSocketName )) { - HandWeapon->SetOwner(this); - HandWeapon->bOnlyRelevantToOwner = true; - HandWeapon->SetReplicates(true); - HandWeapon->GetMesh()->SetVisibility(true); - HandWeapon->GetMesh()->SetOnlyOwnerSee(true); - HandWeapon->GetMesh()->SetCastShadow(false); + HandCollectable->SetOwner(this); + HandCollectable->bOnlyRelevantToOwner = true; + HandCollectable->SetReplicates(true); + HandCollectable->GetMesh()->SetVisibility(true); + HandCollectable->GetMesh()->SetOnlyOwnerSee(true); + HandCollectable->GetMesh()->SetCastShadow(false); LOG_FUNC(LogTemp, Warning, "Weapon attached to arm"); } diff --git a/Source/MyProject/MyCharacter.h b/Source/MyProject/MyCharacter.h index 2978cef8..64d06b3f 100644 --- a/Source/MyProject/MyCharacter.h +++ b/Source/MyProject/MyCharacter.h @@ -41,14 +41,14 @@ class MYPROJECT_API AMyCharacter : public ACharacter DECL_BINDON(OnUseInterrupted) DECL_BINDON(OnInteractInterrupted) + class AMyWeapon* TryGetWeapon() const; + class AMyItem* TryGetItem() const; class UMyInventoryComponent* GetInventory() const; class UMyStatComponent* GetStatComponent() const; - class AMyWeapon* GetWeapon() const; - class AMyCollectable* GetCurrentItem() const; float GetPitchInput() const { return PitchInput; } - void OnWeaponChanged(class AMyPlayerState* ThisPlayerState); + void OnHandChanged(class AMyPlayerState* ThisPlayerState); protected: // Called when the game starts or when spawned @@ -151,9 +151,9 @@ class MYPROJECT_API AMyCharacter : public ACharacter // ============ End of Using ============ UFUNCTION(Server, Reliable) - void Server_AttachArmWeapon(); + void Server_AttachArmCollectable(); - void AttachArmWeaponImpl(); + void AttachArmCollectableImpl(); void Yaw(const float Value); void Pitch(const float Value); @@ -201,7 +201,7 @@ class MYPROJECT_API AMyCharacter : public ACharacter class UMyAnimInstance* ArmAnimInstance; UPROPERTY(VisibleAnywhere, Replicated) - class AMyWeapon* HandWeapon; + class AMyCollectable* HandCollectable; FOnAttackStarted OnAttackStarted; diff --git a/Source/MyProject/MyCollectable.cpp b/Source/MyProject/MyCollectable.cpp index d52b9dd4..917d9947 100644 --- a/Source/MyProject/MyCollectable.cpp +++ b/Source/MyProject/MyCollectable.cpp @@ -4,6 +4,7 @@ #include "MyCollectable.h" #include "MyCharacter.h" +#include "MyPlayerState.h" #include "Components/BoxComponent.h" diff --git a/Source/MyProject/MyFragGrenade.cpp b/Source/MyProject/MyFragGrenade.cpp new file mode 100644 index 00000000..06d62e45 --- /dev/null +++ b/Source/MyProject/MyFragGrenade.cpp @@ -0,0 +1,117 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MyProject/MyFragGrenade.h" + +#include "MyCharacter.h" +#include "MyPlayerController.h" +#include "MyPlayerState.h" +#include "MyStatComponent.h" + +AMyFragGrenade::AMyFragGrenade() : IsThrown(false), IsExploded(false) +{ + PrimaryActorTick.bCanEverTick = true; + + static ConstructorHelpers::FObjectFinder SK_Grenade(TEXT("SkeletalMesh'/Game/FPS_Weapon_Bundle/Weapons/Meshes/G67_Grenade/SK_G67_X.SK_G67_X'")); + + SetSkeletalMesh(); + + if (SK_Grenade.Succeeded()) + { + GetSkeletalMeshComponent()->SetSkeletalMesh(SK_Grenade.Object); + } +} + +bool AMyFragGrenade::AttackImpl() +{ + const auto& Result = Super::AttackImpl(); + + if (Result) + { + Throw(); + } + + return Result; +} + +bool AMyFragGrenade::ReloadImpl() +{ + return false; +} + +void AMyFragGrenade::Throw() +{ + ExecuteServer(this, + &AMyFragGrenade::Multi_Throw, + &AMyFragGrenade::ThrowImpl); +} + +void AMyFragGrenade::Multi_Throw_Implementation() +{ + ThrowImpl(); +} + +void AMyFragGrenade::ThrowImpl() +{ + if (IsExploded || IsThrown) + { + return; + } + + // todo: object will be thrown at the bottom of the character + Drop(); + + GetSkeletalMeshComponent()->AddImpulse(FVector::ForwardVector * 1000.f, NAME_None, true); + + GetWorldTimerManager().SetTimer + ( + OnExplosionTimerExpiredHandle, + this, + &AMyFragGrenade::OnExplosionTimerExpired, + 3.f, + false + ); + + IsThrown = true; +} + +void AMyFragGrenade::OnExplosionTimerExpired() +{ + IsExploded = true; + + // todo: explosion effect + + if (HasAuthority()) + { + TArray HitResults; + + GetWorld()->OverlapMultiByChannel + ( + OUT HitResults, + GetActorLocation(), + FQuat::Identity, + ECollisionChannel::ECC_Pawn, + FCollisionShape::MakeSphere(500.f) + ); + + for (const auto& Result : HitResults) + { + if (const auto& Character = Cast(Result.GetActor())) + { + const auto& Distance = FVector::Distance(Character->GetActorLocation(), GetActorLocation()); + const auto& Ratio = 1.f - Distance / 500.f; + const auto& Damage = 100.f * Ratio; + + Character->GetPlayerState()->TakeDamage + ( + Damage, + {}, + Cast(GetItemOwner()->GetOwner()), + GetItemOwner() + ); + } + } + + Destroy(true); + } +} diff --git a/Source/MyProject/MyFragGrenade.h b/Source/MyProject/MyFragGrenade.h new file mode 100644 index 00000000..dad88d41 --- /dev/null +++ b/Source/MyProject/MyFragGrenade.h @@ -0,0 +1,45 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MyWeapon.h" + +#include "MyProject/MyItem.h" +#include "MyFragGrenade.generated.h" + +/** + * + */ +UCLASS() +class MYPROJECT_API AMyFragGrenade : public AMyWeapon +{ + GENERATED_BODY() + +public: + AMyFragGrenade(); + +protected: + virtual bool AttackImpl() override; + + virtual bool ReloadImpl() override; + +private: + void Throw(); + + UFUNCTION(NetMulticast, Reliable) + void Multi_Throw(); + + void ThrowImpl(); + + void OnExplosionTimerExpired(); + + UPROPERTY(VisibleAnywhere) + bool IsThrown; + + UPROPERTY(VisibleAnywhere) + bool IsExploded; + + FTimerHandle OnExplosionTimerExpiredHandle; + +}; diff --git a/Source/MyProject/MyGameState.cpp b/Source/MyProject/MyGameState.cpp index 34b71de2..6d0cabc9 100644 --- a/Source/MyProject/MyGameState.cpp +++ b/Source/MyProject/MyGameState.cpp @@ -9,6 +9,7 @@ #include "MyCollectable.h" #include "MyInGameHUD.h" #include "MyInGameWidget.h" +#include "MyInventoryComponent.h" #include "MyPlayerController.h" #include "MyPlayerState.h" #include "MyProjectGameModeBase.h" @@ -258,17 +259,7 @@ void AMyGameState::HandlePlayerStateChanged(AMyPlayerState* PlayerState, const E FRotator::ZeroRotator ); - if (Character) - { - if (const auto& Weapon = Character->GetWeapon()) - { - Weapon->Drop(); - } - if (const auto& Item = Character->GetCurrentItem()) - { - Item->Drop(); - } - } + PlayerState->GetInventoryComponent()->DropAll(); const auto& PlayerController = Cast(PlayerState->GetOwner()); diff --git a/Source/MyProject/MyInventoryComponent.cpp b/Source/MyProject/MyInventoryComponent.cpp index bf916be1..fcf8501c 100644 --- a/Source/MyProject/MyInventoryComponent.cpp +++ b/Source/MyProject/MyInventoryComponent.cpp @@ -42,7 +42,6 @@ bool UMyInventoryComponent::TryAddItem(AMyCollectable* Item) else { LOG_FUNC(LogTemp, Warning, "Inventory is full"); - UE_LOG(LogTemp, Warning, TEXT("Inventory is full")); return false; } } @@ -80,6 +79,24 @@ bool UMyInventoryComponent::Find(AMyCollectable* MyCollectable) const return Inventory.Find(MyCollectable) != INDEX_NONE; } +void UMyInventoryComponent::Clear() +{ + for (int32 i = 0; i < Inventory.Num(); ++i) + { + Inventory[i]->Destroy(); + Inventory[i] = nullptr; + } +} + +void UMyInventoryComponent::DropAll() +{ + for (int32 i = 0; i < Inventory.Num(); ++i) + { + Inventory[i]->Drop(); + Inventory[i] = nullptr; + } +} + void UMyInventoryComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); diff --git a/Source/MyProject/MyInventoryComponent.h b/Source/MyProject/MyInventoryComponent.h index 06f83d75..42499e27 100644 --- a/Source/MyProject/MyInventoryComponent.h +++ b/Source/MyProject/MyInventoryComponent.h @@ -59,6 +59,9 @@ class MYPROJECT_API UMyInventoryComponent : public UActorComponent bool Find(AMyCollectable* MyCollectable) const; + void Clear(); + void DropAll(); + protected: virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; diff --git a/Source/MyProject/MyPlayerState.cpp b/Source/MyProject/MyPlayerState.cpp index 34773961..296d9157 100644 --- a/Source/MyProject/MyPlayerState.cpp +++ b/Source/MyProject/MyPlayerState.cpp @@ -29,8 +29,7 @@ AMyPlayerState::AMyPlayerState() Assist(0), Health(0), Money(0), - Weapon(nullptr), - CurrentItem(nullptr) + CurrentHand(nullptr) { StatComponent = CreateDefaultSubobject(TEXT("StatComponent")); InventoryComponent = CreateDefaultSubobject(TEXT("InventoryComponent")); @@ -97,17 +96,13 @@ void AMyPlayerState::Reset() if (State != EMyCharacterState::Alive) { - if (Weapon && !Weapon->GetItemOwner()) + if (CurrentHand && !CurrentHand->GetItemOwner()) { - Weapon->Destroy(); - Weapon = nullptr; + CurrentHand->Destroy(); + CurrentHand = nullptr; } - if (CurrentItem && !CurrentItem->GetItemOwner()) - { - CurrentItem->Destroy(); - CurrentItem = nullptr; - } + InventoryComponent->Clear(); } SetState(EMyCharacterState::Alive); @@ -143,8 +138,7 @@ void AMyPlayerState::GetLifetimeReplicatedProps(TArray& OutLi DOREPLIFETIME(AMyPlayerState, Money); DOREPLIFETIME(AMyPlayerState, InventoryComponent); DOREPLIFETIME(AMyPlayerState, StatComponent); - DOREPLIFETIME(AMyPlayerState, Weapon); - DOREPLIFETIME(AMyPlayerState, CurrentItem); + DOREPLIFETIME(AMyPlayerState, CurrentHand); } void AMyPlayerState::OnRep_HealthChanged() const @@ -152,9 +146,9 @@ void AMyPlayerState::OnRep_HealthChanged() const OnHPChanged.Broadcast(GetHPRatio()); } -void AMyPlayerState::OnRep_WeaponChanged() +void AMyPlayerState::OnRep_HandChanged() { - OnWeaponChanged.Broadcast(this); + OnHandChanged.Broadcast(this); } void AMyPlayerState::Client_OnDamageTaken_Implementation(AMyPlayerState* DamageGiver) @@ -265,13 +259,21 @@ void AMyPlayerState::AddMoney(const int32 Amount) } } -void AMyPlayerState::SetWeapon(AMyWeapon* NewWeapon) +void AMyPlayerState::SetCurrentWeapon(AMyWeapon* NewWeapon) { if (HasAuthority()) { - LOG_FUNC_PRINTF(LogTemp, Warning, "SetWeapon: %s", *NewWeapon->GetName()); - Weapon = NewWeapon; - OnWeaponChanged.Broadcast(this); + if (NewWeapon) + { + LOG_FUNC_PRINTF(LogTemp, Warning, "SetCurrentWeapon: %s", *NewWeapon->GetName()); + } + else + { + LOG_FUNC(LogTemp, Warning, "SetCurrentWeapon: nullptr"); + } + + CurrentHand = NewWeapon; + OnHandChanged.Broadcast(this); } } @@ -279,7 +281,17 @@ void AMyPlayerState::SetCurrentItem(AMyCollectable* NewItem) { if (HasAuthority()) { - CurrentItem = NewItem; + if (NewItem) + { + LOG_FUNC_PRINTF(LogTemp, Warning, "SetCurrentItem: %s", *NewItem->GetName()); + } + else + { + LOG_FUNC(LogTemp, Warning, "SetCurrentItem: nullptr"); + } + + CurrentHand = NewItem; + OnHandChanged.Broadcast(this); } } \ No newline at end of file diff --git a/Source/MyProject/MyPlayerState.h b/Source/MyProject/MyPlayerState.h index 786f709b..ea702db1 100644 --- a/Source/MyProject/MyPlayerState.h +++ b/Source/MyProject/MyPlayerState.h @@ -40,15 +40,14 @@ class MYPROJECT_API AMyPlayerState : public APlayerState class UMyStatComponent* GetStatComponent() const { return StatComponent; } class UMyInventoryComponent* GetInventoryComponent() const { return InventoryComponent; } - class AMyWeapon* GetWeapon() const { return Weapon; } - class AMyCollectable* GetCurrentItem() const { return CurrentItem; } + class AMyCollectable* GetCurrentHand() const { return CurrentHand; } void Use(const int32 Index); void SetState(const EMyCharacterState NewState); void SetHP(const int32 NewHP); void AddMoney(const int32 Amount); - void SetWeapon(class AMyWeapon* NewWeapon); + void SetCurrentWeapon(class AMyWeapon* NewWeapon); void SetCurrentItem(class AMyCollectable* NewItem); FORCEINLINE float GetHP() const @@ -61,7 +60,7 @@ class MYPROJECT_API AMyPlayerState : public APlayerState DECL_BINDON(OnHPChanged, float) DECL_BINDON(OnMoneyChanged, int32) DECL_BINDON(OnStateChanged, class AMyPlayerState*, EMyCharacterState) - DECL_BINDON(OnWeaponChanged, class AMyPlayerState*) + DECL_BINDON(OnHandChanged, class AMyPlayerState*) DECL_BINDON(OnKillOccurred, class AMyPlayerState*, class AMyPlayerState*, const class AMyWeapon*) FORCEINLINE int32 GetMoney() const { return Money; } @@ -93,7 +92,7 @@ class MYPROJECT_API AMyPlayerState : public APlayerState void OnRep_HealthChanged() const; UFUNCTION() - void OnRep_WeaponChanged(); + void OnRep_HandChanged(); void SetTeam(const EMyTeam NewTeam) { @@ -136,11 +135,8 @@ class MYPROJECT_API AMyPlayerState : public APlayerState UPROPERTY(VisibleAnywhere, Replicated) class UMyInventoryComponent* InventoryComponent; - UPROPERTY(VisibleAnywhere, ReplicatedUsing=OnRep_WeaponChanged) - class AMyWeapon* Weapon; - - UPROPERTY(Replicated, VisibleAnywhere) - class AMyCollectable* CurrentItem; + UPROPERTY(VisibleAnywhere, ReplicatedUsing=OnRep_HandChanged) + class AMyCollectable* CurrentHand; FOnDamageTaken OnDamageTaken; @@ -148,7 +144,7 @@ class MYPROJECT_API AMyPlayerState : public APlayerState FOnHPChanged OnHPChanged; - FOnWeaponChanged OnWeaponChanged; + FOnWeaponChanged OnHandChanged; FOnKillOccurred OnKillOccurred; diff --git a/Source/MyProject/MyProjectGameModeBase.cpp b/Source/MyProject/MyProjectGameModeBase.cpp index 8fd0e882..8b8a4358 100644 --- a/Source/MyProject/MyProjectGameModeBase.cpp +++ b/Source/MyProject/MyProjectGameModeBase.cpp @@ -123,23 +123,21 @@ void AMyProjectGameModeBase::RestartPlayer(AController* NewPlayer) if (IsValid(PlayerState)) { - PlayerState->BindOnWeaponChanged(Character, &AMyCharacter::OnWeaponChanged); + PlayerState->BindOnHandChanged(Character, &AMyCharacter::OnHandChanged); } - if (const auto& Weapon = PlayerState->GetWeapon()) + if (const auto& Collectable = PlayerState->GetCurrentHand()) { if (IsValid(Character)) { - PlayerState->SetWeapon(Weapon); - } - } - - if (const auto& Item = PlayerState->GetCurrentItem()) - { - if (IsValid(Character)) - { - // todo: need to be attaching to character - PlayerState->SetCurrentItem(Item); + if (const auto& Weapon = Cast(Collectable)) + { + PlayerState->SetCurrentWeapon(Weapon); + } + else + { + PlayerState->SetCurrentItem(Collectable); + } } } } diff --git a/Source/MyProject/MyWeapon.cpp b/Source/MyProject/MyWeapon.cpp index 9d60b66c..6b1a9ac4 100644 --- a/Source/MyProject/MyWeapon.cpp +++ b/Source/MyProject/MyWeapon.cpp @@ -62,7 +62,7 @@ bool AMyWeapon::PostInteract(AMyCharacter* Character) { if (const auto& PlayerState = Character->GetPlayerState()) { - PlayerState->SetWeapon(this); + PlayerState->SetCurrentWeapon(this); } } @@ -94,7 +94,7 @@ void AMyWeapon::DropImpl() { if (const auto& PlayerState = GetItemOwner()->GetPlayerState()) { - PlayerState->SetWeapon(nullptr); + PlayerState->SetCurrentWeapon(nullptr); } } }