diff --git a/Cloud9.png b/Cloud9.png new file mode 100644 index 000000000..77c3c0cd2 Binary files /dev/null and b/Cloud9.png differ diff --git a/Cloud9.uproject b/Cloud9.uproject index fc336002d..640c4b9b5 100644 --- a/Cloud9.uproject +++ b/Cloud9.uproject @@ -15,5 +15,8 @@ "Name": "EditorScriptingUtilities", "Enabled": true } + ], + "TargetPlatforms": [ + "WindowsNoEditor" ] } \ No newline at end of file diff --git a/Content/Blueprints/Weapons/FirearmWeaponsTable.uasset b/Content/Blueprints/Weapons/FirearmWeaponsTable.uasset index 8667629c6..796280219 100644 Binary files a/Content/Blueprints/Weapons/FirearmWeaponsTable.uasset and b/Content/Blueprints/Weapons/FirearmWeaponsTable.uasset differ diff --git a/Content/Blueprints/Weapons/WeaponTableDefinitions.uasset b/Content/Blueprints/Weapons/WeaponTableDefinitions.uasset index c6bb9fcf0..d7d6456da 100644 Binary files a/Content/Blueprints/Weapons/WeaponTableDefinitions.uasset and b/Content/Blueprints/Weapons/WeaponTableDefinitions.uasset differ diff --git a/Content/Maps/de_train/tools_wrongway_timer.uasset b/Content/Maps/de_train/tools_wrongway_timer.uasset index 166bcc9e0..8ccddfba2 100644 Binary files a/Content/Maps/de_train/tools_wrongway_timer.uasset and b/Content/Maps/de_train/tools_wrongway_timer.uasset differ diff --git a/Content/Maps/warmup.umap b/Content/Maps/warmup.umap index 75e4dc34b..912f09d95 100644 Binary files a/Content/Maps/warmup.umap and b/Content/Maps/warmup.umap differ diff --git a/Content/Vefects/VFX/Tracers/FirearmTracer.uasset b/Content/Vefects/VFX/Tracers/FirearmTracer.uasset new file mode 100644 index 000000000..81826e872 Binary files /dev/null and b/Content/Vefects/VFX/Tracers/FirearmTracer.uasset differ diff --git a/Content/Vefects/VFX/Tracers/Materials/TracerMaterialBase.uasset b/Content/Vefects/VFX/Tracers/Materials/TracerMaterialBase.uasset new file mode 100644 index 000000000..0be19792d Binary files /dev/null and b/Content/Vefects/VFX/Tracers/Materials/TracerMaterialBase.uasset differ diff --git a/Source/Cloud9/Character/Cloud9Character.cpp b/Source/Cloud9/Character/Cloud9Character.cpp index 8b3780b3e..daf57d36d 100644 --- a/Source/Cloud9/Character/Cloud9Character.cpp +++ b/Source/Cloud9/Character/Cloud9Character.cpp @@ -35,13 +35,14 @@ #include "Components/DecalComponent.h" #include "Components/CapsuleComponent.h" +#include "Cloud9/Game/Cloud9GameInstance.h" #include "Cloud9/Game/Cloud9DeveloperSettings.h" #include "Cloud9/Game/Cloud9PlayerController.h" #include "Cloud9/Character/Components/Cloud9CharacterMovement.h" #include "Cloud9/Character/Components/Cloud9Inventory.h" #include "Cloud9/Character/Components/Cloud9SpringArmComponent.h" +#include "Cloud9/Tools/Extensions/TContainer.h" -class UCloud9SpringArmComponent; const FName ACloud9Character::SpringArmComponentName = TEXT("CameraBoom"); const FName ACloud9Character::CameraComponentName = TEXT("TopDownCamera"); const FName ACloud9Character::DecalComponentName = TEXT("CursorToWorld"); @@ -255,3 +256,25 @@ void ACloud9Character::OnConstruction(const FTransform& Transform) MyMesh->bAffectDynamicIndirectLighting = true; } } + +void ACloud9Character::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + // Auto select any available weapon if nothing selected + if (not Inventory->GetSelectedWeapon()) + { + Inventory->SelectOtherAvailableWeapon(false); + } + + // TODO: Remove auto grenade add after debug + if (not Inventory->GetWeaponAt(EWeaponSlot::Grenade) and Inventory->GetSelectedWeapon() != nullptr) + { + if (let GameInstance = GetGameInstance(); IsValid(GameInstance)) + { + GameInstance->GetDefaultWeaponsConfig() + | ETContainer::Filter{[this](let& Config) { return IsValid(Config) and Config.IsGrenadeWeapon(); }} + | ETContainer::ForEach{[this](let& Config) { Inventory->AddWeapon(Config); }}; + } + } +} diff --git a/Source/Cloud9/Character/Cloud9Character.h b/Source/Cloud9/Character/Cloud9Character.h index f20f2bfc3..4dfeeabcd 100644 --- a/Source/Cloud9/Character/Cloud9Character.h +++ b/Source/Cloud9/Character/Cloud9Character.h @@ -46,6 +46,7 @@ class ACloud9Character : public ACharacter ACloud9Character(const FObjectInitializer& ObjectInitializer); virtual void OnConstruction(const FTransform& Transform) override; + virtual void Tick(float DeltaSeconds) override; /** Returns TopDownCameraComponent subobject **/ FORCEINLINE class UCameraComponent* GetTopDownCameraComponent() const { return TopDownCameraComponent; } diff --git a/Source/Cloud9/Character/Components/Cloud9Inventory.cpp b/Source/Cloud9/Character/Components/Cloud9Inventory.cpp index 232330c01..bf9e34cae 100644 --- a/Source/Cloud9/Character/Components/Cloud9Inventory.cpp +++ b/Source/Cloud9/Character/Components/Cloud9Inventory.cpp @@ -32,8 +32,6 @@ UCloud9Inventory::UCloud9Inventory() { - PrimaryComponentTick.bCanEverTick = true; - SelectedWeaponSlot = EWeaponSlot::NotSelected; let SlotsNumber = StaticEnum()->NumEnums(); @@ -73,45 +71,7 @@ void UCloud9Inventory::BeginPlay() } } -void UCloud9Inventory::TickComponent( - float DeltaTime, - ELevelTick TickType, - FActorComponentTickFunction* ThisTickFunction) -{ - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - // Auto select any available weapon if nothing selected - if (not GetSelectedWeapon()) - { - SelectOtherAvailableWeapon(false); - } - - if (not GetWeaponAt(EWeaponSlot::Grenade) and GetSelectedWeapon() != nullptr) - { - // TODO: Remove auto grenade add after debug - - let MyOwner = GetOwner(); - - if (not IsValid(MyOwner)) - { - log(Error, "[Actor='%s'] ACloud9Character isn't valid", *GetName()); - return; - } - - let GameInstance = MyOwner->GetGameInstance(); - - if (not IsValid(GameInstance)) - { - log(Error, "[Actor='%s'] UCloud9GameInstance isn't valid", *GetName()); - } - - GameInstance->GetDefaultWeaponsConfig() - | ETContainer::Filter{[this](let& Config) { return IsValid(Config) and Config.IsGrenadeWeapon(); }} - | ETContainer::ForEach{[this](let& Config) { AddWeapon(Config); }}; - } -} - -bool UCloud9Inventory::SelectWeapon(EWeaponSlot Slot, bool Instant) +bool UCloud9Inventory::SelectWeapon(EWeaponSlot Slot, bool Instant, bool Force) { if (Slot == EWeaponSlot::NotSelected) { @@ -148,7 +108,7 @@ bool UCloud9Inventory::SelectWeapon(EWeaponSlot Slot, bool Instant) "[Inventory='%s'] SelectedWeapon should not be valid if slot == EWeaponSlot::NotSelected", *GetName()); - if (PendingWeapon->ChangeState(EWeaponBond::Armed, Instant)) + if (PendingWeapon->ChangeState(EWeaponBond::Armed, Instant, Force)) { SelectedWeaponSlot = Slot; return true; @@ -160,14 +120,20 @@ bool UCloud9Inventory::SelectWeapon(EWeaponSlot Slot, bool Instant) if (let SelectedWeapon = GetSelectedWeapon(); IsValid(SelectedWeapon)) { - SelectedWeapon->ChangeState(EWeaponBond::Holstered, true); + if (not SelectedWeapon->ChangeState(EWeaponBond::Holstered, true, Force)) + { + log(Verbose, + "[Inventory='%s'] Can't change state of selected weapon from slot='%d'", + *GetName(), SelectedWeaponSlot); + return false; + } } - if (not PendingWeapon->ChangeState(EWeaponBond::Armed, Instant)) + if (not PendingWeapon->ChangeState(EWeaponBond::Armed, Instant, Force)) { log(Verbose, - "[Inventory='%s'] Can't change state of pending weapon slot='%d'", - *GetName(), SelectedWeaponSlot); + "[Inventory='%s'] Can't change state of pending weapon to slot='%d'", + *GetName(), Slot); return false; } @@ -324,9 +290,9 @@ bool UCloud9Inventory::RemoveWeapon(EWeaponSlot Slot) return false; } - if (Slot == SelectedWeaponSlot) + if (Slot == SelectedWeaponSlot and not SelectOtherAvailableWeapon(true)) { - SelectOtherAvailableWeapon(true); + return false; } WeaponAt(Slot)->Destroy(); @@ -335,7 +301,7 @@ bool UCloud9Inventory::RemoveWeapon(EWeaponSlot Slot) return true; } -bool UCloud9Inventory::SelectOtherAvailableWeapon(bool Instant) +bool UCloud9Inventory::SelectOtherAvailableWeapon(bool Instant, bool Force) { EWeaponSlot NewSlot = EWeaponSlot::NotSelected; let Found = WeaponSlots @@ -345,7 +311,7 @@ bool UCloud9Inventory::SelectOtherAvailableWeapon(bool Instant) log(Verbose, "[Inventory='%s'] Change selected slot to '%d'", *GetName(), NewSlot); - return SelectWeapon(NewSlot, Instant); + return SelectWeapon(NewSlot, Instant, Force); } ACloud9WeaponBase* UCloud9Inventory::GetSelectedWeapon() const { return GetWeaponAt(SelectedWeaponSlot); } diff --git a/Source/Cloud9/Character/Components/Cloud9Inventory.h b/Source/Cloud9/Character/Components/Cloud9Inventory.h index d77c578d9..bf094e466 100644 --- a/Source/Cloud9/Character/Components/Cloud9Inventory.h +++ b/Source/Cloud9/Character/Components/Cloud9Inventory.h @@ -51,11 +51,13 @@ class CLOUD9_API UCloud9Inventory * * @param Slot New slot weapon slot. * @param Instant If set then no animation will be shown and switching will be instant. + * @param Force If set then other weapon will be selected even if animation playing */ UFUNCTION(BlueprintCallable) - bool SelectWeapon(EWeaponSlot Slot, bool Instant = false); + bool SelectWeapon(EWeaponSlot Slot, bool Instant = false, bool Force = false); - bool SelectOtherAvailableWeapon(bool Instant); + UFUNCTION(BlueprintCallable) + bool SelectOtherAvailableWeapon(bool Instant, bool Force = false); UFUNCTION(BlueprintCallable) ACloud9WeaponBase* GetWeaponAt(EWeaponSlot Slot) const; @@ -101,10 +103,6 @@ class CLOUD9_API UCloud9Inventory protected: virtual void BeginPlay() override; - virtual void TickComponent( - float DeltaTime, - ELevelTick TickType, - FActorComponentTickFunction* ThisTickFunction) override; template WeaponType* WeaponAt(EWeaponSlot Slot) const { return WeaponSlots[Slot | EUEnum::To{}]; } diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp index 669098f18..1dc526640 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.cpp @@ -335,7 +335,7 @@ bool ACloud9WeaponBase::UpdateWeaponAttachment(EWeaponSlot NewSlot, EWeaponBond return false; } - log(Verbose, + log(Display, "[Weapon='%s' Slot='%s'] Update attachment to character '%s' into socket '%s'", *GetName(), SLOT_NAME, *Character->GetName(), *SocketName.ToString()); @@ -419,27 +419,27 @@ bool ACloud9WeaponBase::RemoveFromInventory() return true; } -bool ACloud9WeaponBase::ChangeState(EWeaponBond NewBond, bool Instant) +bool ACloud9WeaponBase::ChangeState(EWeaponBond NewBond, bool Instant, bool Force) { if (let Character = GetOwner(); not IsValid(Character)) { - log(Warning, "[Weapon='%s' Bond='%s'] Weapon not in any inventory", *GetName(), BOND_NAME); + log(Error, "[Weapon='%s' Bond='%s'] Weapon not in any inventory", *GetName(), BOND_NAME); return false; } if (WeaponState.IsWeaponBond(NewBond)) { - log(Verbose, "[Weapon='%s' Bond='%s'] Weapon will remain the same", *GetName(), BOND_NAME); + log(Error, "[Weapon='%s' Bond='%s'] Weapon will remain the same", *GetName(), BOND_NAME); return false; } - if (IsAnyMontagePlaying()) + if (not Force and IsAnyMontagePlaying()) { log(Verbose, "[Weapon='%s' Bond='%s'] Montage is playing now", *GetName(), BOND_NAME); return false; } - if (IsActionInProgress()) + if (not Force and IsActionInProgress()) { log(Verbose, "[Weapon='%s' Bond='%s'] Some action is in progress", *GetName(), BOND_NAME); return false; @@ -451,7 +451,7 @@ bool ACloud9WeaponBase::ChangeState(EWeaponBond NewBond, bool Instant) void ACloud9WeaponBase::PrimaryAction(bool IsReleased) { WeaponState.ActivateSequence( - EWeaponAction::Primary, + EWeaponAction::PrimaryStart, EWeaponAction::PrimaryLoop, EWeaponAction::PrimaryEnd, IsReleased); diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.h b/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.h index 79148a82b..8fae76607 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.h +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponBase.h @@ -127,7 +127,7 @@ class CLOUD9_API ACloud9WeaponBase : public AActor bool AddToInventory(ACloud9Character* Character, EWeaponSlot NewSlot); bool RemoveFromInventory(); - bool ChangeState(EWeaponBond NewBond, bool Instant); + bool ChangeState(EWeaponBond NewBond, bool Instant, bool Force = false); template FORCEINLINE bool ExecuteAction( diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp index a0ad17b5a..83243d35d 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp @@ -23,6 +23,7 @@ #include "Cloud9WeaponFirearm.h" #include "Engine/StaticMeshActor.h" +#include "NiagaraFunctionLibrary.h" #include "Cloud9/Tools/Macro/Common.h" #include "Cloud9/Tools/Macro/Logging.h" @@ -33,6 +34,9 @@ #include "Cloud9/Character/Cloud9Character.h" #include "Cloud9/Weapon/Tables/WeaponTableFirearm.h" +const FName ACloud9WeaponFirearm::TracerProbabilityParameterName = TEXT("Probability"); +const FName ACloud9WeaponFirearm::TracerDirectionParameterName = TEXT("Direction"); + FWeaponId ACloud9WeaponFirearm::GetWeaponId() const { return ETVariant::Convert(WeaponId); } bool ACloud9WeaponFirearm::OnInitialize(const FWeaponId& NewWeaponId, FName NewWeaponSkin) @@ -159,14 +163,14 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) [this] { WeaponState.ClearAction(EWeaponAction::Deploy); } ); } - else if (WeaponState.IsActionActive(EWeaponAction::Primary)) + else if (WeaponState.IsActionActive(EWeaponAction::PrimaryStart)) { - WeaponState.ClearAction(EWeaponAction::Primary); + WeaponState.ClearAction(EWeaponAction::PrimaryStart); } else if (WeaponState.IsActionActive(EWeaponAction::PrimaryLoop)) { ExecuteAction( - EWeaponAction::Primary, + EWeaponAction::PrimaryStart, WeaponInfo->CycleTime, [&] { @@ -179,7 +183,7 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) // TODO: May be move to notifier? MuzzleFlash->Activate(true); - if (not Fire(WeaponInfo, CommonData->ImpulseMultiplier)) + if (not Fire(WeaponInfo, CommonData->Firearm)) { log(Error, "[Weapon='%s'] Weapon fire failure", *GetName()); return false; @@ -191,7 +195,7 @@ void ACloud9WeaponFirearm::Tick(float DeltaSeconds) if (not WeaponInfo->bIsFullAuto) { - WeaponState.ClearAction(EWeaponAction::Primary); + WeaponState.ClearAction(EWeaponAction::PrimaryStart); } } else if (WeaponState.IsActionActive(EWeaponAction::PrimaryEnd)) @@ -209,7 +213,7 @@ const FFirearmWeaponInfo* ACloud9WeaponFirearm::GetWeaponInfo() const return WeaponDefinition.GetWeaponInfo(); } -bool ACloud9WeaponFirearm::Fire(const FFirearmWeaponInfo* WeaponInfo, float ImpulseMultiplier) const +bool ACloud9WeaponFirearm::Fire(const FFirearmWeaponInfo* WeaponInfo, const FFirearmCommonData& FirearmCommonData) const { let Character = GetOwner(); @@ -250,10 +254,33 @@ bool ACloud9WeaponFirearm::Fire(const FFirearmWeaponInfo* WeaponInfo, float Impu log(Verbose, "Target = %s Owner = %s", *Target->GetName(), *Target->GetOwner()->GetName()); + let Direction = (LineHit.Location - StartLocation) | EFVector::Normalize{}; + if (Target->IsSimulatingPhysics() and Target->Mobility == EComponentMobility::Movable) { - let Direction = (LineHit.Location - StartLocation) | EFVector::Normalize{}; - Target->AddImpulse(WeaponInfo->Damage * ImpulseMultiplier * Direction, NAME_None, true); + let Velocity = WeaponInfo->Damage * FirearmCommonData.ImpulseMultiplier * Direction; + Target->AddImpulse(Velocity, NAME_None, true); + } + + if (IsValid(FirearmCommonData.Tracer)) + { + let Tracer = UNiagaraFunctionLibrary::SpawnSystemAtLocation( + GetWorld(), + FirearmCommonData.Tracer, + StartLocation); + Tracer->SetVectorParameter(TracerDirectionParameterName, Direction); + Tracer->SetFloatParameter(TracerProbabilityParameterName, WeaponInfo->TracerProbability); + Tracer->SetAutoDestroy(true); + } + + if (IsValid(FirearmCommonData.Squib)) + { + let Squib = UNiagaraFunctionLibrary::SpawnSystemAtLocation( + GetWorld(), + FirearmCommonData.Squib, + LineHit.Location, + CursorHit.Normal.Rotation()); + Squib->SetAutoDestroy(true); } return true; @@ -354,7 +381,7 @@ void ACloud9WeaponFirearm::DropMagazine() const Mesh->SetSimulatePhysics(true); Mesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); - Magazine | EAActor::DestroyAfter{WeaponDefinition.GetCommonData()->MagazineLifetime}; + Magazine | EAActor::DestroyAfter{WeaponDefinition.GetCommonData()->Firearm.MagazineLifetime}; } void ACloud9WeaponFirearm::EjectCase() const @@ -382,8 +409,8 @@ void ACloud9WeaponFirearm::EjectCase() const let CommonData = WeaponDefinition.GetCommonData(); Case->SetMobility(EComponentMobility::Movable); - Case->SetActorRotation({0.0f, FMath::RandRange(0.0f, CommonData->CaseMaxEjectRotation), 0.0f}); - Case->SetActorScale3D({CommonData->CaseScale, CommonData->CaseScale, CommonData->CaseScale}); + Case->SetActorRotation({0.0f, FMath::RandRange(0.0f, CommonData->Case.MaxEjectRotation), 0.0f}); + Case->SetActorScale3D({CommonData->Case.Scale, CommonData->Case.Scale, CommonData->Case.Scale}); let Mesh = Case->GetStaticMeshComponent(); Mesh->SetStaticMesh(CaseModel); @@ -392,9 +419,9 @@ void ACloud9WeaponFirearm::EjectCase() const let RightVector = GetWeaponMesh()->GetRightVector(); let ForwardVector = GetWeaponMesh()->GetForwardVector(); - let ImpulseDirection = RightVector.RotateAngleAxis(CommonData->CaseEjectAngle, ForwardVector); + let ImpulseDirection = RightVector.RotateAngleAxis(CommonData->Case.EjectAngle, ForwardVector); - Mesh->AddImpulse(CommonData->CaseImpulse * ImpulseDirection, NAME_None, true); + Mesh->AddImpulse(CommonData->Case.EjectImpulse * ImpulseDirection, NAME_None, true); - Case | EAActor::DestroyAfter{CommonData->CaseLifetime}; + Case | EAActor::DestroyAfter{CommonData->Case.Lifetime}; } diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h index b7a551b6f..3160a7269 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h @@ -44,6 +44,10 @@ class CLOUD9_API ACloud9WeaponFirearm : public ACloud9WeaponBase friend class UCloud9AnimNotifyPlaySound; friend class UCloud9AnimNotifyMagazine; +public: + static const FName TracerProbabilityParameterName; + static const FName TracerDirectionParameterName; + public: virtual FWeaponId GetWeaponId() const override; @@ -57,7 +61,7 @@ class CLOUD9_API ACloud9WeaponFirearm : public ACloud9WeaponBase virtual void Tick(float DeltaSeconds) override; - bool Fire(const FFirearmWeaponInfo* WeaponInfo, float ImpulseMultiplier) const; + bool Fire(const FFirearmWeaponInfo* WeaponInfo, const FFirearmCommonData& FirearmCommonData) const; bool UpdateMagazineAttachment(bool IsReload); void DropMagazine() const; diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp index ddd77cd0f..26a85f977 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp @@ -83,8 +83,8 @@ bool ACloud9WeaponGrenade::OnInitialize(const FWeaponId& NewWeaponId, FName NewW InitializeEffectComponent(ActiveEffect, OnActiveEffect, OnActiveScale); let CommonData = WeaponDefinition.GetCommonData(); - Explosion->Radius = CommonData->GrenadeExplosionRadius; - Explosion->ImpulseStrength = CommonData->GrenadeImpulseStrength; + Explosion->Radius = CommonData->Grenade.ExplosionRadius; + Explosion->ImpulseStrength = CommonData->Grenade.ImpulseStrength; Explosion->bIgnoreOwningActor = true; Explosion->bImpulseVelChange = true; @@ -106,84 +106,6 @@ void ACloud9WeaponGrenade::OnWeaponRemovedFromInventory() ChangeMeshCollisionState(WeaponMesh, true); } -bool ACloud9WeaponGrenade::OnGrenadeActivated() -{ - static let Settings = UCloud9DeveloperSettings::Get(); - - if (IsValid(ActiveEffect)) - { - ActiveEffect->Activate(); - UCloud9SoundPlayer::PlaySingleSound( - GetWeaponInfo()->Sounds.LoopSound, - GetActorLocation(), - Settings->Volume); - } - - if (ActiveTimerHandle.IsValid()) - { - return false; - } - - log(Error, "ActiveTimerHandle set") - - if (GetWeaponInfo()->bIsDestroyedOnDetonation) - { - SetActorTickEnabled(false); - GetWeaponMesh()->SetHiddenInGame(true); - } - - ActiveTimerHandle = GetWorld() - | EUWorld::AsyncAfter{ - [this] { Destroy(); }, - GetWeaponInfo()->ActionTime - }; - - return true; -} - -bool ACloud9WeaponGrenade::OnGrenadeThrown() -{ - static let Settings = UCloud9DeveloperSettings::Get(); - - let IsOnGround = GetVelocity().IsZero(); - let CanDetonateInAir = GetWeaponInfo()->bCanDetonateInAir; - - if (not CanDetonateInAir and not IsOnGround) - { - return false; - } - - if (DetonationTimerHandle.IsValid()) - { - return false; - } - - log(Error, "WeaponState.IsGrenadeThrown()") - - DetonationTimerHandle = GetWorld() - | EUWorld::AsyncAfter{ - [this] - { - if (IsValid(DetonationEffect)) - { - DetonationEffect->Activate(); - } - - Explosion->FireImpulse(); - - UCloud9SoundPlayer::PlayRandomSound( - GetWeaponInfo()->Sounds.ExplodeSounds, - GetActorLocation(), - Settings->Volume); - - WeaponState.GrenadeActivated(); - }, - GetWeaponInfo()->TimeToDetonate - }; - - return true; -} - void ACloud9WeaponGrenade::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); @@ -217,29 +139,34 @@ void ACloud9WeaponGrenade::Tick(float DeltaSeconds) [this] { WeaponState.ClearAction(EWeaponAction::Deploy); } ); } - else if (WeaponState.IsActionActive(EWeaponAction::Primary)) + else if (WeaponState.IsActionActive(EWeaponAction::PrimaryStart)) { - // log(Error, "PoseMontages->PinpullPrimaryActionMontage"); + log(Error, "EWeaponAction::Primary"); ExecuteAction( - EWeaponAction::Primary, + EWeaponAction::PrimaryStart, GetWeaponInfo()->PinpullTime, [&] { return PlayAnimMontage(PoseMontages->PinpullPrimaryActionMontage); }, [this] { - WeaponState.ClearAction(EWeaponAction::Primary); + WeaponState.ClearAction(EWeaponAction::PrimaryStart); WeaponState.ActivateAction(EWeaponAction::PrimaryLoop); } ); } else if (WeaponState.IsActionActive(EWeaponAction::PrimaryLoop)) { - // log(Error, "PoseMontages->PinpullPrimaryActionMontage"); + log(Error, "EWeaponAction::PrimaryLoop"); // Play hold frame of montage PlayAnimMontage( PoseMontages->PinpullPrimaryActionMontage, PoseMontages->PinpullPrimaryActionHoldTiming); + + if (WeaponState.IsActionActive(EWeaponAction::PrimaryEnd)) + { + WeaponState.ClearAction(EWeaponAction::PrimaryLoop); + } } else if (WeaponState.IsActionActive(EWeaponAction::Secondary)) { @@ -249,10 +176,9 @@ void ACloud9WeaponGrenade::Tick(float DeltaSeconds) { // TODO: Implement middle throw (or cancel throw) for grenade } - - if (WeaponState.IsActionActive(EWeaponAction::PrimaryEnd)) + else if (WeaponState.IsActionActive(EWeaponAction::PrimaryEnd)) { - // log(Error, "PoseMontages->PrimaryActionMontage"); + log(Error, "EWeaponAction::PrimaryEnd"); ExecuteAction( EWeaponAction::PrimaryEnd, @@ -271,6 +197,84 @@ void ACloud9WeaponGrenade::Tick(float DeltaSeconds) } } +bool ACloud9WeaponGrenade::OnGrenadeActivated() +{ + static let Settings = UCloud9DeveloperSettings::Get(); + + if (IsValid(ActiveEffect)) + { + ActiveEffect->Activate(); + UCloud9SoundPlayer::PlaySingleSound( + GetWeaponInfo()->Sounds.LoopSound, + GetActorLocation(), + Settings->Volume); + } + + if (ActiveTimerHandle.IsValid()) + { + return false; + } + + log(Error, "ActiveTimerHandle set") + + if (GetWeaponInfo()->bIsDestroyedOnDetonation) + { + SetActorTickEnabled(false); + GetWeaponMesh()->SetHiddenInGame(true); + } + + ActiveTimerHandle = GetWorld() + | EUWorld::AsyncAfter{ + [this] { Destroy(); }, + GetWeaponInfo()->ActionTime + }; + + return true; +} + +bool ACloud9WeaponGrenade::OnGrenadeThrown() +{ + static let Settings = UCloud9DeveloperSettings::Get(); + + let IsOnGround = GetVelocity().IsZero(); + let CanDetonateInAir = GetWeaponInfo()->bCanDetonateInAir; + + if (not CanDetonateInAir and not IsOnGround) + { + return false; + } + + if (DetonationTimerHandle.IsValid()) + { + return false; + } + + log(Error, "WeaponState.IsGrenadeThrown()") + + DetonationTimerHandle = GetWorld() + | EUWorld::AsyncAfter{ + [this] + { + if (IsValid(DetonationEffect)) + { + DetonationEffect->Activate(); + } + + Explosion->FireImpulse(); + + UCloud9SoundPlayer::PlayRandomSound( + GetWeaponInfo()->Sounds.ExplodeSounds, + GetActorLocation(), + Settings->Volume); + + WeaponState.GrenadeActivated(); + }, + GetWeaponInfo()->TimeToDetonate + }; + + return true; +} + bool ACloud9WeaponGrenade::Throw() const { let Character = GetOwner(); @@ -308,8 +312,8 @@ bool ACloud9WeaponGrenade::Throw() const Inventory->DropWeapon( GetWeaponSlot(), CursorHit.Location, - CommonData->GrenadeMaxThrowAngle, - CommonData->GrenadeMaxThrowImpulse); + CommonData->Grenade.MaxThrowAngle, + CommonData->Grenade.MaxThrowImpulse); return true; } diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp index 1a88a5f23..e10e6d190 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponMelee.cpp @@ -91,10 +91,10 @@ void ACloud9WeaponMelee::Tick(float DeltaSeconds) [this] { WeaponState.ClearAction(EWeaponAction::Deploy); } ); } - else if (WeaponState.IsActionActive(EWeaponAction::Primary)) + else if (WeaponState.IsActionActive(EWeaponAction::PrimaryStart)) { ExecuteAction( - EWeaponAction::Primary, + EWeaponAction::PrimaryStart, WeaponInfo->SlashCycleTime, [&] { return PlayAnimMontage(PoseMontages->PrimaryActionMontage); } ); diff --git a/Source/Cloud9/Weapon/Enums/WeaponAction.h b/Source/Cloud9/Weapon/Enums/WeaponAction.h index 5f7745105..d68c78881 100644 --- a/Source/Cloud9/Weapon/Enums/WeaponAction.h +++ b/Source/Cloud9/Weapon/Enums/WeaponAction.h @@ -32,7 +32,7 @@ enum class EWeaponAction : uint8 ReloadLoop = 2 UMETA(DisplayName="ReloadLoop"), ReloadEnd = 3 UMETA(DisplayName="ReloadEnd"), - Primary = 4 UMETA(DisplayName="Primary"), + PrimaryStart = 4 UMETA(DisplayName="Primary"), PrimaryLoop = 5 UMETA(DisplayName="PrimaryLoop"), PrimaryEnd = 6 UMETA(DisplayName="PrimaryEnd"), diff --git a/Source/Cloud9/Weapon/Tables/WeaponCommonData.h b/Source/Cloud9/Weapon/Tables/WeaponCommonData.h index ecab863c9..6b87208fd 100644 --- a/Source/Cloud9/Weapon/Tables/WeaponCommonData.h +++ b/Source/Cloud9/Weapon/Tables/WeaponCommonData.h @@ -26,60 +26,87 @@ #include "WeaponCommonData.generated.h" USTRUCT(BlueprintType) -struct FWeaponCommonData +struct FFirearmCommonData { GENERATED_BODY() UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Damage) float ImpulseMultiplier = 50.0f; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Case) - float CaseImpulse = 500.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Visual) + float MagazineLifetime = 10.0f; + + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Visual) + UNiagaraSystem* Tracer; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Case) - float CaseEjectAngle = 45.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Visual) + UNiagaraSystem* Squib; +}; + +USTRUCT(BlueprintType) +struct FCaseCommonData +{ + GENERATED_BODY() - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Case) - float CaseMaxEjectRotation = 30.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Eject) + float EjectImpulse = 200.0f; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Case) - float CaseScale = 2.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Eject) + float EjectAngle = 30.0f; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Case) - float CaseLifetime = 2.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Eject) + float MaxEjectRotation = 25.0f; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Grenade) - float GrenadeExplosionRadius = 200.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Visual) + float Scale = 1.2f; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Grenade) - float GrenadeImpulseStrength = 1000.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Visual) + float Lifetime = 3.0f; +}; - /** - * Maximum fire weapon range (no any impact after this distance) - */ - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Grenade) - float GrenadeMaxThrowImpulse = 800.0f; +USTRUCT(BlueprintType) +struct FGrenadeCommonData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Explosive) + float ExplosionRadius = 200.0f; + + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Explosive) + float ImpulseStrength = 2000.0f; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Grenade) - float GrenadeMaxThrowAngle = 30.0f; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Throw) + float MaxThrowImpulse = 800.0f; + + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Throw) + float MaxThrowAngle = 30.0f; /** * Distance multiplier when making run throw */ - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Grenade, + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Throw, meta=(UIMin="0", UIMax="100", ClampMin="0", ClampMax="100")) - float GrenadeRunDistanceMultiplier = 1.0f; + float RunDistanceMultiplier = 1.0f; /** * Distance multiplier when making jump throw */ - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Grenade, + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Throw, meta=(UIMin="0", UIMax="100", ClampMin="0", ClampMax="100")) - float GrenadeJumpDistanceMultiplier = 1.0f; + float JumpDistanceMultiplier = 1.0f; +}; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Weapon) - float MagazineLifetime = 10.0f; +USTRUCT(BlueprintType) +struct FWeaponCommonData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) + FFirearmCommonData Firearm; + + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) + FCaseCommonData Case; - UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Weapon) - UParticleSystem* FirearmTracer; + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) + FGrenadeCommonData Grenade; }; diff --git a/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h b/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h index a73bc6ef5..f0b4e82e7 100644 --- a/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h +++ b/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h @@ -306,11 +306,13 @@ struct FFirearmWeaponInfo : public FBaseWeaponInfo bool bIsManualBoltCycle = false; /** - * The frequency at which tracers are applied to bullets (0 = never) + * Probability of tracer for weapon + * + * NOTE: Originally used TracerFrequency - the frequency at which tracers are applied to bullets (0 = never) */ UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=VFX, - meta=(UIMin="0", UIMax="5", ClampMin="0", ClampMax="5")) - int TracerFrequency = 1; + meta=(UIMin="0", UIMax="1", ClampMin="0", ClampMax="1")) + float TracerProbability = 0.3f; /** * The number of bullets (pellets for a shotgun) fired from a cartridge