diff --git a/Repsi.uproject b/Repsi.uproject index 997386a..de086b6 100644 --- a/Repsi.uproject +++ b/Repsi.uproject @@ -1,13 +1,13 @@ -{ - "FileVersion": 3, - "EngineAssociation": "4.25", - "Category": "", - "Description": "", - "Modules": [ - { - "Name": "RepsiCore", - "Type": "Runtime", - "LoadingPhase": "Default" - } - ] -} +{ + "FileVersion": 3, + "EngineAssociation": "5.6", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "RepsiCore", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/Source/Repsi.Target.cs b/Source/Repsi.Target.cs index 1542fad..85cb5db 100644 --- a/Source/Repsi.Target.cs +++ b/Source/Repsi.Target.cs @@ -1,11 +1,12 @@ -using UnrealBuildTool; - -public class RepsiTarget : TargetRules -{ - public RepsiTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Game; - DefaultBuildSettings = BuildSettingsVersion.V2; - ExtraModuleNames.AddRange( new string[] { "RepsiCore" } ); - } -} +using UnrealBuildTool; + +public class RepsiTarget : TargetRules +{ + public RepsiTarget(TargetInfo Target) : base(Target) + { + Type = TargetType.Game; + IncludeOrderVersion = EngineIncludeOrderVersion.Latest; + DefaultBuildSettings = BuildSettingsVersion.V5; + ExtraModuleNames.AddRange( new string[] { "RepsiCore" } ); + } +} diff --git a/Source/RepsiCore/Private/TargetSphere.cpp b/Source/RepsiCore/Private/TargetSphere.cpp index d3b029a..092ade9 100644 --- a/Source/RepsiCore/Private/TargetSphere.cpp +++ b/Source/RepsiCore/Private/TargetSphere.cpp @@ -1,103 +1,105 @@ -#include "TargetSphere.h" - -#include "Net/UnrealNetwork.h" -#include "UObject/ConstructorHelpers.h" -#include "Engine/EngineTypes.h" -#include "Engine/StaticMesh.h" -#include "Materials/MaterialInterface.h" -#include "Components/SceneComponent.h" -#include "Components/StaticMeshComponent.h" - -#include "DamageType_WeaponFire.h" -#include "RepsiPawn.h" - -ATargetSphere::ATargetSphere(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - static ConstructorHelpers::FObjectFinder MeshFinder(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'")); - static ConstructorHelpers::FObjectFinder MaterialFinder(TEXT("Material'/Game/Assets/TargetSphere/M_TargetSphere.M_TargetSphere'")); - - // Enable replication, and set a relatively short cull distance for testing - bReplicates = true; - NetCullDistanceSquared = FMath::Square(1500.0f); - - // This actor will tick for a moment after it's shot, so it can animate its color - PrimaryActorTick.bCanEverTick = true; - PrimaryActorTick.bStartWithTickEnabled = false; - ColorChangeDuration = 0.333f; - - // Make our Actor damageable initially, so Weapon traces will deal damage - SetCanBeDamaged(true); - - RootComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("RootComponent")); - - MeshComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("MeshComponent")); - MeshComponent->SetupAttachment(RootComponent); - MeshComponent->SetStaticMesh(MeshFinder.Object); - MeshComponent->SetMaterial(0, MaterialFinder.Object); -} - -void ATargetSphere::PostInitializeComponents() -{ - Super::PostInitializeComponents(); - - MeshMID = MeshComponent->CreateDynamicMaterialInstance(0); -} - -float ATargetSphere::InternalTakePointDamage(float Damage, FPointDamageEvent const& PointDamageEvent, AController* EventInstigator, AActor* DamageCauser) -{ - // If shot with a Weapon, accept the damage event and randomize the sphere's Color - if (PointDamageEvent.DamageTypeClass == UDamageType_WeaponFire::StaticClass()) - { - const ARepsiPawn* Pawn = EventInstigator ? EventInstigator->GetPawn() : nullptr; - if (Pawn) - { - Color = Pawn->Color; - OnRep_Color(); - } - return Damage; - } - return Super::InternalTakePointDamage(Damage, PointDamageEvent, EventInstigator, DamageCauser); -} - -void ATargetSphere::Tick(float DeltaSeconds) -{ - Super::Tick(DeltaSeconds); - - // Compute our relative progress in the color change animation - const float CurrentTime = GetWorld()->GetTimeSeconds(); - const float ColorChangeElapsed = CurrentTime - LastColorChangeTime; - const float ColorChangeAlpha = ColorChangeDuration < KINDA_SMALL_NUMBER ? 1.0f : FMath::Min(1.0f, ColorChangeElapsed / ColorChangeDuration); - - // Start interpolating toward a brighter version of new color, settling on - // the unmodified color at the end - const FLinearColor TargetColor = Color * FMath::Lerp(10.0f, 1.0f, ColorChangeAlpha); - const FLinearColor NewColor = FLinearColor::LerpUsingHSV(PreviousColor, TargetColor, ColorChangeAlpha); - if (MeshMID) - { - MeshMID->SetVectorParameterValue(TEXT("Color"), NewColor); - } - - // Disable ticking once the animation is finished - if (ColorChangeAlpha >= 1.0f) - { - SetActorTickEnabled(false); - } -} - -void ATargetSphere::OnRep_Color() -{ - if (MeshMID) - { - PreviousColor = MeshMID->K2_GetVectorParameterValue(TEXT("Color")); - } - LastColorChangeTime = GetWorld()->GetTimeSeconds(); - SetActorTickEnabled(true); -} - -void ATargetSphere::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const -{ - Super::GetLifetimeReplicatedProps(OutLifetimeProps); - - DOREPLIFETIME(ATargetSphere, Color); -} +#include "TargetSphere.h" + +#include "Net/UnrealNetwork.h" +#include "UObject/ConstructorHelpers.h" +#include "Engine/EngineTypes.h" +#include "Engine/StaticMesh.h" +#include "Materials/MaterialInterface.h" +#include "Components/SceneComponent.h" +#include "Components/StaticMeshComponent.h" +#include "GameFramework/DamageType.h" // Add this include for FPointDamageEvent +#include "Engine/DamageEvents.h" // Include this header for FPointDamageEvent + +#include "DamageType_WeaponFire.h" +#include "RepsiPawn.h" + +ATargetSphere::ATargetSphere(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + static ConstructorHelpers::FObjectFinder MeshFinder(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'")); + static ConstructorHelpers::FObjectFinder MaterialFinder(TEXT("Material'/Game/Assets/TargetSphere/M_TargetSphere.M_TargetSphere'")); + + // Enable replication, and set a relatively short cull distance for testing + bReplicates = true; + NetCullDistanceSquared = FMath::Square(1500.0f); + + // This actor will tick for a moment after it's shot, so it can animate its color + PrimaryActorTick.bCanEverTick = true; + PrimaryActorTick.bStartWithTickEnabled = false; + ColorChangeDuration = 0.333f; + + // Make our Actor damageable initially, so Weapon traces will deal damage + SetCanBeDamaged(true); + + RootComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("RootComponent")); + + MeshComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("MeshComponent")); + MeshComponent->SetupAttachment(RootComponent); + MeshComponent->SetStaticMesh(MeshFinder.Object); + MeshComponent->SetMaterial(0, MaterialFinder.Object); +} + +void ATargetSphere::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + + MeshMID = MeshComponent->CreateDynamicMaterialInstance(0); +} + +float ATargetSphere::InternalTakePointDamage(float Damage, FPointDamageEvent const& PointDamageEvent, AController* EventInstigator, AActor* DamageCauser) +{ + // If shot with a Weapon, accept the damage event and randomize the sphere's Color + if (PointDamageEvent.DamageTypeClass == UDamageType_WeaponFire::StaticClass()) + { + const ARepsiPawn* Pawn = EventInstigator ? EventInstigator->GetPawn() : nullptr; + if (Pawn) + { + Color = Pawn->Color; + OnRep_Color(); + } + return Damage; + } + return Super::InternalTakePointDamage(Damage, PointDamageEvent, EventInstigator, DamageCauser); +} + +void ATargetSphere::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + // Compute our relative progress in the color change animation + const float CurrentTime = GetWorld()->GetTimeSeconds(); + const float ColorChangeElapsed = CurrentTime - LastColorChangeTime; + const float ColorChangeAlpha = ColorChangeDuration < KINDA_SMALL_NUMBER ? 1.0f : FMath::Min(1.0f, ColorChangeElapsed / ColorChangeDuration); + + // Start interpolating toward a brighter version of new color, settling on + // the unmodified color at the end + const FLinearColor TargetColor = Color * FMath::Lerp(10.0f, 1.0f, ColorChangeAlpha); + const FLinearColor NewColor = FLinearColor::LerpUsingHSV(PreviousColor, TargetColor, ColorChangeAlpha); + if (MeshMID) + { + MeshMID->SetVectorParameterValue(TEXT("Color"), NewColor); + } + + // Disable ticking once the animation is finished + if (ColorChangeAlpha >= 1.0f) + { + SetActorTickEnabled(false); + } +} + +void ATargetSphere::OnRep_Color() +{ + if (MeshMID) + { + PreviousColor = MeshMID->K2_GetVectorParameterValue(TEXT("Color")); + } + LastColorChangeTime = GetWorld()->GetTimeSeconds(); + SetActorTickEnabled(true); +} + +void ATargetSphere::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + DOREPLIFETIME(ATargetSphere, Color); +} diff --git a/Source/RepsiCore/Private/Weapon.cpp b/Source/RepsiCore/Private/Weapon.cpp index afdaf38..65c6b74 100644 --- a/Source/RepsiCore/Private/Weapon.cpp +++ b/Source/RepsiCore/Private/Weapon.cpp @@ -1,301 +1,302 @@ -#include "Weapon.h" - -#include "Net/UnrealNetwork.h" -#include "UObject/ConstructorHelpers.h" -#include "Engine/World.h" -#include "Engine/EngineTypes.h" -#include "Engine/StaticMesh.h" -#include "Engine/CollisionProfile.h" -#include "Materials/MaterialInterface.h" -#include "Particles/ParticleSystem.h" -#include "Sound/SoundBase.h" -#include "Components/SceneComponent.h" -#include "Components/StaticMeshComponent.h" -#include "GameFramework/Pawn.h" -#include "Kismet/GameplayStatics.h" - -#include "Log.h" -#include "DamageType_WeaponFire.h" - -AWeapon::AWeapon(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - static ConstructorHelpers::FObjectFinder MeshFinder(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'")); - static ConstructorHelpers::FObjectFinder MaterialFinder(TEXT("Material'/Game/Assets/Weapon/M_Weapon.M_Weapon'")); - - static ConstructorHelpers::FObjectFinder FireEffectFinder(TEXT("ParticleSystem'/Game/Assets/Weapon/PS_MuzzleFlash.PS_MuzzleFlash'")); - static ConstructorHelpers::FObjectFinder ImpactEffectFinder(TEXT("ParticleSystem'/Game/Assets/Weapon/PS_WeaponImpact.PS_WeaponImpact'")); - - static ConstructorHelpers::FObjectFinder FireSoundFinder(TEXT("SoundCue'/Game/Audio/A_Weapon_Fire.A_Weapon_Fire'")); - static ConstructorHelpers::FObjectFinder DryFireSoundFinder(TEXT("SoundCue'/Game/Audio/A_Weapon_DryFire.A_Weapon_DryFire'")); - static ConstructorHelpers::FObjectFinder DamagingImpactSoundFinder(TEXT("SoundCue'/Game/Audio/A_WeaponImpact_Damaging.A_WeaponImpact_Damaging'")); - static ConstructorHelpers::FObjectFinder NonDamagingImpactSoundFinder(TEXT("SoundCue'/Game/Audio/A_WeaponImpact_NonDamaging.A_WeaponImpact_NonDamaging'")); - - PrimaryActorTick.bCanEverTick = true; - - // Define default cooldown/firing properties - FireCooldown = 0.4f; - LastFireTime = TNumericLimits::Lowest(); - - // Define default values for aiming properties - AimInterpSpeed = 8.0f; - DropInterpSpeed = 10.0f; - DropRotation = FRotator(-30.0f, -80.0f, 0.0f); - - // Make sure that weapons will be replicated as long as their owning Pawn - // is replicated - bReplicates = true; - bNetUseOwnerRelevancy = true; - - // Create a static mesh, as well as a SceneComponent at the location of the - // muzzle (i.e. where line traces would originate from, and where effects - // would spawn) - RootComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("RootComponent")); - - MeshComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("MeshComponent")); - MeshComponent->SetupAttachment(RootComponent); - MeshComponent->SetStaticMesh(MeshFinder.Object); - MeshComponent->SetMaterial(0, MaterialFinder.Object); - MeshComponent->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName); - MeshComponent->SetRelativeLocation(FVector(20.0f, 0.0f, 0.0f)); - MeshComponent->SetRelativeRotation(FRotator(0.0f, 0.0f, 45.0f)); - MeshComponent->SetRelativeScale3D(FVector(0.5f, 0.15f, 0.15f)); - - MuzzleHandle = ObjectInitializer.CreateDefaultSubobject(this, TEXT("MuzzleHandle")); - MuzzleHandle->SetupAttachment(RootComponent); - MuzzleHandle->SetRelativeLocation(FVector(50.0f, 0.0f, 0.0f)); - - // Cache asset references for visual effects and audio - FireEffect = FireEffectFinder.Object; - ImpactEffect = ImpactEffectFinder.Object; - FireSound = FireSoundFinder.Object; - DryFireSound = DryFireSoundFinder.Object; - DamagingImpactSound = DamagingImpactSoundFinder.Object; - NonDamagingImpactSound = NonDamagingImpactSoundFinder.Object; -} - -void AWeapon::GatherCurrentMovement() -{ -} - -void AWeapon::Tick(float DeltaSeconds) -{ - Super::Tick(DeltaSeconds); - - if (bAimLocationIsValid) - { - const FVector AimDisplacement = AimLocation - GetActorLocation(); - const FVector AimDirection = AimDisplacement.GetSafeNormal(); - - const FQuat TargetRotation = AimDirection.ToOrientationQuat(); - const FQuat NewRotation = FMath::QInterpTo(GetActorQuat(), TargetRotation, DeltaSeconds, AimInterpSpeed); - SetActorRotation(NewRotation); - } - else - { - AActor* AttachParent = GetAttachParentActor(); - const FQuat TargetRotation = AttachParent ? AttachParent->GetActorTransform().TransformRotation(FQuat(DropRotation)) : FQuat(DropRotation); - const FQuat NewRotation = FMath::QInterpTo(GetActorQuat(), TargetRotation, DeltaSeconds, DropInterpSpeed); - SetActorRotation(NewRotation); - } -} - -void AWeapon::UpdateAimLocation(const FVector& InWorldAimLocation, const FVector& InViewAimLocation) -{ - AimLocation = InWorldAimLocation; - bAimLocationIsValid = InViewAimLocation.X > MuzzleHandle->GetRelativeLocation().X; -} - -void AWeapon::HandleFireInput() -{ - // This function is being called on the local client to let us know that - // they've pressed the fire button. We'll do an initial client-side cooldown - // check just to avoid spamming the server with unnecessary RPCs, but this - // isn't an authoritative cooldown check: that's up to the server - const float CurrentTime = GetWorld()->GetTimeSeconds(); - const float ElapsedSinceLastFire = CurrentTime - LastFireTime; - if (ElapsedSinceLastFire >= FireCooldown) - { - // Issue a Server RPC to notify the server that the client wants to - // fire: the server will then make its own authoritative decision and - // update game state accordingly. Note that if we're playing standalone, - // this call just runs locally: it doesn't travel over the network, but - // the end result is the same (and technically the separate client and - // server cooldown checks are redundant in that case, but that's not - // significant enough to warrant special-case logic for single-player) - const FVector MuzzleLocation = MuzzleHandle->GetComponentLocation(); - const FVector Direction = MuzzleHandle->GetComponentQuat().Vector(); - Server_TryFire(MuzzleLocation, Direction); - LastFireTime = CurrentTime; - - // If this weapon, belonging to the locally controlled player, has - // authority, that means we're either running standalone or as a listen - // server. In that case, Server_TryFire will execute right away, and it - // will spawn cosmetic effects right away. But if we *don't* have - // authority, we're running as a client, which means there's latency - // between when the player fires the weapon and when the server decides - // where the shot lands. So if we're a client, we want to spawn cosmetic - // effects (particles, sound) right away, rather than waiting for the - // server to tell us with 100% certainty that we've successfully fired). - // This means our effects are non-authoritative (i.e. we're speculating - // about what we hit and hoping that we're right, but the server has the - // final say and might disagree), but it gives the player immediate - // visual feedback, without which their game would feel laggy. - if (!HasAuthority()) - { - PlayFireEffects(); - - // Run a cosmetic line trace just to see whether we should spawn an - // impact effect - FHitResult Hit; - if (RunFireTrace(Hit)) - { - const bool bWillProbablyCauseDamage = Hit.Actor.IsValid() && Hit.Actor->CanBeDamaged(); - PlayImpactEffects(Hit.ImpactPoint, Hit.ImpactNormal, bWillProbablyCauseDamage); - } - } - } - else - { - // If the weapon is still on cooldown, play a click sound - PlayUnableToFireEffects(); - } -} - -void AWeapon::Server_TryFire_Implementation(const FVector& MuzzleLocation, const FVector& Direction) -{ - // We're now running with authority: whereas HandleFireInput is repsonsible - // for responding to the input by deciding if we should ask the server to - // fire, Server_TryFire is responsible for making the final, authoritative - // decision about whether we should fire a shot. We need to do our own - // cooldown check with authority; we can't simply trust the client, since - // simple hacks would allow a player to bypass the cooldown check and issue - // server RPCs whenever they wanted. - const float CurrentTime = GetWorld()->GetTimeSeconds(); - const float ElapsedSinceLastFire = CurrentTime - LastFireTime; - if (ElapsedSinceLastFire >= FireCooldown) - { - // Cache our last fire time: note that LastFireTime isn't replicated; - // it's updated independently on the server and on clients - LastFireTime = CurrentTime; - - // Update our LastFirePacket to reflect that the server has allowed the - // weapon to fire: this will replicate to non-owning clients (i.e. it - // won't be sent to the player who originally issued this RPC), causing - // their OnRep_LastFirePacket function to be called - LastFirePacket.ServerFireTime = CurrentTime; - - // Run a server-authoritative line trace to determine if there's - // any blocking geometry in the path of the shot, and if so, whether - // that's a primitive belonging to an actor who we might deal damage to - FHitResult Hit; - if (RunFireTrace(Hit)) - { - // If we hit a damageable actor, attempt to damage it - float DamageCaused = 0.0f; - if (Hit.Actor.IsValid() && Hit.Actor->CanBeDamaged()) - { - const float BaseDamage = 1.0f; - const FPointDamageEvent DamageEvent(BaseDamage, Hit, Direction, UDamageType_WeaponFire::StaticClass()); - DamageCaused = Hit.Actor->TakeDamage(BaseDamage, DamageEvent, GetInstigatorController(), this); - } - - // Spawn particle effects and audio server-side: note that we're - // using UGameplayStatics functions which have no effect on - // dedicated server, so while we could gate these calls behind - // IsRunningDedicatedServer(), it's not strictly necessary. Either - // way, these function calls take effect if we're running standalone - // or as listen server. - PlayFireEffects(); - PlayImpactEffects(Hit.ImpactPoint, Hit.ImpactNormal, DamageCaused > 0.0f); - - // Propagate the details of our hit to non-owning clients: this - // gives them everything they need to know in order to spawn their - // own cosmetic effectst - LastFirePacket.bCausedDamage = DamageCaused > 0.0f; - LastFirePacket.ImpactPoint = Hit.ImpactPoint; - LastFirePacket.ImpactNormal = Hit.ImpactNormal; - } - else - { - // Set ImpactNormal to zero as a sentinel to indicate that this - // shot didn't hit anything - LastFirePacket.ImpactNormal = FVector::ZeroVector; - } - } -} - -void AWeapon::OnRep_LastFirePacket() -{ - // LastFirePacket is replicated with COND_SkipOwner, so if we get this - // notify, that means we're a remote client (i.e. not the client that owns - // this weapon) -- this weapon belongs to a non-local player. Since all - // the gameplay-authoritative stuff happens separately on the server, all we - // need to do here is spawn cosmetic effects so that the local player can - // see that this weapon has just been fired. - PlayFireEffects(); - - if (!LastFirePacket.ImpactNormal.IsZero()) - { - PlayImpactEffects(LastFirePacket.ImpactPoint, LastFirePacket.ImpactNormal, LastFirePacket.bCausedDamage); - } -} - -void AWeapon::PlayFireEffects() -{ - // This is yet another instance where defining the particulars of our - // cosmetic effects in Blueprints would make more sense: you'd typically - // expose these events via a BlueprintCallable function. Also note that - // we're spawning UParticleSystemComponents and UAudioComponents at runtime, - // which carries some overhead, but it allows us to fire-and-forget our - // effects without having to worry about overlap. - if (FireEffect) - { - UGameplayStatics::SpawnEmitterAttached(FireEffect, MuzzleHandle); - } - - if (FireSound) - { - UGameplayStatics::PlaySoundAtLocation(GetWorld(), FireSound, MuzzleHandle->GetComponentLocation(), MuzzleHandle->GetComponentRotation()); - } -} - -void AWeapon::PlayUnableToFireEffects() -{ - if (DryFireSound) - { - UGameplayStatics::PlaySoundAtLocation(GetWorld(), DryFireSound, MuzzleHandle->GetComponentLocation(), MuzzleHandle->GetComponentRotation()); - } -} - -void AWeapon::PlayImpactEffects(const FVector& ImpactPoint, const FVector& ImpactNormal, bool bCausedDamage) -{ - const FRotator ImpactRotation = ImpactNormal.ToOrientationRotator(); - - if (ImpactEffect) - { - UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), FireEffect, ImpactPoint, ImpactRotation); - } - - USoundBase* Sound = bCausedDamage ? DamagingImpactSound : NonDamagingImpactSound; - if (Sound) - { - UGameplayStatics::PlaySoundAtLocation(GetWorld(), Sound, ImpactPoint, ImpactRotation); - } -} - -bool AWeapon::RunFireTrace(FHitResult& OutHit) -{ - const FVector& TraceStart = MuzzleHandle->GetComponentLocation(); - const FVector TraceEnd = TraceStart + (MuzzleHandle->GetForwardVector() * 5000.0f); - const FName ProfileName = UCollisionProfile::BlockAllDynamic_ProfileName; - const FCollisionQueryParams QueryParams(TEXT("WeaponFire"), false, GetOwner()); - return GetWorld()->LineTraceSingleByProfile(OutHit, TraceStart, TraceEnd, ProfileName, QueryParams); -} - -void AWeapon::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const -{ - Super::GetLifetimeReplicatedProps(OutLifetimeProps); - - DOREPLIFETIME_CONDITION(AWeapon, LastFirePacket, COND_SkipOwner); -} +#include "Weapon.h" + +#include "Net/UnrealNetwork.h" +#include "UObject/ConstructorHelpers.h" +#include "Engine/World.h" +#include "Engine/EngineTypes.h" +#include "Engine/StaticMesh.h" +#include "Engine/CollisionProfile.h" +#include "Materials/MaterialInterface.h" +#include "Particles/ParticleSystem.h" +#include "Sound/SoundBase.h" +#include "Components/SceneComponent.h" +#include "Components/StaticMeshComponent.h" +#include "GameFramework/Pawn.h" +#include "Kismet/GameplayStatics.h" +#include "Engine/DamageEvents.h" // Include the header where FPointDamageEvent is defined + +#include "Log.h" +#include "DamageType_WeaponFire.h" + +AWeapon::AWeapon(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + static ConstructorHelpers::FObjectFinder MeshFinder(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'")); + static ConstructorHelpers::FObjectFinder MaterialFinder(TEXT("Material'/Game/Assets/Weapon/M_Weapon.M_Weapon'")); + + static ConstructorHelpers::FObjectFinder FireEffectFinder(TEXT("ParticleSystem'/Game/Assets/Weapon/PS_MuzzleFlash.PS_MuzzleFlash'")); + static ConstructorHelpers::FObjectFinder ImpactEffectFinder(TEXT("ParticleSystem'/Game/Assets/Weapon/PS_WeaponImpact.PS_WeaponImpact'")); + + static ConstructorHelpers::FObjectFinder FireSoundFinder(TEXT("SoundCue'/Game/Audio/A_Weapon_Fire.A_Weapon_Fire'")); + static ConstructorHelpers::FObjectFinder DryFireSoundFinder(TEXT("SoundCue'/Game/Audio/A_Weapon_DryFire.A_Weapon_DryFire'")); + static ConstructorHelpers::FObjectFinder DamagingImpactSoundFinder(TEXT("SoundCue'/Game/Audio/A_WeaponImpact_Damaging.A_WeaponImpact_Damaging'")); + static ConstructorHelpers::FObjectFinder NonDamagingImpactSoundFinder(TEXT("SoundCue'/Game/Audio/A_WeaponImpact_NonDamaging.A_WeaponImpact_NonDamaging'")); + + PrimaryActorTick.bCanEverTick = true; + + // Define default cooldown/firing properties + FireCooldown = 0.4f; + LastFireTime = TNumericLimits::Lowest(); + + // Define default values for aiming properties + AimInterpSpeed = 8.0f; + DropInterpSpeed = 10.0f; + DropRotation = FRotator(-30.0f, -80.0f, 0.0f); + + // Make sure that weapons will be replicated as long as their owning Pawn + // is replicated + bReplicates = true; + bNetUseOwnerRelevancy = true; + + // Create a static mesh, as well as a SceneComponent at the location of the + // muzzle (i.e. where line traces would originate from, and where effects + // would spawn) + RootComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("RootComponent")); + + MeshComponent = ObjectInitializer.CreateDefaultSubobject(this, TEXT("MeshComponent")); + MeshComponent->SetupAttachment(RootComponent); + MeshComponent->SetStaticMesh(MeshFinder.Object); + MeshComponent->SetMaterial(0, MaterialFinder.Object); + MeshComponent->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName); + MeshComponent->SetRelativeLocation(FVector(20.0f, 0.0f, 0.0f)); + MeshComponent->SetRelativeRotation(FRotator(0.0f, 0.0f, 45.0f)); + MeshComponent->SetRelativeScale3D(FVector(0.5f, 0.15f, 0.15f)); + + MuzzleHandle = ObjectInitializer.CreateDefaultSubobject(this, TEXT("MuzzleHandle")); + MuzzleHandle->SetupAttachment(RootComponent); + MuzzleHandle->SetRelativeLocation(FVector(50.0f, 0.0f, 0.0f)); + + // Cache asset references for visual effects and audio + FireEffect = FireEffectFinder.Object; + ImpactEffect = ImpactEffectFinder.Object; + FireSound = FireSoundFinder.Object; + DryFireSound = DryFireSoundFinder.Object; + DamagingImpactSound = DamagingImpactSoundFinder.Object; + NonDamagingImpactSound = NonDamagingImpactSoundFinder.Object; +} + +void AWeapon::GatherCurrentMovement() +{ +} + +void AWeapon::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + if (bAimLocationIsValid) + { + const FVector AimDisplacement = AimLocation - GetActorLocation(); + const FVector AimDirection = AimDisplacement.GetSafeNormal(); + + const FQuat TargetRotation = AimDirection.ToOrientationQuat(); + const FQuat NewRotation = FMath::QInterpTo(GetActorQuat(), TargetRotation, DeltaSeconds, AimInterpSpeed); + SetActorRotation(NewRotation); + } + else + { + AActor* AttachParent = GetAttachParentActor(); + const FQuat TargetRotation = AttachParent ? AttachParent->GetActorTransform().TransformRotation(FQuat(DropRotation)) : FQuat(DropRotation); + const FQuat NewRotation = FMath::QInterpTo(GetActorQuat(), TargetRotation, DeltaSeconds, DropInterpSpeed); + SetActorRotation(NewRotation); + } +} + +void AWeapon::UpdateAimLocation(const FVector& InWorldAimLocation, const FVector& InViewAimLocation) +{ + AimLocation = InWorldAimLocation; + bAimLocationIsValid = InViewAimLocation.X > MuzzleHandle->GetRelativeLocation().X; +} + +void AWeapon::HandleFireInput() +{ + // This function is being called on the local client to let us know that + // they've pressed the fire button. We'll do an initial client-side cooldown + // check just to avoid spamming the server with unnecessary RPCs, but this + // isn't an authoritative cooldown check: that's up to the server + const float CurrentTime = GetWorld()->GetTimeSeconds(); + const float ElapsedSinceLastFire = CurrentTime - LastFireTime; + if (ElapsedSinceLastFire >= FireCooldown) + { + // Issue a Server RPC to notify the server that the client wants to + // fire: the server will then make its own authoritative decision and + // update game state accordingly. Note that if we're playing standalone, + // this call just runs locally: it doesn't travel over the network, but + // the end result is the same (and technically the separate client and + // server cooldown checks are redundant in that case, but that's not + // significant enough to warrant special-case logic for single-player) + const FVector MuzzleLocation = MuzzleHandle->GetComponentLocation(); + const FVector Direction = MuzzleHandle->GetComponentQuat().Vector(); + Server_TryFire(MuzzleLocation, Direction); + LastFireTime = CurrentTime; + + // If this weapon, belonging to the locally controlled player, has + // authority, that means we're either running standalone or as a listen + // server. In that case, Server_TryFire will execute right away, and it + // will spawn cosmetic effects right away. But if we *don't* have + // authority, we're running as a client, which means there's latency + // between when the player fires the weapon and when the server decides + // where the shot lands. So if we're a client, we want to spawn cosmetic + // effects (particles, sound) right away, rather than waiting for the + // server to tell us with 100% certainty that we've successfully fired). + // This means our effects are non-authoritative (i.e. we're speculating + // about what we hit and hoping that we're right, but the server has the + // final say and might disagree), but it gives the player immediate + // visual feedback, without which their game would feel laggy. + if (!HasAuthority()) + { + PlayFireEffects(); + + // Run a cosmetic line trace just to see whether we should spawn an + // impact effect + FHitResult Hit; + if (RunFireTrace(Hit)) + { + const bool bWillProbablyCauseDamage = Hit.HitObjectHandle.IsValid() && Hit.HitObjectHandle.FetchActor()->CanBeDamaged(); + PlayImpactEffects(Hit.ImpactPoint, Hit.ImpactNormal, bWillProbablyCauseDamage); + } + } + } + else + { + // If the weapon is still on cooldown, play a click sound + PlayUnableToFireEffects(); + } +} + +void AWeapon::Server_TryFire_Implementation(const FVector& MuzzleLocation, const FVector& Direction) +{ + // We're now running with authority: whereas HandleFireInput is repsonsible + // for responding to the input by deciding if we should ask the server to + // fire, Server_TryFire is responsible for making the final, authoritative + // decision about whether we should fire a shot. We need to do our own + // cooldown check with authority; we can't simply trust the client, since + // simple hacks would allow a player to bypass the cooldown check and issue + // server RPCs whenever they wanted. + const float CurrentTime = GetWorld()->GetTimeSeconds(); + const float ElapsedSinceLastFire = CurrentTime - LastFireTime; + if (ElapsedSinceLastFire >= FireCooldown) + { + // Cache our last fire time: note that LastFireTime isn't replicated; + // it's updated independently on the server and on clients + LastFireTime = CurrentTime; + + // Update our LastFirePacket to reflect that the server has allowed the + // weapon to fire: this will replicate to non-owning clients (i.e. it + // won't be sent to the player who originally issued this RPC), causing + // their OnRep_LastFirePacket function to be called + LastFirePacket.ServerFireTime = CurrentTime; + + // Run a server-authoritative line trace to determine if there's + // any blocking geometry in the path of the shot, and if so, whether + // that's a primitive belonging to an actor who we might deal damage to + FHitResult Hit; + if (RunFireTrace(Hit)) + { + // If we hit a damageable actor, attempt to damage it + float DamageCaused = 0.0f; + if (Hit.HitObjectHandle.IsValid() && Hit.HitObjectHandle.FetchActor()->CanBeDamaged()) + { + const float BaseDamage = 1.0f; + const FPointDamageEvent DamageEvent(BaseDamage, Hit, Direction, UDamageType_WeaponFire::StaticClass()); + DamageCaused = Hit.HitObjectHandle.FetchActor()->TakeDamage(BaseDamage, DamageEvent, GetInstigatorController(), this); + } + + // Spawn particle effects and audio server-side: note that we're + // using UGameplayStatics functions which have no effect on + // dedicated server, so while we could gate these calls behind + // IsRunningDedicatedServer(), it's not strictly necessary. Either + // way, these function calls take effect if we're running standalone + // or as listen server. + PlayFireEffects(); + PlayImpactEffects(Hit.ImpactPoint, Hit.ImpactNormal, DamageCaused > 0.0f); + + // Propagate the details of our hit to non-owning clients: this + // gives them everything they need to know in order to spawn their + // own cosmetic effectst + LastFirePacket.bCausedDamage = DamageCaused > 0.0f; + LastFirePacket.ImpactPoint = Hit.ImpactPoint; + LastFirePacket.ImpactNormal = Hit.ImpactNormal; + } + else + { + // Set ImpactNormal to zero as a sentinel to indicate that this + // shot didn't hit anything + LastFirePacket.ImpactNormal = FVector::ZeroVector; + } + } +} + +void AWeapon::OnRep_LastFirePacket() +{ + // LastFirePacket is replicated with COND_SkipOwner, so if we get this + // notify, that means we're a remote client (i.e. not the client that owns + // this weapon) -- this weapon belongs to a non-local player. Since all + // the gameplay-authoritative stuff happens separately on the server, all we + // need to do here is spawn cosmetic effects so that the local player can + // see that this weapon has just been fired. + PlayFireEffects(); + + if (!LastFirePacket.ImpactNormal.IsZero()) + { + PlayImpactEffects(LastFirePacket.ImpactPoint, LastFirePacket.ImpactNormal, LastFirePacket.bCausedDamage); + } +} + +void AWeapon::PlayFireEffects() +{ + // This is yet another instance where defining the particulars of our + // cosmetic effects in Blueprints would make more sense: you'd typically + // expose these events via a BlueprintCallable function. Also note that + // we're spawning UParticleSystemComponents and UAudioComponents at runtime, + // which carries some overhead, but it allows us to fire-and-forget our + // effects without having to worry about overlap. + if (FireEffect) + { + UGameplayStatics::SpawnEmitterAttached(FireEffect, MuzzleHandle); + } + + if (FireSound) + { + UGameplayStatics::PlaySoundAtLocation(GetWorld(), FireSound, MuzzleHandle->GetComponentLocation(), MuzzleHandle->GetComponentRotation()); + } +} + +void AWeapon::PlayUnableToFireEffects() +{ + if (DryFireSound) + { + UGameplayStatics::PlaySoundAtLocation(GetWorld(), DryFireSound, MuzzleHandle->GetComponentLocation(), MuzzleHandle->GetComponentRotation()); + } +} + +void AWeapon::PlayImpactEffects(const FVector& ImpactPoint, const FVector& ImpactNormal, bool bCausedDamage) +{ + const FRotator ImpactRotation = ImpactNormal.ToOrientationRotator(); + + if (ImpactEffect) + { + UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), FireEffect, ImpactPoint, ImpactRotation); + } + + USoundBase* Sound = bCausedDamage ? DamagingImpactSound : NonDamagingImpactSound; + if (Sound) + { + UGameplayStatics::PlaySoundAtLocation(GetWorld(), Sound, ImpactPoint, ImpactRotation); + } +} + +bool AWeapon::RunFireTrace(FHitResult& OutHit) +{ + const FVector& TraceStart = MuzzleHandle->GetComponentLocation(); + const FVector TraceEnd = TraceStart + (MuzzleHandle->GetForwardVector() * 5000.0f); + const FName ProfileName = UCollisionProfile::BlockAllDynamic_ProfileName; + const FCollisionQueryParams QueryParams(TEXT("WeaponFire"), false, GetOwner()); + return GetWorld()->LineTraceSingleByProfile(OutHit, TraceStart, TraceEnd, ProfileName, QueryParams); +} + +void AWeapon::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + DOREPLIFETIME_CONDITION(AWeapon, LastFirePacket, COND_SkipOwner); +} diff --git a/Source/RepsiCore/Public/Weapon.h b/Source/RepsiCore/Public/Weapon.h index 88cda74..3f4a4a0 100644 --- a/Source/RepsiCore/Public/Weapon.h +++ b/Source/RepsiCore/Public/Weapon.h @@ -1,122 +1,128 @@ -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/Actor.h" - -#include "Weapon.generated.h" - -USTRUCT(BlueprintType) -struct FWeaponFirePacket -{ - GENERATED_BODY() - - /** Game time on the server when this fire event occurred. */ - UPROPERTY() - float ServerFireTime; - - /** Whether the weapon hit an actor and caused damage. */ - UPROPERTY() - bool bCausedDamage; - - /** World-space location where the blocking hit occurred, if any. */ - UPROPERTY() - FVector_NetQuantize ImpactPoint; - - /** World-space surface normal of the hit: if zero, no hit occurred. */ - UPROPERTY() - FVector_NetQuantizeNormal ImpactNormal; -}; - -UCLASS() -class AWeapon : public AActor -{ - GENERATED_BODY() - -public: - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components") - class UStaticMeshComponent* MeshComponent; - - /** Positioned at the end of the weapon, where line traces should originate from (and where muzzle flash effects etc. should be spawned).*/ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components") - class USceneComponent* MuzzleHandle; - -public: - /** How long we're required to wait between successive shots. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - float FireCooldown; - - /** Game time when the weapon was last fired, for cooldown checks. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - float LastFireTime; - - /** Visual effect to play (at the muzzle) when the weapon is fired. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - class UParticleSystem* FireEffect; - - /** Particle system spawned when the weapon hits something (with +X oriented along the impact normal). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - class UParticleSystem* ImpactEffect; - - /** Sound to play when the weapon is fired. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - class USoundBase* FireSound; - - /** Sound to play in response to a Fire input when the weapon isn't yet ready to fire. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - class USoundBase* DryFireSound; - - /** Sound to play when the weapon hits an actor and successfully deals damage. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - class USoundBase* DamagingImpactSound; - - /** Sound to play when the weapon hits an inert surface. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") - class USoundBase* NonDamagingImpactSound; - - /** Replicated to non-owning clients: contains information about the last fire event that the server generated for this weapon. */ - UPROPERTY(ReplicatedUsing=OnRep_LastFirePacket, VisibleAnywhere, BlueprintReadOnly, Category="Firing|State") - FWeaponFirePacket LastFirePacket; - -public: - /** How quickly the weapon will rotate to orient itself toward the point where the player is aiming. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Aiming") - float AimInterpSpeed; - - /** Alternative rotation interp speed used when dropping the weapon (because the player is aiming at a point that's too close or is otherwise invalid). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Aiming") - float DropInterpSpeed; - - /** Local-space rotation that the weapon will adopt when it's not being aimed at a valid point in the world. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Aiming") - FRotator DropRotation; - - /** World-space location representing where the player is aiming the weapon. */ - UPROPERTY(Transient, VisibleAnywhere, BlueprintReadOnly, Category="Aiming|State") - FVector AimLocation; - - /** Indicates whether AimLocation is a point we should actually aim the weapon at: if not, the weapon will drop down (into the DropRotation) until the aim location becomes valid again. */ - UPROPERTY(Transient, VisibleAnywhere, BlueprintReadOnly, Category="Aiming|State") - bool bAimLocationIsValid; - -public: - AWeapon(const FObjectInitializer& ObjectInitializer); - virtual void GatherCurrentMovement() override; - virtual void Tick(float DeltaSeconds) override; - - void UpdateAimLocation(const FVector& InWorldAimLocation, const FVector& InViewAimLocation); - void HandleFireInput(); - -public: - UFUNCTION(Server, Reliable) - void Server_TryFire(const FVector& MuzzleLocation, const FVector& Direction); - - UFUNCTION() - void OnRep_LastFirePacket(); - -private: - void PlayFireEffects(); - void PlayUnableToFireEffects(); - void PlayImpactEffects(const FVector& ImpactPoint, const FVector& ImpactNormal, bool bCausedDamage); - - bool RunFireTrace(FHitResult& OutHit); -}; +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" + +#include "Weapon.generated.h" + +USTRUCT(BlueprintType) +struct FWeaponFirePacket +{ + GENERATED_BODY() + + /** Game time on the server when this fire event occurred. */ + UPROPERTY() + float ServerFireTime; + + /** Whether the weapon hit an actor and caused damage. */ + UPROPERTY() + bool bCausedDamage; + + /** World-space location where the blocking hit occurred, if any. */ + UPROPERTY() + FVector_NetQuantize ImpactPoint; + + /** World-space surface normal of the hit: if zero, no hit occurred. */ + UPROPERTY() + FVector_NetQuantizeNormal ImpactNormal; + + FWeaponFirePacket() + { + ServerFireTime = 0.f; + bCausedDamage = false; + } +}; + +UCLASS() +class AWeapon : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components") + class UStaticMeshComponent* MeshComponent; + + /** Positioned at the end of the weapon, where line traces should originate from (and where muzzle flash effects etc. should be spawned).*/ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components") + class USceneComponent* MuzzleHandle; + +public: + /** How long we're required to wait between successive shots. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + float FireCooldown; + + /** Game time when the weapon was last fired, for cooldown checks. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + float LastFireTime; + + /** Visual effect to play (at the muzzle) when the weapon is fired. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + class UParticleSystem* FireEffect; + + /** Particle system spawned when the weapon hits something (with +X oriented along the impact normal). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + class UParticleSystem* ImpactEffect; + + /** Sound to play when the weapon is fired. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + class USoundBase* FireSound; + + /** Sound to play in response to a Fire input when the weapon isn't yet ready to fire. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + class USoundBase* DryFireSound; + + /** Sound to play when the weapon hits an actor and successfully deals damage. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + class USoundBase* DamagingImpactSound; + + /** Sound to play when the weapon hits an inert surface. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Firing") + class USoundBase* NonDamagingImpactSound; + + /** Replicated to non-owning clients: contains information about the last fire event that the server generated for this weapon. */ + UPROPERTY(ReplicatedUsing=OnRep_LastFirePacket, VisibleAnywhere, BlueprintReadOnly, Category="Firing|State") + FWeaponFirePacket LastFirePacket; + +public: + /** How quickly the weapon will rotate to orient itself toward the point where the player is aiming. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Aiming") + float AimInterpSpeed; + + /** Alternative rotation interp speed used when dropping the weapon (because the player is aiming at a point that's too close or is otherwise invalid). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Aiming") + float DropInterpSpeed; + + /** Local-space rotation that the weapon will adopt when it's not being aimed at a valid point in the world. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Aiming") + FRotator DropRotation; + + /** World-space location representing where the player is aiming the weapon. */ + UPROPERTY(Transient, VisibleAnywhere, BlueprintReadOnly, Category="Aiming|State") + FVector AimLocation; + + /** Indicates whether AimLocation is a point we should actually aim the weapon at: if not, the weapon will drop down (into the DropRotation) until the aim location becomes valid again. */ + UPROPERTY(Transient, VisibleAnywhere, BlueprintReadOnly, Category="Aiming|State") + bool bAimLocationIsValid; + +public: + AWeapon(const FObjectInitializer& ObjectInitializer); + virtual void GatherCurrentMovement() override; + virtual void Tick(float DeltaSeconds) override; + + void UpdateAimLocation(const FVector& InWorldAimLocation, const FVector& InViewAimLocation); + void HandleFireInput(); + +public: + UFUNCTION(Server, Reliable) + void Server_TryFire(const FVector& MuzzleLocation, const FVector& Direction); + + UFUNCTION() + void OnRep_LastFirePacket(); + +private: + void PlayFireEffects(); + void PlayUnableToFireEffects(); + void PlayImpactEffects(const FVector& ImpactPoint, const FVector& ImpactNormal, bool bCausedDamage); + + bool RunFireTrace(FHitResult& OutHit); +}; diff --git a/Source/RepsiCore/RepsiCore.Build.cs b/Source/RepsiCore/RepsiCore.Build.cs index a2ff961..2a1fc75 100644 --- a/Source/RepsiCore/RepsiCore.Build.cs +++ b/Source/RepsiCore/RepsiCore.Build.cs @@ -1,13 +1,13 @@ -using UnrealBuildTool; - -public class RepsiCore : ModuleRules -{ - public RepsiCore(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - bEnforceIWYU = true; - - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" }); - PrivateDependencyModuleNames.AddRange(new string[] { }); - } -} +using UnrealBuildTool; + +public class RepsiCore : ModuleRules +{ + public RepsiCore(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + IWYUSupport = IWYUSupport.Full; + + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" }); + PrivateDependencyModuleNames.AddRange(new string[] { }); + } +} diff --git a/Source/RepsiEditor.Target.cs b/Source/RepsiEditor.Target.cs index 82446fc..ead1dcd 100644 --- a/Source/RepsiEditor.Target.cs +++ b/Source/RepsiEditor.Target.cs @@ -1,11 +1,12 @@ -using UnrealBuildTool; - -public class RepsiEditorTarget : TargetRules -{ - public RepsiEditorTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Editor; - DefaultBuildSettings = BuildSettingsVersion.V2; - ExtraModuleNames.AddRange( new string[] { "RepsiCore" } ); - } -} +using UnrealBuildTool; + +public class RepsiEditorTarget : TargetRules +{ + public RepsiEditorTarget(TargetInfo Target) : base(Target) + { + Type = TargetType.Editor; + IncludeOrderVersion = EngineIncludeOrderVersion.Latest; + DefaultBuildSettings = BuildSettingsVersion.V5; + ExtraModuleNames.AddRange( new string[] { "RepsiCore" } ); + } +} diff --git a/Source/RepsiServer.Target.cs b/Source/RepsiServer.Target.cs index b75cb37..9d02880 100644 --- a/Source/RepsiServer.Target.cs +++ b/Source/RepsiServer.Target.cs @@ -1,11 +1,12 @@ -using UnrealBuildTool; - -public class RepsiServerTarget : TargetRules -{ - public RepsiServerTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Server; - DefaultBuildSettings = BuildSettingsVersion.V2; - ExtraModuleNames.AddRange( new string[] { "RepsiCore" } ); - } -} +using UnrealBuildTool; + +public class RepsiServerTarget : TargetRules +{ + public RepsiServerTarget(TargetInfo Target) : base(Target) + { + Type = TargetType.Server; + IncludeOrderVersion = EngineIncludeOrderVersion.Latest; + DefaultBuildSettings = BuildSettingsVersion.V5; + ExtraModuleNames.AddRange( new string[] { "RepsiCore" } ); + } +}