diff --git a/Source/Cloud9/Character/Effects/Cloud9CharacterEffect.cpp b/Source/Cloud9/Character/Effects/Cloud9CharacterEffect.cpp new file mode 100644 index 000000000..145bb10ea --- /dev/null +++ b/Source/Cloud9/Character/Effects/Cloud9CharacterEffect.cpp @@ -0,0 +1,4 @@ +// Copyright (c) 2024 Alexei Gladkikh + + +#include "Cloud9CharacterEffect.h" diff --git a/Source/Cloud9/Character/Effects/Cloud9CharacterEffect.h b/Source/Cloud9/Character/Effects/Cloud9CharacterEffect.h new file mode 100644 index 000000000..aee813cf0 --- /dev/null +++ b/Source/Cloud9/Character/Effects/Cloud9CharacterEffect.h @@ -0,0 +1,25 @@ +// Copyright (c) 2024 Alexei Gladkikh + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "Cloud9CharacterEffect.generated.h" + +class ACloud9Character; + +UINTERFACE() +class UCloud9CharacterEffect : public UInterface +{ + GENERATED_BODY() +}; + + +class CLOUD9_API ICloud9CharacterEffect +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, BlueprintNativeEvent) + bool Apply(ACloud9Character* Character); +}; diff --git a/Source/Cloud9/Cloud9Consts.h b/Source/Cloud9/Cloud9Consts.h new file mode 100644 index 000000000..61518aace --- /dev/null +++ b/Source/Cloud9/Cloud9Consts.h @@ -0,0 +1,24 @@ +// Copyright (c) 2024 Alexei Gladkikh + +#pragma once + +/** + * See cs_shareddefs.cpp in cstrike15_src + */ +namespace Cloud9Player +{ + constexpr float SpeedRun = 260.0f; + constexpr float SpeedVip = 227.0f; + constexpr float SpeedShield = 160.0f; + constexpr float SpeedHas_Hostage = 200.0f; + constexpr float SpeedStopped = 1.0f; + constexpr float SpeedObserver = 900.0f; + + constexpr float SpeedDuckModifier = 0.34f; + constexpr float SpeedWalkModifier = 0.52f; + constexpr float SpeedClimbModifier = 0.34f; + + constexpr float HeavyArmorFlinchModifier = 0.5f; + + constexpr float DuckSpeedIdeal = 8.0f; +} diff --git a/Source/Cloud9/Game/Cloud9DeveloperSettings.cpp b/Source/Cloud9/Game/Cloud9DeveloperSettings.cpp index 511647812..d91cd8d30 100644 --- a/Source/Cloud9/Game/Cloud9DeveloperSettings.cpp +++ b/Source/Cloud9/Game/Cloud9DeveloperSettings.cpp @@ -57,6 +57,8 @@ UCloud9DeveloperSettings::UCloud9DeveloperSettings(const FObjectInitializer& Obj bIsCheatsEnabled = 0; bIsSelfAimEnabled = 0; Volume = 0.1; + WeaponAirSpreadScale = 1.0f; + JumpImpulse = 301.993377; } UCloud9DeveloperSettings* UCloud9DeveloperSettings::Get() diff --git a/Source/Cloud9/Game/Cloud9DeveloperSettings.h b/Source/Cloud9/Game/Cloud9DeveloperSettings.h index f965b66ff..a01a767cc 100644 --- a/Source/Cloud9/Game/Cloud9DeveloperSettings.h +++ b/Source/Cloud9/Game/Cloud9DeveloperSettings.h @@ -70,7 +70,7 @@ class CLOUD9_API UCloud9DeveloperSettings : public UDeveloperSettings static FString SelfAimEnabledName; static FString CameraVerticalSpeedLagName; static FString VolumeName; - + public: // properties UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Debug) int32 bIsDrawHitCursorLine; @@ -111,6 +111,15 @@ class CLOUD9_API UCloud9DeveloperSettings : public UDeveloperSettings UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Sound) float Volume; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category=Sound) + bool bIsNoSpread; + + UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Debug) + float WeaponAirSpreadScale; + + UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Debug) + float JumpImpulse; + UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Debug) EUnUsedEnum UnUsedEnum; diff --git a/Source/Cloud9/Tools/Cloud9ToolsLibrary.h b/Source/Cloud9/Tools/Cloud9ToolsLibrary.h index 11cfb5668..a5af8b6c6 100644 --- a/Source/Cloud9/Tools/Cloud9ToolsLibrary.h +++ b/Source/Cloud9/Tools/Cloud9ToolsLibrary.h @@ -112,4 +112,47 @@ class CLOUD9_API UCloud9ToolsLibrary : public UBlueprintFunctionLibrary UFUNCTION(BlueprintCallable) static TArray GetObjectEditorProperties(UClass* Class); + + // see mathlib.h from cstrike15_src + + template + static constexpr InputType Select(InputType Value, OutputType A, OutputType B) + { + return Value < 0.0f ? A : B; + } + + template + static constexpr ValueType RemapValue( + ValueType Value, + ValueType InRangeMin, + ValueType InRangeMax, + ValueType OutRangeMin, + ValueType OutRangeMax) + { + if (InRangeMin == InRangeMax) + { + return Select(Value - InRangeMax, OutRangeMax, OutRangeMin); + } + + return OutRangeMin + (OutRangeMax - OutRangeMin) * (Value - InRangeMin) / (InRangeMax - InRangeMin); + } + + template + static constexpr ValueType RemapValueClamped( + ValueType Value, + ValueType InRangeMin, + ValueType InRangeMax, + ValueType OutRangeMin, + ValueType OutRangeMax) + { + if (InRangeMin == InRangeMax) + { + return Select(Value - InRangeMax, OutRangeMax, OutRangeMin); + } + + float Temp = (Value - InRangeMin) / (InRangeMax - InRangeMin); + Temp = FMath::Clamp(Temp, 0.0f, 1.0f); + + return OutRangeMin + (OutRangeMax - OutRangeMin) * Temp; + } }; diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp index 91f7b65a6..4d4f76453 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.cpp @@ -24,6 +24,7 @@ #include "Cloud9WeaponFirearm.h" #include "Engine/StaticMeshActor.h" #include "NiagaraFunctionLibrary.h" +#include "Cloud9/Cloud9Consts.h" #include "Components/WidgetInteractionComponent.h" #include "Cloud9/Tools/Macro/Common.h" @@ -101,6 +102,8 @@ bool ACloud9WeaponFirearm::OnInitialize(const FWeaponConfig& WeaponConfig) CurrentAmmo = WeaponConfig.GetAmmoInMagazine(MaxMagazineSize); AmmoInReserve = WeaponConfig.GetAmmoInReserve(MaxAmmoInReserve); + AccuracyPenalty = 0.0f; + OnAmmoInMagazineChanged.Broadcast(CurrentAmmo); OnAmmoInReserveChanged.Broadcast(AmmoInReserve); @@ -592,6 +595,91 @@ bool ACloud9WeaponFirearm::UpdateReloadAmmo(bool IsShotgun) return true; } +float ACloud9WeaponFirearm::GetInaccuracy() +{ + constexpr float MaxFallingPenalty = 2.0f; // Accuracy is never worse than 2x starting penalty + constexpr float MovementCurve01Exponent = 0.25f; + + static let Settings = UCloud9DeveloperSettings::Get(); + + let Character = GetOwner(); + + if (not IsValid(Character)) + { + return 0.0f; + } + + if (Settings->bIsNoSpread) + { + return 0.0f; + } + + let WeaponInfo = GetWeaponInfo(); + + float Inaccuracy = AccuracyPenalty; + + let Velocity = Character->GetVelocity(); + + let MaxSpeed = WeaponInfo->GetMaxSpeed(); + + var MovementInaccuracyScale = UCloud9ToolsLibrary::RemapValueClamped( + Velocity.Size2D(), + MaxSpeed * Cloud9Player::SpeedDuckModifier, + MaxSpeed * 0.95f, // max out at 95% of run speed to avoid jitter near max speed + 0.0f, + 1.0f); + + if (MovementInaccuracyScale > 0.0f) + { + // power curve only applies at speeds greater than walk + if (not Character->bIsSneaking) + { + MovementInaccuracyScale = FMath::Pow(MovementInaccuracyScale, MovementCurve01Exponent); + } + + Inaccuracy += MovementInaccuracyScale * WeaponInfo->GetInaccuracyMove(); + } + + // If we are in the air/on ladder, add inaccuracy based on vertical speed (maximum accuracy at apex of jump) + if (not Character->GetMovementComponent()->IsMovingOnGround()) + { + let VerticalSpeed = FMath::Abs(Velocity.Z); + + let InaccuracyJumpInitial = WeaponInfo->GetInaccuracyJump() * Settings->WeaponAirSpreadScale; + + // Use sqrt here to make the curve more "sudden" around the accurate point at the apex of the jump + let SqrtMaxJumpSpeed = FMath::Sqrt(Settings->JumpImpulse); + let SqrtVerticalSpeed = FMath::Sqrt(VerticalSpeed); + + var AirSpeedInaccuracy = UCloud9ToolsLibrary::RemapValue( + SqrtVerticalSpeed, + + SqrtMaxJumpSpeed * 0.25f, + // Anything less than 6.25% of maximum speed has no additional accuracy penalty for z-motion (6.25% = .25 * .25) + SqrtMaxJumpSpeed, // Penalty at max jump speed + + 0.0f, // No movement-related penalty when close to stopped + InaccuracyJumpInitial + ); // Movement-penalty at start of jump + + // Clamp to min/max values. (Don't use RemapValClamped because it makes clamping to > kJumpMovePenalty hard) + if (AirSpeedInaccuracy < 0.0f) + { + AirSpeedInaccuracy = 0.0f; + } + else if (AirSpeedInaccuracy > MaxFallingPenalty * InaccuracyJumpInitial) + { + AirSpeedInaccuracy = MaxFallingPenalty * InaccuracyJumpInitial; + } + + // Apply air velocity inaccuracy penalty + // (There is an additional penalty for being in the air at all applied in UpdateAccuracyPenalty()) + Inaccuracy += AirSpeedInaccuracy; + } + + return FMath::Min(Inaccuracy, 1.0f); +} + bool ACloud9WeaponFirearm::UpdateMagazineAttachment(bool IsReload) { let Character = GetOwner(); diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h index d46a126d1..62f9a04a1 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponFirearm.h @@ -84,6 +84,7 @@ class CLOUD9_API ACloud9WeaponFirearm : public ACloud9WeaponBase EFirearmFireStatus Fire(const FFirearmWeaponInfo* WeaponInfo, const FFirearmCommonData& FirearmCommonData); bool UpdateReloadAmmo(bool IsShotgun); + float GetInaccuracy(); bool UpdateMagazineAttachment(bool IsReload); void DropMagazine() const; @@ -113,6 +114,9 @@ class CLOUD9_API ACloud9WeaponFirearm : public ACloud9WeaponBase UPROPERTY(Category=Weapon, BlueprintReadOnly, meta=(AllowPrivateAccess)) int MaxAmmoInReserve; + + UPROPERTY() + float AccuracyPenalty; }; template <> diff --git a/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h b/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h index 7782c2bc7..a5e2a1a48 100644 --- a/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h +++ b/Source/Cloud9/Weapon/Tables/WeaponTableFirearm.h @@ -458,4 +458,10 @@ struct FFirearmWeaponInfo : public FBaseWeaponInfo UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Flinch, AdvancedDisplay, meta=(UIMin="0.0", UIMax="1.0", ClampMin="0", ClampMax="1.0")) float FlinchVelocityModifierNext = 1.0f; + + float GetMaxSpeed(float Scale = 1.0f) const { return MaxPlayerSpeed * Scale; } + + float GetInaccuracyMove(float Scale = 0.001f) const { return Inaccuracy.OnMove * Scale; } + + float GetInaccuracyJump(float Scale = 0.001f) const { return Inaccuracy.OnJump * Scale; } };