From a16e75d9a332cd03fa02987200bb8b4162ef9055 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Tue, 16 Apr 2024 20:03:01 +0200 Subject: [PATCH 01/13] Only Play Sniper Impact Sound When Dealing Damage --- Classes/ST_SniperRifle.uc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Classes/ST_SniperRifle.uc b/Classes/ST_SniperRifle.uc index e23398a..bb4b820 100644 --- a/Classes/ST_SniperRifle.uc +++ b/Classes/ST_SniperRifle.uc @@ -60,11 +60,10 @@ function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vect if (Other == Level) { Spawn(class'UT_HeavyWallHitEffect',,, HitLocation+HitNormal, Rotator(HitNormal)); } else if ((Other != self) && (Other != Owner) && (Other != None)) { - if (Other.bIsPawn) - Other.PlaySound(Sound 'ChunkHit',, 4.0,,100); if (Other.bIsPawn && STM.CheckHeadshot(Pawn(Other), HitLocation, X) && (instigator.IsA('PlayerPawn') || (instigator.IsA('Bot') && !Bot(Instigator).bNovice)) ) { + Other.PlaySound(Sound 'ChunkHit',, 4.0,,100); Other.TakeDamage( STM.WeaponSettings.SniperHeadshotDamage, PawnOwner, @@ -72,10 +71,12 @@ function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vect STM.WeaponSettings.SniperHeadshotMomentum * 35000 * X, AltDamageType); } else if (Other.bIsPawn == false || STM.CheckBodyShot(Pawn(Other), HitLocation, X)) { - if (Other.bIsPawn) + if (Other.bIsPawn) { + Other.PlaySound(Sound 'ChunkHit',, 4.0,,100); Momentum = STM.WeaponSettings.SniperMomentum * 30000.0*X; - else + } else { Momentum = 30000.0*X; + } Other.TakeDamage( STM.WeaponSettings.SniperDamage, From fd6ce57aea2ca293035483e933fe24050e0d25a9 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Tue, 16 Apr 2024 20:53:33 +0200 Subject: [PATCH 02/13] Grab Damage From PrioritizeArmor This works around idiocy in UT_ShieldBelt that destroys all armors on pickup. --- Classes/IGPlus_HitFeedbackTracker.uc | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Classes/IGPlus_HitFeedbackTracker.uc b/Classes/IGPlus_HitFeedbackTracker.uc index 1562474..e91ee16 100644 --- a/Classes/IGPlus_HitFeedbackTracker.uc +++ b/Classes/IGPlus_HitFeedbackTracker.uc @@ -8,17 +8,7 @@ function bool HandlePickupQuery(Inventory Item) { return false; } -function int ArmorPriority(name DamageType) { - return MaxInt; -} - -function int ArmorAbsorbDamage(int Damage, name DamageType, vector HitLocation) { +function Inventory PrioritizeArmor(int Damage, name DamageType, vector HitLocation) { LastDamage = Damage; - return Damage; -} - -defaultproperties { - bIsAnArmor=True - ArmorAbsorption=0 - Charge=0 + return super.PrioritizeArmor(Damage, DamageType, HitLocation); } From 6a339b3cbf5ae51e35d6faa01e8ce27a0dff15bd Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Wed, 17 Apr 2024 19:27:05 +0200 Subject: [PATCH 03/13] Update .int --- System/InstaGibPlus.int | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/System/InstaGibPlus.int b/System/InstaGibPlus.int index f14d867..e4326b3 100644 --- a/System/InstaGibPlus.int +++ b/System/InstaGibPlus.int @@ -86,8 +86,8 @@ NetStatsLocationErrorText="Show Location Error" NetStatsLocationErrorHelp="If checked, the netstats graph contains the difference between your predicted location and the actual location on the server" NetStatsFrameTimeText="Show Frame Time" NetStatsFrameTimeHelp="If checked, the netstats graph contains how long each frame took to create" -NetStatsWidthText="Demo Char" -NetStatsWidthHelp="Replacement character for those that cannot be used in file-names" +NetStatsWidthText="Graph Width" +NetStatsWidthHelp="Horizontal size of the graph in pixels, max is 511" NetStatsLocationText="Location" NetStatsLocationHelp="Where on screen to show the net stats graph" AutoDemoLblText="Auto Demo" From faf760d32be26f41baea66c5ddb28fabb1836600 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Wed, 17 Apr 2024 23:27:52 +0200 Subject: [PATCH 04/13] Rework HitSound Handling Fixes broken initialization logic that didnt pull hitsounds from the correct package. Hit sounds are now loaded eagerly to avoid lags during the match. --- Classes/ClientSettings.uc | 32 +++++++++++++++++-------------- Classes/IGPlus_SettingsContent.uc | 5 +---- Classes/bbCHSpectator.uc | 2 +- Classes/bbPlayer.uc | 2 +- Classes/bbPlayerStatics.uc | 12 ++---------- 5 files changed, 23 insertions(+), 30 deletions(-) diff --git a/Classes/ClientSettings.uc b/Classes/ClientSettings.uc index 01b04af..2b5bd37 100644 --- a/Classes/ClientSettings.uc +++ b/Classes/ClientSettings.uc @@ -133,6 +133,9 @@ var CrosshairLayer TopLayer; var config float MenuX, MenuY, MenuWidth, MenuHeight; +var Sound DefaultHitSound[16]; +var Sound LoadedHitSound[16]; + simulated function AppendLayer(CrosshairLayer L) { if (BottomLayer == none) { BottomLayer = L; @@ -164,21 +167,22 @@ simulated function CreateCrosshairLayers() { } } -simulated function CheckConfig(string PackageBaseName) { +simulated function LoadHitSounds() { local int i; - local string PackageName; - - PackageName = class'StringUtils'.static.GetPackage(); for (i = 0; i < arraycount(sHitSound); i++) { - if (Left(sHitSound[i], Len(PackageBaseName)) ~= PackageBaseName) { - sHitSound[i] = class'StringUtils'.static.GetPackage()$Mid(sHitSound[i], InStr(sHitSound[i], ".")); - } - if (sHitSound[i] == "" && sHitSound[i] != default.sHitSound[i]) { - sHitSound[i] = default.sHitSound[i]; - } + if (sHitSound[i] != "") + LoadedHitSound[i] = Sound(DynamicLoadObject(sHitSound[i], class'Sound', true)); + else + LoadedHitSound[i] = none; + + if (LoadedHitSound[i] == none && DefaultHitSound[i] != none) + LoadedHitSound[i] = DefaultHitSound[i]; } +} +simulated function CheckConfig() { + LoadHitSounds(); CreateCrosshairLayers(); if (FPSCounterSmoothingStrength <= 0) @@ -425,10 +429,10 @@ defaultproperties SelectedTeamHitSound=2 HitSoundVolume=4 HitSoundTeamVolume=4 - sHitSound(0)="InstaGibPlusAssets_v1.HitSound" - sHitSound(1)="UnrealShare.StingerFire" - sHitSound(2)="InstaGibPlusAssets_v1.HitSoundFriendly" - sHitSound(3)="InstaGibPlusAssets_v1.HitSound1" + DefaultHitSound(0)=Sound'HitSound' + DefaultHitSound(1)=Sound'StingerFire' + DefaultHitSound(2)=Sound'HitSoundFriendly' + DefaultHitSound(3)=Sound'HitSound1' cShockBeam=1 bHideOwnBeam=False bBeamEnableLight=True diff --git a/Classes/IGPlus_SettingsContent.uc b/Classes/IGPlus_SettingsContent.uc index 99ca406..286c216 100644 --- a/Classes/IGPlus_SettingsContent.uc +++ b/Classes/IGPlus_SettingsContent.uc @@ -772,6 +772,7 @@ function SaveHitSounds() { Settings.sHitSound[i++] = Item.Value; Item = UWindowComboListItem(Item.Next); } + Settings.LoadHitSounds(); } function ShowCrosshairFactoryDialog() { @@ -1147,10 +1148,6 @@ function Save() { Settings.HitSoundTeamVolume = float(Edit_HitSoundTeamVolume.GetValue()); Settings.SelectedTeamHitSound = Cmb_SelectedTeamHitSound.GetSelectedIndex2(); - // force reloading of hit sounds in case the selected ones changed - class'bbPlayerStatics'.default.PlayedHitSound = none; - class'bbPlayerStatics'.default.PlayedTeamHitSound = none; - SaveHitSounds(); Settings.bEnableKillCam = Chk_EnableKillCam.bChecked; diff --git a/Classes/bbCHSpectator.uc b/Classes/bbCHSpectator.uc index 93d0e97..ee1b956 100644 --- a/Classes/bbCHSpectator.uc +++ b/Classes/bbCHSpectator.uc @@ -152,7 +152,7 @@ simulated function InitSettings() { if (Settings == none) { ClientSettingsHelper = new(none, 'InstaGibPlus') class'Object'; Settings = new(ClientSettingsHelper, 'ClientSettings') class'ClientSettings'; - Settings.CheckConfig(VersionInfo.GetPropertyText("PackageBaseName")); + Settings.CheckConfig(); Log("Loaded Settings!", 'IGPlus'); } } diff --git a/Classes/bbPlayer.uc b/Classes/bbPlayer.uc index a7451e3..d0ef8fe 100644 --- a/Classes/bbPlayer.uc +++ b/Classes/bbPlayer.uc @@ -926,7 +926,7 @@ simulated function InitSettings() { if (Settings == none) { ClientSettingsHelper = new(none, StringUtils.StringToName(VersionInfo.GetPropertyText("PackageBaseName"))) class'Object'; // object name = INI file name Settings = new(ClientSettingsHelper, 'ClientSettings') class'ClientSettings'; // object name = Section name - Settings.CheckConfig(VersionInfo.GetPropertyText("PackageBaseName")); + Settings.CheckConfig(); Log("Loaded Settings!", 'IGPlus'); } } diff --git a/Classes/bbPlayerStatics.uc b/Classes/bbPlayerStatics.uc index 610dc4f..43f7863 100644 --- a/Classes/bbPlayerStatics.uc +++ b/Classes/bbPlayerStatics.uc @@ -10,8 +10,6 @@ var float HitMarkerLifespan; var float HitMarkerSize; var color HitMarkerColor; var color HitMarkerTeamColors[4]; -var Sound PlayedHitSound; -var Sound PlayedTeamHitSound; static function DrawFPS(Canvas C, HUD MyHud, ClientSettings Settings, float DeltaTime) { local string FPS; @@ -164,17 +162,11 @@ static function DrawHitMarker(Canvas C, ClientSettings Settings, float DeltaTime } static function Sound GetHitSound(ClientSettings Settings) { - if (default.PlayedHitSound == none) { - default.PlayedHitSound = Sound(DynamicLoadObject(Settings.sHitSound[Settings.SelectedHitSound], class'Sound')); - } - return default.PlayedHitSound; + return Settings.LoadedHitSound[Settings.SelectedHitSound]; } static function Sound GetTeamHitSound(ClientSettings Settings) { - if (default.PlayedTeamHitSound == none) { - default.PlayedTeamHitSound = Sound(DynamicLoadObject(Settings.sHitSound[Settings.SelectedTeamHitSound], class'Sound')); - } - return default.PlayedTeamHitSound; + return Settings.LoadedHitSound[Settings.SelectedTeamHitSound]; } static function PlaySoundWithPitch469(PlayerPawn Me, Sound S, float Volume, optional byte Priority, optional float Pitch) { From 53b170e8e9cc5a533f63fb4b16682db8ea0df07e Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Wed, 17 Apr 2024 23:29:35 +0200 Subject: [PATCH 05/13] Fix Compile Error --- Classes/bbPlayer.uc | 2 -- 1 file changed, 2 deletions(-) diff --git a/Classes/bbPlayer.uc b/Classes/bbPlayer.uc index d0ef8fe..d34e1aa 100644 --- a/Classes/bbPlayer.uc +++ b/Classes/bbPlayer.uc @@ -4580,7 +4580,6 @@ exec function setForcedTeamSkins(int maleSkin, int femaleSkin) { exec function SetHitSound(byte hs) { if (hs >= 0 && hs < 16) { Settings.SelectedHitSound = hs; - class'bbPlayerStatics'.default.PlayedHitSound = none; IGPlus_SaveSettings(); ClientMessage("HitSound set!"); } else { @@ -4591,7 +4590,6 @@ exec function SetHitSound(byte hs) { exec function SetTeamHitSound(byte hs) { if (hs >= 0 && hs < 16) { Settings.SelectedTeamHitSound = hs; - class'bbPlayerStatics'.default.PlayedTeamHitSound = none; IGPlus_SaveSettings(); ClientMessage("Team HitSound set!"); } else { From 40c32c7751caa64239a300c56741c691e48fdeb8 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Wed, 17 Apr 2024 23:56:07 +0200 Subject: [PATCH 06/13] Make Settings Menu Better Behaved Around HitSounds --- Classes/ClientSettings.uc | 2 +- Classes/IGPlus_SettingsContent.uc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/ClientSettings.uc b/Classes/ClientSettings.uc index 2b5bd37..535ecb7 100644 --- a/Classes/ClientSettings.uc +++ b/Classes/ClientSettings.uc @@ -171,7 +171,7 @@ simulated function LoadHitSounds() { local int i; for (i = 0; i < arraycount(sHitSound); i++) { - if (sHitSound[i] != "") + if (sHitSound[i] != "" && (sHitSound[i] ~= "none") == false) LoadedHitSound[i] = Sound(DynamicLoadObject(sHitSound[i], class'Sound', true)); else LoadedHitSound[i] = none; diff --git a/Classes/IGPlus_SettingsContent.uc b/Classes/IGPlus_SettingsContent.uc index 286c216..050c8ab 100644 --- a/Classes/IGPlus_SettingsContent.uc +++ b/Classes/IGPlus_SettingsContent.uc @@ -697,7 +697,7 @@ function SetUpHitSoundComboBox(IGPlus_ComboBox Cmb) { Cmb.EditBox.bDelayedNotify = true; Cmb.Clear(); for (i = 0; i < arraycount(Settings.sHitSound); ++i) - Cmb.AddItem(Settings.sHitSound[i], string(i)); + Cmb.AddItem(string(Settings.LoadedHitSound[i]), string(i)); } function SetUpHitSourceComboBox(IGPlus_ComboBox Cmb) { From 930ff16e4d3e5df216dcc2b595acf08bf90ac7cd Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Wed, 1 May 2024 23:20:57 +0200 Subject: [PATCH 07/13] Prevent Rockets From Firing On Death --- Classes/ST_UT_Eightball.uc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Classes/ST_UT_Eightball.uc b/Classes/ST_UT_Eightball.uc index 62d4a11..be193ca 100644 --- a/Classes/ST_UT_Eightball.uc +++ b/Classes/ST_UT_Eightball.uc @@ -70,8 +70,11 @@ state FireRockets local float Spread; local int i; + if (bCanClientFire == false) + return; + PawnOwner = Pawn(Owner); - if ( PawnOwner == None ) + if (PawnOwner == None) return; PawnOwner.PlayRecoil(FiringSpeed); PlayerOwner = PlayerPawn(Owner); From a36fa66ec69dbc5293246a5f625eecee9f438859 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Sun, 26 May 2024 18:39:29 +0200 Subject: [PATCH 08/13] Allow Negative Offsets To Be Entered For Crosshairs --- Classes/IGPlus_CrosshairSettingsContent.uc | 2 + Classes/IGPlus_EditBox.uc | 43 ++++++++++++++++++++++ Classes/IGPlus_EditControl.uc | 16 ++++++++ Classes/IGPlus_SettingsContent.uc | 1 + 4 files changed, 62 insertions(+) create mode 100644 Classes/IGPlus_EditBox.uc diff --git a/Classes/IGPlus_CrosshairSettingsContent.uc b/Classes/IGPlus_CrosshairSettingsContent.uc index 6070986..b086b0f 100644 --- a/Classes/IGPlus_CrosshairSettingsContent.uc +++ b/Classes/IGPlus_CrosshairSettingsContent.uc @@ -206,6 +206,7 @@ function Created() { Edit_OffsetX.SetDelayedNotify(true); Edit_OffsetX.SetNumericOnly(true); Edit_OffsetX.SetNumericFloat(false); + Edit_OffsetX.SetNumericNegative(true); Edit_OffsetX.EditBoxWidthFraction = (1.0/3.0); Edit_OffsetX.EditBoxWidth = 50; @@ -217,6 +218,7 @@ function Created() { Edit_OffsetY.SetDelayedNotify(true); Edit_OffsetY.SetNumericOnly(true); Edit_OffsetY.SetNumericFloat(false); + Edit_OffsetY.SetNumericNegative(true); Edit_OffsetY.EditBoxWidthFraction = (1.0/3.0); Edit_OffsetY.EditBoxWidth = 50; diff --git a/Classes/IGPlus_EditBox.uc b/Classes/IGPlus_EditBox.uc new file mode 100644 index 0000000..2b38f4e --- /dev/null +++ b/Classes/IGPlus_EditBox.uc @@ -0,0 +1,43 @@ +class IGPlus_EditBox extends UWindowEditBox; + +var bool bNumericNegative; + +function KeyType(int Key, float MouseX, float MouseY) { + if (bCanEdit == false || bKeyDown == false) + return; + + if (bControlDown) + return; + + if (bAllSelected) + Clear(); + + bAllSelected = False; + + if (bNumericOnly) { + if (Key >= 0x30 && Key <= 0x39) + Insert(Key); + if (bNumericNegative) { + if (Key == 0x2D && Left(Value, 1) != "-") { + Value = "-"$Value; + CaretOffset += 1; + + if (bDelayedNotify) + bChangePending = true; + else + Notify(DE_Change); + } else if (Key == 0x2B && Left(Value, 1) == "-") { + Value = Mid(Value, 1); + CaretOffset -= 1; + + if (bDelayedNotify) + bChangePending = true; + else + Notify(DE_Change); + } + } + } else { + if (Key >= 0x20 && Key < 0x80) + Insert(Key); + } +} \ No newline at end of file diff --git a/Classes/IGPlus_EditControl.uc b/Classes/IGPlus_EditControl.uc index b4fbcd9..1730db5 100644 --- a/Classes/IGPlus_EditControl.uc +++ b/Classes/IGPlus_EditControl.uc @@ -6,12 +6,28 @@ var float EditBoxMinWidth; var float EditBoxMaxWidth; var float EditBoxWidthFraction; +function Created() { + Super.Created(); + + EditBox = IGPlus_EditBox(CreateWindow(class'IGPlus_EditBox', 0, 0, WinWidth, WinHeight)); + EditBox.NotifyOwner = Self; + EditBox.bSelectOnFocus = True; + + EditBoxWidth = WinWidth / 2; + + SetEditTextColor(LookAndFeel.EditBoxTextColor); +} + function AfterCreate() { EditBoxWidthFraction = FClamp(EditBoxWidth / WinWidth, 0.05, 0.95); EditBoxMinWidth = WinWidth * EditBoxWidthFraction; EditBoxMaxWidth = WinWidth * EditBoxWidthFraction; } +function SetNumericNegative(bool bEnable) { + IGPlus_EditBox(EditBox).bNumericNegative = bEnable; +} + function MouseLeave() { Super.MouseLeave(); if(HelpText != "") ToolTip(""); diff --git a/Classes/IGPlus_SettingsContent.uc b/Classes/IGPlus_SettingsContent.uc index 050c8ab..7188a3e 100644 --- a/Classes/IGPlus_SettingsContent.uc +++ b/Classes/IGPlus_SettingsContent.uc @@ -908,6 +908,7 @@ function Created() { Cmb_HitMarkerColorMode.AddItem(HitMarkerColorModeTeamColor); Edit_HitMarkerSize = CreateEdit(ECT_Integer, HitMarkerSizeText, HitMarkerSizeHelp, , 64); Edit_HitMarkerOffset = CreateEdit(ECT_Integer, HitMarkerOffsetText, HitMarkerOffsetHelp, , 64); + Edit_HitMarkerOffset.SetNumericNegative(true); Edit_HitMarkerDuration = CreateEdit(ECT_Real, HitMarkerDurationText, HitMarkerDurationHelp, , 64); Edit_HitMarkerDecayExponent = CreateEdit(ECT_Real, HitMarkerDecayExponentText, HitMarkerDecayExponentHelp, , 64); Chk_EnableHitMarker = CreateCheckbox(EnableHitMarkerText, EnableHitMarkerHelp); From 6cbc9508540b02d684dbbdc02916e7699de995bd Mon Sep 17 00:00:00 2001 From: w0ss4g3 <97363958+w0ss4g3@users.noreply.github.com> Date: Thu, 25 Jul 2024 18:42:03 +0100 Subject: [PATCH 09/13] Changed EnhancedSplash so that it can be controlled for each weapon. (#26) Authored-by: w0ss4g3 Added settings to control each weapon. Made default settings to only enable enhanced splash for ripper, flak and rockets. --- Classes/ST_FlakSlug.uc | 2 +- Classes/ST_Razor2Alt.uc | 2 +- Classes/ST_RocketMk2.uc | 2 +- Classes/ST_ShockProj.uc | 4 ++-- Classes/ST_UT_Grenade.uc | 2 +- Classes/ST_UT_SeekingRocket.uc | 2 +- Classes/WeaponSettings.uc | 14 ++++++++++---- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Classes/ST_FlakSlug.uc b/Classes/ST_FlakSlug.uc index 4d7acc1..0427ec5 100644 --- a/Classes/ST_FlakSlug.uc +++ b/Classes/ST_FlakSlug.uc @@ -19,7 +19,7 @@ function NewExplode(vector HitLocation, vector HitNormal, bool bDirect) local vector start; local ST_UTChunkInfo CI; - if (STM.WeaponSettings.bEnableEnhancedSplash) { + if (STM.WeaponSettings.bEnableEnhancedSplashFlakSlug) { STM.EnhancedHurtRadius( self, STM.WeaponSettings.FlakSlugDamage, diff --git a/Classes/ST_Razor2Alt.uc b/Classes/ST_Razor2Alt.uc index 841131a..5bb40aa 100644 --- a/Classes/ST_Razor2Alt.uc +++ b/Classes/ST_Razor2Alt.uc @@ -53,7 +53,7 @@ auto state Flying local float damageScale, dist; local vector dir; - if (STM.WeaponSettings.bEnableEnhancedSplash) { + if (STM.WeaponSettings.bEnableEnhancedSplashRipperSecondary) { STM.EnhancedHurtRadius( self, STM.WeaponSettings.RipperSecondaryDamage, diff --git a/Classes/ST_RocketMk2.uc b/Classes/ST_RocketMk2.uc index e33b733..a561cef 100644 --- a/Classes/ST_RocketMk2.uc +++ b/Classes/ST_RocketMk2.uc @@ -52,7 +52,7 @@ auto state Flying function BlowUp(vector HitLocation) { - if (STM.WeaponSettings.bEnableEnhancedSplash) { + if (STM.WeaponSettings.bEnableEnhancedSplashRockets) { STM.EnhancedHurtRadius( self, STM.WeaponSettings.RocketDamage, diff --git a/Classes/ST_ShockProj.uc b/Classes/ST_ShockProj.uc index 5322e7e..103885a 100644 --- a/Classes/ST_ShockProj.uc +++ b/Classes/ST_ShockProj.uc @@ -17,7 +17,7 @@ simulated function PostBeginPlay() { } function SuperExplosion() { - if (STM.WeaponSettings.bEnableEnhancedSplashCombo) { + if (STM.WeaponSettings.bEnableEnhancedSplashShockCombo) { STM.EnhancedHurtRadius( self, STM.WeaponSettings.ShockComboDamage, @@ -42,7 +42,7 @@ function SuperExplosion() { function Explode(vector HitLocation,vector HitNormal) { PlaySound(ImpactSound, SLOT_Misc, 0.5,,, 0.5+FRand()); - if (STM.WeaponSettings.bEnableEnhancedSplash) { + if (STM.WeaponSettings.bEnableEnhancedSplashShockProjectile) { STM.EnhancedHurtRadius( self, STM.WeaponSettings.ShockProjectileDamage, diff --git a/Classes/ST_UT_Grenade.uc b/Classes/ST_UT_Grenade.uc index f937f1a..d36e58e 100644 --- a/Classes/ST_UT_Grenade.uc +++ b/Classes/ST_UT_Grenade.uc @@ -28,7 +28,7 @@ function Explosion(vector HitLocation) function BlowUp(vector HitLocation) { - if (STM.WeaponSettings.bEnableEnhancedSplash) { + if (STM.WeaponSettings.bEnableEnhancedSplashRockets) { STM.EnhancedHurtRadius( self, STM.WeaponSettings.GrenadeDamage, diff --git a/Classes/ST_UT_SeekingRocket.uc b/Classes/ST_UT_SeekingRocket.uc index a6955d2..0088aca 100644 --- a/Classes/ST_UT_SeekingRocket.uc +++ b/Classes/ST_UT_SeekingRocket.uc @@ -51,7 +51,7 @@ auto state Flying function BlowUp(vector HitLocation) { - if (STM.WeaponSettings.bEnableEnhancedSplash) { + if (STM.WeaponSettings.bEnableEnhancedSplashRockets) { STM.EnhancedHurtRadius( self, STM.WeaponSettings.RocketDamage, diff --git a/Classes/WeaponSettings.uc b/Classes/WeaponSettings.uc index 45f806b..112bdec 100644 --- a/Classes/WeaponSettings.uc +++ b/Classes/WeaponSettings.uc @@ -14,9 +14,12 @@ var config bool bReplaceRocketLauncher; var config bool bReplaceSniperRifle; var config bool bReplaceWarheadLauncher; -var config bool bEnableEnhancedSplash; var config bool bEnableEnhancedSplashBio; -var config bool bEnableEnhancedSplashCombo; +var config bool bEnableEnhancedSplashShockCombo; +var config bool bEnableEnhancedSplashShockProjectile; +var config bool bEnableEnhancedSplashRipperSecondary; +var config bool bEnableEnhancedSplashFlakSlug; +var config bool bEnableEnhancedSplashRockets; var config bool bEnhancedSplashIgnoreStationaryPawns; var config float SplashMaxDiffraction; var config float SplashMinDiffractionDistance; @@ -164,9 +167,12 @@ defaultproperties bReplaceSniperRifle=True bReplaceWarheadLauncher=True - bEnableEnhancedSplash=True bEnableEnhancedSplashBio=False - bEnableEnhancedSplashCombo=False + bEnableEnhancedSplashShockCombo=False + bEnableEnhancedSplashShockProjectile=False + bEnableEnhancedSplashRipperSecondary=True + bEnableEnhancedSplashFlakSlug=True + bEnableEnhancedSplashRockets=True SplashMaxDiffraction=0.5 SplashMinDiffractionDistance=50.0 From 869975db95e2aa91785293641b66a7ed2d4105a5 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Fri, 26 Jul 2024 11:22:44 +0200 Subject: [PATCH 10/13] Fix SniperRifle Shots Ending On Hitboxes --- Classes/ST_Mutator.uc | 33 +++++++++++++++++++++++++++++++++ Classes/ST_Razor2.uc | 2 +- Classes/ST_SniperRifle.uc | 25 +++++++++++++++++++++---- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Classes/ST_Mutator.uc b/Classes/ST_Mutator.uc index 599262e..79cb65e 100644 --- a/Classes/ST_Mutator.uc +++ b/Classes/ST_Mutator.uc @@ -510,6 +510,39 @@ function bool CheckBodyShot(Pawn P, vector HitLocation, vector Direction) { return Result; } +function Actor TraceShot(out vector HitLocation, out vector HitNormal, vector EndTrace, vector StartTrace, Pawn PawnOwner) { + local Actor A, Other; + local Pawn P; + local bool bSProjBlocks; + local bool bWeaponShock; + local vector Dir; + + bSProjBlocks = WeaponSettings.ShockProjectileBlockBullets; + bWeaponShock = (PawnOwner.Weapon != none && PawnOwner.Weapon.IsA('ShockRifle')); + Dir = Normal(EndTrace - StartTrace); + + foreach TraceActors(class'Actor', A, HitLocation, HitNormal, EndTrace, StartTrace) { + P = Pawn(A); + if (P != none) { + if (P == PawnOwner) + continue; + if (P.AdjustHitLocation(HitLocation, EndTrace - StartTrace) == false) + continue; + if (CheckBodyShot(P, HitLocation, Dir) == false && CheckHeadShot(P, HitLocation, Dir) == false) + continue; + + Other = A; + } else if ((A == Level) || (Mover(A) != None) || A.bProjTarget || (A.bBlockPlayers && A.bBlockActors)) { + if (bSProjBlocks || A.IsA('ShockProj') == false || bWeaponShock) + Other = A; + } + + if (Other != none) + break; + } + return Other; +} + defaultproperties { DefaultWeapon=Class'ST_ImpactHammer' diff --git a/Classes/ST_Razor2.uc b/Classes/ST_Razor2.uc index aa57946..1deafe0 100644 --- a/Classes/ST_Razor2.uc +++ b/Classes/ST_Razor2.uc @@ -27,7 +27,7 @@ auto state Flying Dir = Normal(Velocity); if (bCanHitInstigator || (Other != Instigator)) { if (Role == ROLE_Authority) { - if (Other.bIsPawn && STM.CheckHeadshot(Pawn(Other), HitLocation, Dir) && + if (Other.bIsPawn && STM.CheckHeadShot(Pawn(Other), HitLocation, Dir) && (!Instigator.IsA('Bot') || !Bot(Instigator).bNovice) ) { Other.TakeDamage( diff --git a/Classes/ST_SniperRifle.uc b/Classes/ST_SniperRifle.uc index bb4b820..1089317 100644 --- a/Classes/ST_SniperRifle.uc +++ b/Classes/ST_SniperRifle.uc @@ -43,6 +43,23 @@ function PostBeginPlay() break; // Find master :D } +function TraceFire(float Accuracy) { + local vector HitLocation, HitNormal, StartTrace, EndTrace, X,Y,Z; + local actor Other; + local Pawn PawnOwner; + + PawnOwner = Pawn(Owner); + + Owner.MakeNoise(PawnOwner.SoundDampening); + GetAxes(PawnOwner.ViewRotation,X,Y,Z); + StartTrace = Owner.Location + PawnOwner.Eyeheight * vect(0,0,1); + AdjustedAim = PawnOwner.AdjustAim(1000000, StartTrace, 2*AimError, False, False); + X = vector(AdjustedAim); + EndTrace = StartTrace + 10000 * X; + Other = STM.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace, PawnOwner); + ProcessTraceHit(Other, HitLocation, HitNormal, X,Y,Z); +} + function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vector X, Vector Y, Vector Z) { local UT_Shellcase s; @@ -60,7 +77,7 @@ function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vect if (Other == Level) { Spawn(class'UT_HeavyWallHitEffect',,, HitLocation+HitNormal, Rotator(HitNormal)); } else if ((Other != self) && (Other != Owner) && (Other != None)) { - if (Other.bIsPawn && STM.CheckHeadshot(Pawn(Other), HitLocation, X) && + if (Other.bIsPawn && STM.CheckHeadShot(Pawn(Other), HitLocation, X) && (instigator.IsA('PlayerPawn') || (instigator.IsA('Bot') && !Bot(Instigator).bNovice)) ) { Other.PlaySound(Sound 'ChunkHit',, 4.0,,100); @@ -70,12 +87,14 @@ function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vect HitLocation, STM.WeaponSettings.SniperHeadshotMomentum * 35000 * X, AltDamageType); - } else if (Other.bIsPawn == false || STM.CheckBodyShot(Pawn(Other), HitLocation, X)) { + } else { if (Other.bIsPawn) { Other.PlaySound(Sound 'ChunkHit',, 4.0,,100); Momentum = STM.WeaponSettings.SniperMomentum * 30000.0*X; } else { Momentum = 30000.0*X; + if (Other.IsA('Carcass') == false) + Spawn(class'UT_SpriteSmokePuff',,,HitLocation+HitNormal*9); } Other.TakeDamage( @@ -85,8 +104,6 @@ function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vect Momentum, MyDamageType); } - if (!Other.bIsPawn && !Other.IsA('Carcass')) - Spawn(class'UT_SpriteSmokePuff',,,HitLocation+HitNormal*9); } } From 6aceb1532b1a0bae74fe81af24fa2eef59b11881 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Fri, 26 Jul 2024 18:37:35 +0200 Subject: [PATCH 11/13] Increase Range Of SniperRifle --- Classes/ST_SniperRifle.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/ST_SniperRifle.uc b/Classes/ST_SniperRifle.uc index 1089317..ebb3491 100644 --- a/Classes/ST_SniperRifle.uc +++ b/Classes/ST_SniperRifle.uc @@ -55,7 +55,7 @@ function TraceFire(float Accuracy) { StartTrace = Owner.Location + PawnOwner.Eyeheight * vect(0,0,1); AdjustedAim = PawnOwner.AdjustAim(1000000, StartTrace, 2*AimError, False, False); X = vector(AdjustedAim); - EndTrace = StartTrace + 10000 * X; + EndTrace = StartTrace + 100000 * X; Other = STM.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace, PawnOwner); ProcessTraceHit(Other, HitLocation, HitNormal, X,Y,Z); } From c05ef0a742fd205747710885ee311a1cf519e790 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Mon, 29 Jul 2024 23:11:59 +0200 Subject: [PATCH 12/13] Dont Use Actors As Outer For Objects --- Classes/bbPlayer.uc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/bbPlayer.uc b/Classes/bbPlayer.uc index d34e1aa..bdf083d 100644 --- a/Classes/bbPlayer.uc +++ b/Classes/bbPlayer.uc @@ -944,7 +944,7 @@ event PostBeginPlay() VersionInfoClass = class(DynamicLoadObject(StringUtils.GetPackage()$".VersionInfo", class'class', true)); VersionInfo = Spawn(VersionInfoClass); IGPlus_SavedInputChain = Spawn(class'IGPlus_SavedInputChain'); - IGPlus_InputReplicationBuffer = new class'IGPlus_DataBuffer'; + IGPlus_InputReplicationBuffer = new(XLevel) class'IGPlus_DataBuffer'; InitSettings(); @@ -982,7 +982,7 @@ simulated event PostNetBeginPlay() VersionInfoClass = class(DynamicLoadObject(StringUtils.GetPackage()$".VersionInfo", class'class', true)); VersionInfo = Spawn(VersionInfoClass); IGPlus_SavedInputChain = Spawn(class'IGPlus_SavedInputChain'); - IGPlus_InputReplicationBuffer = new class'IGPlus_DataBuffer'; + IGPlus_InputReplicationBuffer = new(XLevel) class'IGPlus_DataBuffer'; InitSettings(); From 1d370fd5dbb5bb5907af35a14a495165350b9d06 Mon Sep 17 00:00:00 2001 From: Lukas Bagaric Date: Mon, 29 Jul 2024 23:31:38 +0200 Subject: [PATCH 13/13] Apply Reduced Hitbox To Sniper/Enforcer/Shock Beam Removed weapon setting bEnhancedHeadshotDetection. Added weapon setting SniperUseReducedHitbox. Added weapon setting ShockBeamUseReducedHitbox. Added weapon setting EnforcerUseReducedHitbox. SniperRifle behavior unaffected if SniperUseReducedHitbox matches bEnhancedHeadshotDetection before. Ripper headshot will always use default behavior now. --- Classes/ST_Mutator.uc | 6 ------ Classes/ST_Razor2.uc | 4 ++-- Classes/ST_ShockRifle.uc | 33 +++++++++++++++++++++++++++++++ Classes/ST_SniperRifle.uc | 16 ++++++++++++--- Classes/ST_enforcer.uc | 41 +++++++++++++++++++++++++++++++++++++++ Classes/WeaponSettings.uc | 8 ++++++-- 6 files changed, 95 insertions(+), 13 deletions(-) diff --git a/Classes/ST_Mutator.uc b/Classes/ST_Mutator.uc index 79cb65e..28262d7 100644 --- a/Classes/ST_Mutator.uc +++ b/Classes/ST_Mutator.uc @@ -421,9 +421,6 @@ function bool CheckHeadShot(Pawn P, vector HitLocation, vector Direction) { if (P == none) return false; - if (WeaponSettings.bEnhancedHeadshotDetection == false) - return (HitLocation.Z - P.Location.Z > 0.62 * P.CollisionHeight); - if (HitLocation.Z - P.Location.Z <= 0.3 * P.CollisionHeight) return false; @@ -475,9 +472,6 @@ function bool CheckBodyShot(Pawn P, vector HitLocation, vector Direction) { if (P == none) return false; - if (WeaponSettings.bEnhancedHeadshotDetection == false) - return CheckHeadShot(P, HitLocation, Direction) == false; - if (CollChecker == none || CollChecker.bDeleteMe) { CollChecker = Spawn(class'ST_HitTestHelper',self, , P.Location); CollChecker.bCollideWorld = false; diff --git a/Classes/ST_Razor2.uc b/Classes/ST_Razor2.uc index 1deafe0..80180eb 100644 --- a/Classes/ST_Razor2.uc +++ b/Classes/ST_Razor2.uc @@ -27,7 +27,7 @@ auto state Flying Dir = Normal(Velocity); if (bCanHitInstigator || (Other != Instigator)) { if (Role == ROLE_Authority) { - if (Other.bIsPawn && STM.CheckHeadShot(Pawn(Other), HitLocation, Dir) && + if (Other.bIsPawn && (HitLocation.Z - Other.Location.Z > 0.62 * Other.CollisionHeight) && (!Instigator.IsA('Bot') || !Bot(Instigator).bNovice) ) { Other.TakeDamage( @@ -37,7 +37,7 @@ auto state Flying STM.WeaponSettings.RipperHeadshotMomentum * MomentumTransfer * Dir, 'decapitated' ); - } else if (Other.bIsPawn == false || STM.CheckBodyShot(Pawn(Other), HitLocation, Dir)) { + } else { Other.TakeDamage( STM.WeaponSettings.RipperPrimaryDamage, instigator, diff --git a/Classes/ST_ShockRifle.uc b/Classes/ST_ShockRifle.uc index 07dba65..e9a244c 100644 --- a/Classes/ST_ShockRifle.uc +++ b/Classes/ST_ShockRifle.uc @@ -35,6 +35,39 @@ function PostBeginPlay() break; // Find master :D } +function TraceFire(float Accuracy) { + local vector HitLocation, HitNormal, StartTrace, EndTrace, X,Y,Z; + local actor Other; + local Pawn PawnOwner; + + PawnOwner = Pawn(Owner); + + Owner.MakeNoise(PawnOwner.SoundDampening); + GetAxes(PawnOwner.ViewRotation,X,Y,Z); + StartTrace = Owner.Location + CalcDrawOffset() + FireOffset.Y * Y + FireOffset.Z * Z; + EndTrace = StartTrace + (Accuracy * (FRand() - 0.5 )* Y * 1000) + (Accuracy * (FRand() - 0.5 ) * Z * 1000); + + if (bBotSpecialMove && (Tracked != None) && ( + ((Owner.Acceleration == vect(0,0,0)) && (VSize(Owner.Velocity) < 40)) || + (Normal(Owner.Velocity) Dot Normal(Tracked.Velocity) > 0.95) + ) + ) { + EndTrace += 10000 * Normal(Tracked.Location - StartTrace); + } else { + AdjustedAim = PawnOwner.AdjustAim(1000000, StartTrace, 2.75*AimError, False, False); + EndTrace += (10000 * vector(AdjustedAim)); + } + + Tracked = None; + bBotSpecialMove = false; + + if (STM.WeaponSettings.ShockBeamUseReducedHitbox) + Other = STM.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace, PawnOwner); + else + Other = PawnOwner.TraceShot(HitLocation,HitNormal,EndTrace,StartTrace); + ProcessTraceHit(Other, HitLocation, HitNormal, vector(AdjustedAim),Y,Z); +} + function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vector X, Vector Y, Vector Z) { local PlayerPawn PlayerOwner; diff --git a/Classes/ST_SniperRifle.uc b/Classes/ST_SniperRifle.uc index ebb3491..de16468 100644 --- a/Classes/ST_SniperRifle.uc +++ b/Classes/ST_SniperRifle.uc @@ -55,8 +55,11 @@ function TraceFire(float Accuracy) { StartTrace = Owner.Location + PawnOwner.Eyeheight * vect(0,0,1); AdjustedAim = PawnOwner.AdjustAim(1000000, StartTrace, 2*AimError, False, False); X = vector(AdjustedAim); - EndTrace = StartTrace + 100000 * X; - Other = STM.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace, PawnOwner); + EndTrace = StartTrace + 100000 * X; + if (STM.WeaponSettings.SniperUseReducedHitbox) + Other = STM.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace, PawnOwner); + else + Other = PawnOwner.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace); ProcessTraceHit(Other, HitLocation, HitNormal, X,Y,Z); } @@ -77,7 +80,7 @@ function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vect if (Other == Level) { Spawn(class'UT_HeavyWallHitEffect',,, HitLocation+HitNormal, Rotator(HitNormal)); } else if ((Other != self) && (Other != Owner) && (Other != None)) { - if (Other.bIsPawn && STM.CheckHeadShot(Pawn(Other), HitLocation, X) && + if (Other.bIsPawn && CheckHeadShot(Pawn(Other), HitLocation, X) && (instigator.IsA('PlayerPawn') || (instigator.IsA('Bot') && !Bot(Instigator).bNovice)) ) { Other.PlaySound(Sound 'ChunkHit',, 4.0,,100); @@ -107,6 +110,13 @@ function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vect } } +function bool CheckHeadShot(Pawn P, vector HitLocation, vector BulletDir) { + if (STM.WeaponSettings.SniperUseReducedHitbox == false) + return (HitLocation.Z - P.Location.Z > 0.62 * P.CollisionHeight); + + return STM.CheckHeadShot(P, HitLocation, BulletDir); +} + function SetSwitchPriority(pawn Other) { // Make sure "old" priorities are kept. local int i; diff --git a/Classes/ST_enforcer.uc b/Classes/ST_enforcer.uc index d552dff..173baef 100644 --- a/Classes/ST_enforcer.uc +++ b/Classes/ST_enforcer.uc @@ -35,6 +35,47 @@ function PostBeginPlay() break; // Find master :D } +function TraceFire(float Accuracy) { + local vector RealOffset; + local vector HitLocation, HitNormal, StartTrace, EndTrace, X,Y,Z; + local Actor Other; + local Pawn PawnOwner; + + RealOffset = FireOffset; + FireOffset *= 0.35; + if ( (SlaveEnforcer != None) || bIsSlave ) + Accuracy = FClamp(3*Accuracy,0.75,3); + else if ( Owner.IsA('Bot') && !Bot(Owner).bNovice ) + Accuracy = FMax(Accuracy, 0.45); + + PawnOwner = Pawn(Owner); + + Owner.MakeNoise(PawnOwner.SoundDampening); + GetAxes(PawnOwner.ViewRotation,X,Y,Z); + StartTrace = Owner.Location + CalcDrawOffset() + FireOffset.X * X + FireOffset.Y * Y + FireOffset.Z * Z; + AdjustedAim = PawnOwner.AdjustAim(1000000, StartTrace, 2*AimError, False, False); + EndTrace = StartTrace + Accuracy * (FRand() - 0.5 )* Y * 1000 + + Accuracy * (FRand() - 0.5 ) * Z * 1000; + X = vector(AdjustedAim); + EndTrace += (10000 * X); + if (STM.WeaponSettings.EnforcerUseReducedHitbox) + Other = STM.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace, PawnOwner); + else + Other = PawnOwner.TraceShot(HitLocation, HitNormal, EndTrace, StartTrace); + ProcessTraceHit(Other, HitLocation, HitNormal, X,Y,Z); + + FireOffset = RealOffset; + + // Higor: move slave enforcer to TraceFire start location + // to ensure firing sounds are played from the right place + if (Owner != None && (Level.NetMode == NM_DedicatedServer || Level.NetMode == NM_ListenServer)) { + if (bIsSlave && !bCollideActors) + SetLocation(Owner.Location + CalcDrawOffset()); + else if (SlaveEnforcer != None && !SlaveEnforcer.bCollideActors) + SlaveEnforcer.SetLocation(Owner.Location + SlaveEnforcer.CalcDrawOffset()); + } +} + function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vector X, Vector Y, Vector Z) { local UT_Shellcase s; diff --git a/Classes/WeaponSettings.uc b/Classes/WeaponSettings.uc index 112bdec..9a40398 100644 --- a/Classes/WeaponSettings.uc +++ b/Classes/WeaponSettings.uc @@ -24,7 +24,6 @@ var config bool bEnhancedSplashIgnoreStationaryPawns; var config float SplashMaxDiffraction; var config float SplashMinDiffractionDistance; -var config bool bEnhancedHeadshotDetection; var config float HeadHalfHeight; var config float HeadRadius; @@ -38,6 +37,7 @@ var config float SniperHeadshotDamage; var config float SniperMomentum; var config float SniperHeadshotMomentum; var config float SniperReloadTime; +var config bool SniperUseReducedHitbox; var config float EightballSelectTime; var config float EightballDownTime; @@ -97,6 +97,7 @@ var config float ShockSelectTime; var config float ShockDownTime; var config float ShockBeamDamage; var config float ShockBeamMomentum; +var config bool ShockBeamUseReducedHitbox; var config float ShockProjectileDamage; var config float ShockProjectileHurtRadius; var config float ShockProjectileMomentum; @@ -124,6 +125,7 @@ var config float EnforcerMomentum; var config float EnforcerReloadTime; var config float EnforcerReloadTimeAlt; var config float EnforcerReloadTimeRepeat; +var config bool EnforcerUseReducedHitbox; var config bool EnforcerAllowDouble; var config float EnforcerDamageDouble; @@ -176,7 +178,6 @@ defaultproperties SplashMaxDiffraction=0.5 SplashMinDiffractionDistance=50.0 - bEnhancedHeadshotDetection=False HeadHalfHeight=7.5 HeadRadius=10.0 @@ -190,6 +191,7 @@ defaultproperties SniperMomentum=1.0 SniperHeadshotMomentum=1.0 SniperReloadTime=0.6666666666 + SniperUseReducedHitbox=False EightballSelectTime=0.606061 EightballDownTime=0.366667 @@ -249,6 +251,7 @@ defaultproperties ShockDownTime=0.259259 ShockBeamDamage=40 ShockBeamMomentum=1.0 + ShockBeamUseReducedHitbox=False ShockProjectileDamage=55 ShockProjectileHurtRadius=70 ShockProjectileMomentum=1.0 @@ -276,6 +279,7 @@ defaultproperties EnforcerReloadTime=0.27 EnforcerReloadTimeAlt=0.26 EnforcerReloadTimeRepeat=0.266667 + EnforcerUseReducedHitbox=False EnforcerAllowDouble=True EnforcerDamageDouble=17