From bb536b2d601c1bbbd3e92f18c0bbf1ddfd894e8a Mon Sep 17 00:00:00 2001
From: SamaelGray <56392968+SamaelGray@users.noreply.github.com>
Date: Sun, 27 Aug 2023 21:29:40 +0330
Subject: [PATCH 001/158] VFE - Deserters patch
---
.../Apparel_Belts.xml | 23 ++
.../Buildings_Security_Turrets.xml | 15 ++
.../Damages_Empire.xml | 23 ++
.../RangedSpacer.xml | 251 ++++++++++++++++++
4 files changed, 312 insertions(+)
create mode 100644 Patches/Vanilla Factions Expanded - Deserters/Apparel_Belts.xml
create mode 100644 Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
create mode 100644 Patches/Vanilla Factions Expanded - Deserters/Damages_Empire.xml
create mode 100644 Patches/Vanilla Factions Expanded - Deserters/RangedSpacer.xml
diff --git a/Patches/Vanilla Factions Expanded - Deserters/Apparel_Belts.xml b/Patches/Vanilla Factions Expanded - Deserters/Apparel_Belts.xml
new file mode 100644
index 0000000000..88de27692c
--- /dev/null
+++ b/Patches/Vanilla Factions Expanded - Deserters/Apparel_Belts.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ Vanilla Factions Expanded - Deserters
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Apparel_BombPack" or defName="VFED_Apparel_InvisibilityEngulfer" or defName="VFED_DeserterDeclassifier"]/statBases
+
+ 3
+ 1
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml b/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
new file mode 100644
index 0000000000..e5beaf9ada
--- /dev/null
+++ b/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ Vanilla Factions Expanded - Deserters
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Patches/Vanilla Factions Expanded - Deserters/Damages_Empire.xml b/Patches/Vanilla Factions Expanded - Deserters/Damages_Empire.xml
new file mode 100644
index 0000000000..be33fc2ddb
--- /dev/null
+++ b/Patches/Vanilla Factions Expanded - Deserters/Damages_Empire.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ Vanilla Factions Expanded - Deserters
+
+
+
+
+
+ Defs/DamageDef[defName="VFED_Fletchling"]
+
+ 0.05
+ 0.05
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Patches/Vanilla Factions Expanded - Deserters/RangedSpacer.xml b/Patches/Vanilla Factions Expanded - Deserters/RangedSpacer.xml
new file mode 100644
index 0000000000..b6a21e8f52
--- /dev/null
+++ b/Patches/Vanilla Factions Expanded - Deserters/RangedSpacer.xml
@@ -0,0 +1,251 @@
+
+
+
+
+
+ Vanilla Factions Expanded - Deserters
+
+
+
+
+
+
+ Defs/ThingDef[defName = "VFED_Gun_ChargeCycler"]/tools
+
+
+
+ grip
+
+ Blunt
+
+ 2
+ 1.54
+ 1.5
+ 0.555
+ Grip
+
+
+ muzzle
+
+ Poke
+
+ 2
+ 1.54
+ 0.555
+ Muzzle
+
+
+
+
+
+
+ Defs/ThingDef[defName = "VFED_Gun_Fletchling"]/tools
+
+
+
+ stock
+
+ Blunt
+
+ 8
+ 1.55
+ 1.5
+ 2.755
+ Stock
+
+
+ barrel
+
+ Blunt
+
+ 5
+ 2.02
+ 1.630
+ Barrel
+
+
+ muzzle
+
+ Poke
+
+ 8
+ 1.55
+ 2.755
+ Muzzle
+
+
+
+
+
+
+
+ VFED_Gun_ChargeCycler
+
+ 2.0
+ 3.0
+ 1.15
+ 0.15
+ 0.8
+ 0.36
+
+
+ 8
+ 4
+ AmmoSet_6x18mmCharged
+
+
+ Snapshot
+
+
+ CE_Sidearm
+ CE_OneHandedWeapon
+
+
+
+
+ Defs/ThingDef[defName="VFED_Gun_ChargeCycler"]/verbs
+
+
+
+ 3.0
+ CombatExtended.Verb_ShootCE
+ True
+ Bullet_6x18mmCharged
+ 0.5
+ 12
+ VFED_Shot_ChargeCycler
+ GunTail_Light
+ 9
+
+
+
+
+
+
+ Defs/ThingDef[defName = "VFED_Gun_ChargeCycler"]/comps/li[@Class="MVCF.Comps.CompProperties_VerbProps"]
+
+
+ CompEquippable
+
+
+
+ 1
+ 3
+ AmmoSet_40x68mmDemo
+
+
+ 2.0
+ CombatExtended.Verb_ShootCE
+ true
+ Bullet_40x68mmDemo_Thump
+ 1.1
+ 25
+
+ true
+
+ ThumpCannon_Fire
+ GunTail_Medium
+ 5
+
+
+ FALSE
+ SuppressFire
+
+
+
+
+
+
+
+
+ Defs
+
+
+
+ AmmoSet_FletchlingDart
+ Fletchling Dart
+
+ Bullet_FletchlingDart
+
+ AmmoSet_ChargedHeavy
+
+
+
+ Bullet_FletchlingDart
+ fletchling dart
+
+ Projectile/Bullet_Fletcher
+ Graphic_Single
+ TransparentPostLight
+
+
+ 2
+ 62
+ VFED_Fletchling
+ 3
+ 8
+ 18
+ 8.9
+
+
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Gun_ChargeCycler"]/verbs
+
+
+
+ 3.0
+ CombatExtended.Verb_ShootCE
+ True
+ Bullet_6x18mmCharged
+ 0.5
+ 12
+ VFED_Shot_ChargeCycler
+ GunTail_Light
+ 9
+
+
+
+
+
+
+ VFED_Gun_Fletchling
+
+ 8
+ 0.79
+ 1
+ 0.15
+ 1.8
+ 9
+
+
+ 1.0
+ CombatExtended.Verb_ShootCE
+ true
+ Bullet_FletchlingDart
+ 0.6
+ 25
+ VFED_Shot_Fletchling
+ GunTail_Light
+ 4
+
+
+ 5
+ true
+ 0.85
+ AmmoSet_FletchlingDart
+
+
+ FALSE
+ Snapshot
+
+
+
+
+
+
+
+
\ No newline at end of file
From 49544c0d24edbd5d47916e15f9754084e6dc0e78 Mon Sep 17 00:00:00 2001
From: SamaelGray <56392968+SamaelGray@users.noreply.github.com>
Date: Sun, 27 Aug 2023 21:31:09 +0330
Subject: [PATCH 002/158] VFE-D third party support
---
SupportedThirdPartyMods.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/SupportedThirdPartyMods.md b/SupportedThirdPartyMods.md
index a7bcdea2d6..ea41be9691 100644
--- a/SupportedThirdPartyMods.md
+++ b/SupportedThirdPartyMods.md
@@ -480,6 +480,7 @@ Vanilla Brewing Expanded |
Vanilla Chemfuel Expanded |
Vanilla Factions Expanded - Ancients |
Vanilla Factions Expanded - Classical |
+Vanilla Factions Expanded - Deserters |
Vanilla Factions Expanded - Empire |
Vanilla Factions Expanded - Insectoids |
Vanilla Factions Expanded - Mechanoids |
From d177926b4cfb63d1686aa6dad29c0858eb2a91a6 Mon Sep 17 00:00:00 2001
From: SamaelGray <56392968+SamaelGray@users.noreply.github.com>
Date: Mon, 28 Aug 2023 00:09:45 +0330
Subject: [PATCH 003/158] Security stuff
---
.../Buildings_Security_Turrets.xml | 319 ++++++++++++++++++
.../Things_Security.xml | 75 ++++
2 files changed, 394 insertions(+)
create mode 100644 Patches/Vanilla Factions Expanded - Deserters/Things_Security.xml
diff --git a/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml b/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
index e5beaf9ada..936c91ad14 100644
--- a/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
+++ b/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
@@ -8,6 +8,325 @@
+
+ Defs/ThingDef[@Name="VFED_ImperialTurretBase" or defName="VFED_Turret_StrikerTurret"]/fillPercent
+
+ 0.85
+
+
+
+
+ Defs/ThingDef[@Name="VFED_ImperialTurretBase"]/comps/li[@Class="CompProperties_Explosive"]
+
+
+
+ Defs/ThingDef[@Name="VFED_ImperialTurretBase" or defName="VFED_Turret_StrikerTurret"]/thingClass
+
+ CombatExtended.Building_TurretGunCE
+
+
+
+
+ Defs/ThingDef[@Name="VFED_ImperialTurretBase"]/statBases/MaxHitPoints
+
+ 800
+
+
+
+
+ Defs/ThingDef[@Name="VFED_ImperialTurretBase"]/statBases/Flammability
+
+ 0.4
+
+
+
+
+ Defs/ThingDef[@Name="VFED_ImperialTurretBase"]/comps/li[@Class="CompProperties_Power"]
+
+
+ CompPowerTrader
+ 750
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion" or defName="VFED_Gun_Kontarion" or defName="VFED_Turret_Onager"]/comps/li[@Class="CompProperties_Refuelable"]
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion" or defName="VFED_Gun_Kontarion" or defName="VFED_Turret_Onager"]/comps/li[@Class="CompProperties_BoxRefuel"]
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion" or defName="VFED_Gun_Kontarion" or defName="VFED_Turret_Onager"]/modExtensions/li[@Class="TurretExtension_Barrels"]
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion"]/statBases
+
+ 0.75
+ 0.5
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion"]/statBases/ShootingAccuracyTurret
+
+ 1.5
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion"]/building/turretBurstCooldownTime
+
+ 1.0
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion"]/costList
+
+
+ 550
+ 100
+ 60
+ 14
+
+
+
+
+
+ VFED_Gun_Kontarion
+
+ 2.36
+ 0.01
+ 2.0
+ 0.5
+ 150
+
+
+ 4.15
+ CombatExtended.Verb_ShootCE
+ true
+ Bullet_40x311mmR_AP
+ 4.6
+ 12
+ 500
+ 12
+ 6
+ Shot_TurretSniper
+ GunTail_Heavy
+ 20
+ Mounted
+
+
+ 60
+ 13.6
+ AmmoSet_40x311mmR
+
+
+ AimedShot
+ true
+ true
+
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Palintone"]/statBases
+
+ 0.75
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Palintone"]/statBases/ShootingAccuracyTurret
+
+ 1.25
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Palintone"]/building/turretBurstCooldownTime
+
+ 1.0
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Palintone"]/costList
+
+
+ 550
+ 80
+ 12
+
+
+
+
+
+ VFED_Gun_Palintone
+
+ 1
+ 0.01
+ 1.8
+ 0.37
+ 150
+
+
+ 1.50
+ CombatExtended.Verb_ShootCE
+ true
+ Bullet_20x102mmNATO_AP
+ 2.3
+ 14
+ 500
+ 7
+ 40
+ HeavyMG
+ GunTail_Heavy
+ 16
+ Mounted
+
+
+ 200
+ 15.6
+ AmmoSet_20x102mmNATO
+
+
+ AimedShot
+ true
+ true
+
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Onager"]/statBases/ShootingAccuracyTurret
+
+ 1.25
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Onager"]/statBases
+
+ 1.0
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Onager"]/building/turretBurstCooldownTime
+
+ 4.2
+
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Onager"]/costList
+
+
+ 600
+ 90
+ 15
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Gun_Onager"]/statBases/AccuracyTouch
+ | Defs/ThingDef[defName="VFED_Gun_Onager"]/statBases/AccuracyShort
+ | Defs/ThingDef[defName="VFED_Gun_Onager"]/statBases/AccuracyMedium
+ | Defs/ThingDef[defName="VFED_Gun_Onager"]/statBases/AccuracyLong
+
+
+
+ Defs/ThingDef[defName="VFED_Gun_Onager"]/comps
+
+ Defs/ThingDef[defName="VFED_Gun_Onager"]
+
+
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Gun_Onager"]/comps
+
+
+
+ 20
+ 30
+ 40
+ 50
+ 60
+
+
+
+ 24
+ 13
+ AmmoSet_50mmRocket
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Gun_Onager"]/verbs
+
+
+
+ 1.73
+ CombatExtended.Verb_ShootMortarCE
+ false
+ true
+ false
+ Bullet_50mmRocket_HE
+ 3.6
+ 86
+ 14
+ 6
+ 6
+ RocketswarmLauncher_Fire
+ GunTail_Heavy
+ 39
+ 1
+ 0.25
+
+ true
+
+
+
+
+
+
+
+
+
+ Defs/ThingDef[defName="Turret_MiniTurret"]/statBases
+
+ 0.25
+
+
+
+
+ Defs/ThingDef[defName="Turret_MiniTurret"]/statBases/ShootingAccuracyTurret
+
+ 0.5
+
+
+
+
+ Defs/ThingDef[defName="Turret_MiniTurret"]/building/turretBurstCooldownTime
+
+ 1.0
+
+
+
diff --git a/Patches/Vanilla Factions Expanded - Deserters/Things_Security.xml b/Patches/Vanilla Factions Expanded - Deserters/Things_Security.xml
new file mode 100644
index 0000000000..355300d4fc
--- /dev/null
+++ b/Patches/Vanilla Factions Expanded - Deserters/Things_Security.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+ Vanilla Factions Expanded - Deserters
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_RemoteTrapIED_HighExplosive"]/comps/li[@Class="CompProperties_Explosive"]
+
+
+ Bomb
+ 270
+ 4.5
+
+ Bullet
+ Arrow
+ ArrowHighVelocity
+
+
+ 5
+ 30
+
+
+
+
+ 16
+ 100
+
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_RemoteTrapIED_Incendiary"]/comps/li[@Class="CompProperties_Explosive"]
+
+
+ 15
+ PrometheumFlame
+ 5
+ FilthPrometheum
+ 0.75
+
+ Bullet
+ Arrow
+ ArrowHighVelocity
+
+
+ 5
+ 20
+
+
+
+
+
+
+ Defs/ThingDef[defName="VFED_Shell_Shrapnel" or defName="VFED_Shell_ArmorPiercing" or defName="VFED_Shell_Cluster"]
+
+
+
+ Defs/ThingDef[defName="VFED_TrapIED_Shrapnel" or defName="VFED_TrapIED_ArmorPiercing" or defName="VFED_TrapIED_Cluster"]
+
+
+
+ Defs/ThingDef[defName="VFED_RemoteTrapIED_Shrapnel" or defName="VFED_RemoteTrapIED_ArmorPiercing" or defName="VFED_RemoteTrapIED_Cluster"]
+
+
+
+
+
+
+
\ No newline at end of file
From 3b6bf84ea5a03e4120e18c01d894aca538e13c77 Mon Sep 17 00:00:00 2001
From: SamaelGray <56392968+SamaelGray@users.noreply.github.com>
Date: Mon, 28 Aug 2023 11:00:51 +0330
Subject: [PATCH 004/158] Fixes and tweaks
---
.../Buildings_Security_Turrets.xml | 32 ++++++++++++-------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml b/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
index 936c91ad14..acd6e1a86f 100644
--- a/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
+++ b/Patches/Vanilla Factions Expanded - Deserters/Buildings_Security_Turrets.xml
@@ -51,15 +51,25 @@
- Defs/ThingDef[defName="VFED_Turret_Kontarion" or defName="VFED_Gun_Kontarion" or defName="VFED_Turret_Onager"]/comps/li[@Class="CompProperties_Refuelable"]
+ Defs/ThingDef[@Name="VFED_ImperialTurretBase"]/placeWorkers/li[.="PlaceWorker_ShowTurretRadius"]
- Defs/ThingDef[defName="VFED_Turret_Kontarion" or defName="VFED_Gun_Kontarion" or defName="VFED_Turret_Onager"]/comps/li[@Class="CompProperties_BoxRefuel"]
+ Defs/ThingDef[defName="VFED_Turret_Kontarion"]/comps/li[@Class="CompProperties_Refuelable"]
+ | Defs/ThingDef[defName="VFED_Turret_Palintone"]/comps/li[@Class="CompProperties_Refuelable"]
+ | Defs/ThingDef[defName="VFED_Turret_Onager"]/comps/li[@Class="CompProperties_Refuelable"]
- Defs/ThingDef[defName="VFED_Turret_Kontarion" or defName="VFED_Gun_Kontarion" or defName="VFED_Turret_Onager"]/modExtensions/li[@Class="TurretExtension_Barrels"]
+ Defs/ThingDef[defName="VFED_Turret_Kontarion"]/comps/li[@Class="VFED.CompProperties_BoxRefuel"]
+ | Defs/ThingDef[defName="VFED_Turret_Palintone"]/comps/li[@Class="VFED.CompProperties_BoxRefuel"]
+ | Defs/ThingDef[defName="VFED_Turret_Onager"]/comps/li[@Class="VFED.CompProperties_BoxRefuel"]
+
+
+
+ Defs/ThingDef[defName="VFED_Turret_Kontarion"]/modExtensions/li[@Class="VFED.TurretExtension_Barrels"]
+ | Defs/ThingDef[defName="VFED_Turret_Palintone"]/modExtensions/li[@Class="VFED.TurretExtension_Barrels"]
+ | Defs/ThingDef[defName="VFED_Turret_Onager"]/modExtensions/li[@Class="VFED.TurretExtension_Barrels"]
@@ -103,12 +113,12 @@
2.36
0.01
- 2.0
+ 1.0
0.5
150
- 4.15
+ 1.0
CombatExtended.Verb_ShootCE
true
Bullet_40x311mmR_AP
@@ -173,12 +183,12 @@
1
0.01
- 1.8
+ 1.0
0.37
150
- 1.50
+ 1.0
CombatExtended.Verb_ShootCE
true
Bullet_20x102mmNATO_AP
@@ -280,7 +290,7 @@
- 1.73
+ 1.0
CombatExtended.Verb_ShootMortarCE
false
true
@@ -307,21 +317,21 @@
- Defs/ThingDef[defName="Turret_MiniTurret"]/statBases
+ Defs/ThingDef[defName="VFED_Turret_StrikerTurret"]/statBases
0.25
- Defs/ThingDef[defName="Turret_MiniTurret"]/statBases/ShootingAccuracyTurret
+ Defs/ThingDef[defName="VFED_Turret_StrikerTurret"]/statBases/ShootingAccuracyTurret
0.5
- Defs/ThingDef[defName="Turret_MiniTurret"]/building/turretBurstCooldownTime
+ Defs/ThingDef[defName="VFED_Turret_StrikerTurret"]/building/turretBurstCooldownTime
1.0
From c72441e65b8f355fd29a893061b6eb20d5e83162 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sat, 9 Sep 2023 18:58:18 +0100
Subject: [PATCH 005/158] initPush
---
.../CombatExtended/CE_Utility.cs | 53 +++++++++++++++++++
.../Things/Building_TurretGunCE.cs | 5 +-
.../Verbs/Verb_LaunchProjectileCE.cs | 1 +
3 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index b4b9af0c63..5119c5bbcd 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -733,6 +733,59 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
}
*/
+ ///
+ /// A copy of the same function in Rimworld.EquipmentUtility, except changing requirement to Verb.
+ ///
+ ///
+ private static readonly SimpleCurve RecoilCurveAxisX = new SimpleCurve
+ {
+ new CurvePoint(0f, 0f),
+ new CurvePoint(1f, 0.02f),
+ new CurvePoint(2f, 0.03f)
+ };
+
+ private static readonly SimpleCurve RecoilCurveAxisY = new SimpleCurve
+ {
+ new CurvePoint(0f, 0f),
+ new CurvePoint(1f, 0.05f),
+ new CurvePoint(2f, 0.075f)
+ };
+
+ private static readonly SimpleCurve RecoilCurveRotation = new SimpleCurve
+ {
+ new CurvePoint(0f, 0f),
+ new CurvePoint(1f, 3f),
+ new CurvePoint(2f, 4f)
+ };
+
+ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle)
+ {
+ drawOffset = Vector3.zero;
+ angleOffset = 0f;
+ float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount * 10;
+ if (!(recoil > 0f) || shootVerb == null)
+ {
+ return;
+ }
+ Rand.PushState(shootVerb.LastShotTick);
+ try
+ {
+ int num = Find.TickManager.TicksGame - shootVerb.LastShotTick;
+ if ((float)num < weaponDef.verbs[0].ticksBetweenBurstShots)
+ {
+ float num2 = Mathf.Clamp01((float)num / weaponDef.verbs[0].ticksBetweenBurstShots);
+ float num3 = Mathf.Lerp(recoil, 0f, num2);
+ drawOffset = new Vector3((float)Rand.Sign * RecoilCurveAxisX.Evaluate(num2), 0f, 0f - RecoilCurveAxisY.Evaluate(num2)) * num3;
+ angleOffset = (float)Rand.Sign * RecoilCurveRotation.Evaluate(num2) * num3;
+ drawOffset = drawOffset.RotatedBy(aimAngle);
+ }
+ }
+ finally
+ {
+ Log.Message(drawOffset.ToString() + " " + angleOffset.ToString());
+ Rand.PopState();
+ }
+ }
#endregion Misc
#region MoteThrower
diff --git a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
index 99bb02f5d3..bd0ca7af6f 100644
--- a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
+++ b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
@@ -574,7 +574,10 @@ public override string GetInspectString() // Replaced vanilla loaded text
public override void Draw()
{
- top.DrawTurret(Vector3.zero, 0f);
+ Vector3 drawOffset = Vector3.zero;
+ float angleOffset = 0f;
+ CE_Utility.Recoil(def.building.turretGunDef, AttackVerb, out drawOffset, out angleOffset, top.CurRotation);
+ top.DrawTurret(drawOffset, angleOffset);
base.Draw();
}
diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs
index 34f0835f61..3a96eafa35 100644
--- a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs
@@ -1085,6 +1085,7 @@ public override bool TryCastShot()
CompReloadable.UsedOnce();
}
}
+ lastShotTick = Find.TickManager.TicksGame;
return true;
}
From c6526b61c6ae1f3485195367b20b2261c584b333 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sat, 9 Sep 2023 19:01:34 +0100
Subject: [PATCH 006/158] add angle offset back before I decide if to remove it
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 5119c5bbcd..bef7c01282 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -777,6 +777,7 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
float num3 = Mathf.Lerp(recoil, 0f, num2);
drawOffset = new Vector3((float)Rand.Sign * RecoilCurveAxisX.Evaluate(num2), 0f, 0f - RecoilCurveAxisY.Evaluate(num2)) * num3;
angleOffset = (float)Rand.Sign * RecoilCurveRotation.Evaluate(num2) * num3;
+ aimAngle += angleOffset;
drawOffset = drawOffset.RotatedBy(aimAngle);
}
}
From 01924ad37c09365ede804cf64543e3e8e21e804c Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sat, 9 Sep 2023 19:43:45 +0100
Subject: [PATCH 007/158] more fiddles
---
.../CombatExtended/CE_Utility.cs | 20 ++++++++-----------
1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index bef7c01282..064b0bc5d5 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -737,12 +737,6 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
/// A copy of the same function in Rimworld.EquipmentUtility, except changing requirement to Verb.
///
///
- private static readonly SimpleCurve RecoilCurveAxisX = new SimpleCurve
- {
- new CurvePoint(0f, 0f),
- new CurvePoint(1f, 0.02f),
- new CurvePoint(2f, 0.03f)
- };
private static readonly SimpleCurve RecoilCurveAxisY = new SimpleCurve
{
@@ -758,11 +752,14 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
new CurvePoint(2f, 4f)
};
+ const float recoilMagicNumber = 20;
+
public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle)
{
drawOffset = Vector3.zero;
angleOffset = 0f;
- float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount * 10;
+ float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount * Math.Min(recoilMagicNumber, recoilMagicNumber / weaponDef.verbs[0].ticksBetweenBurstShots);
+ float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) / 2f;
if (!(recoil > 0f) || shootVerb == null)
{
return;
@@ -771,19 +768,18 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
try
{
int num = Find.TickManager.TicksGame - shootVerb.LastShotTick;
- if ((float)num < weaponDef.verbs[0].ticksBetweenBurstShots)
+ if ((float)num < recoilRelaxation)
{
- float num2 = Mathf.Clamp01((float)num / weaponDef.verbs[0].ticksBetweenBurstShots);
+ float num2 = Mathf.Clamp01((float)num / recoilRelaxation);
float num3 = Mathf.Lerp(recoil, 0f, num2);
- drawOffset = new Vector3((float)Rand.Sign * RecoilCurveAxisX.Evaluate(num2), 0f, 0f - RecoilCurveAxisY.Evaluate(num2)) * num3;
+ drawOffset = new Vector3(0f, 0f, 0f - RecoilCurveAxisY.Evaluate(num2)) * num3;
angleOffset = (float)Rand.Sign * RecoilCurveRotation.Evaluate(num2) * num3;
- aimAngle += angleOffset;
drawOffset = drawOffset.RotatedBy(aimAngle);
+ aimAngle += angleOffset;
}
}
finally
{
- Log.Message(drawOffset.ToString() + " " + angleOffset.ToString());
Rand.PopState();
}
}
From e6c0b8485c88d6b2cb21844e530f7f6aeb736eb7 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sat, 9 Sep 2023 20:28:08 +0100
Subject: [PATCH 008/158] squared recoil
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 064b0bc5d5..26b27180fb 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -752,15 +752,17 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
new CurvePoint(2f, 4f)
};
- const float recoilMagicNumber = 20;
+ const float RecoilMagicNumber = 20;
public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle)
{
drawOffset = Vector3.zero;
angleOffset = 0f;
- float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount * Math.Min(recoilMagicNumber, recoilMagicNumber / weaponDef.verbs[0].ticksBetweenBurstShots);
- float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) / 2f;
- if (!(recoil > 0f) || shootVerb == null)
+ float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;
+ recoil = recoil * recoil * Mathf.Clamp(RecoilMagicNumber / weaponDef.verbs[0].ticksBetweenBurstShots, 1, RecoilMagicNumber);
+
+ float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
+ if (recoil <= 0f || shootVerb == null)
{
return;
}
@@ -776,6 +778,8 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
angleOffset = (float)Rand.Sign * RecoilCurveRotation.Evaluate(num2) * num3;
drawOffset = drawOffset.RotatedBy(aimAngle);
aimAngle += angleOffset;
+
+ Log.Message(recoil.ToString() + " " + recoilRelaxation.ToString());
}
}
finally
From b4d5cd603e13bfdb027173bfe1320813c600e27c Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sat, 9 Sep 2023 21:44:05 +0100
Subject: [PATCH 009/158] mod extension for outliers
---
.../CombatExtended/CombatExtended/CE_Utility.cs | 12 ++++++++++--
.../CombatExtended/Defs/RecoilAdjustExtension.cs | 15 +++++++++++++++
2 files changed, 25 insertions(+), 2 deletions(-)
create mode 100644 Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 26b27180fb..0116354ea7 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -759,13 +759,21 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
drawOffset = Vector3.zero;
angleOffset = 0f;
float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;
- recoil = recoil * recoil * Mathf.Clamp(RecoilMagicNumber / weaponDef.verbs[0].ticksBetweenBurstShots, 1, RecoilMagicNumber);
-
+ recoil = Math.Min(recoil * recoil, 20) * Mathf.Clamp(RecoilMagicNumber / weaponDef.verbs[0].ticksBetweenBurstShots, 1, RecoilMagicNumber);
float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
+ RecoilAdjustExtension RecoilAdjustExtension = weaponDef.GetModExtension();
+
+ if (RecoilAdjustExtension != null)
+ {
+ recoil *= RecoilAdjustExtension.recoilModifier;
+ recoilRelaxation = RecoilAdjustExtension.recoilTick > 0 ? RecoilAdjustExtension.recoilTick : recoilRelaxation;
+ }
+
if (recoil <= 0f || shootVerb == null)
{
return;
}
+
Rand.PushState(shootVerb.LastShotTick);
try
{
diff --git a/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs b/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
new file mode 100644
index 0000000000..07461ed357
--- /dev/null
+++ b/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+
+namespace CombatExtended
+{
+ public class RecoilAdjustExtension : DefModExtension
+ {
+ public float recoilModifier = 1;
+ public int recoilTick = -1;
+ }
+}
From cabfec4dfce4b5988de25a587ddf8d4bf195b881 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sat, 9 Sep 2023 22:42:29 +0100
Subject: [PATCH 010/158] fixed wrong relation with RoF
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 0116354ea7..ed7369860d 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -752,14 +752,14 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
new CurvePoint(2f, 4f)
};
- const float RecoilMagicNumber = 20;
+ const float RecoilMagicNumber = 2;
public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle)
{
drawOffset = Vector3.zero;
angleOffset = 0f;
float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;
- recoil = Math.Min(recoil * recoil, 20) * Mathf.Clamp(RecoilMagicNumber / weaponDef.verbs[0].ticksBetweenBurstShots, 1, RecoilMagicNumber);
+ recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp(weaponDef.verbs[0].ticksBetweenBurstShots, 1, 10);
float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
RecoilAdjustExtension RecoilAdjustExtension = weaponDef.GetModExtension();
@@ -769,7 +769,8 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
recoilRelaxation = RecoilAdjustExtension.recoilTick > 0 ? RecoilAdjustExtension.recoilTick : recoilRelaxation;
}
- if (recoil <= 0f || shootVerb == null)
+ //Prevents recoil for something with absurd ROF, it's too fast for any meaningful recoil animation
+ if (recoil <= 0f || shootVerb == null || recoilRelaxation < 2)
{
return;
}
From 6e11f3a333f7b2724247ff430e25fc33d7f6fe5f Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sun, 10 Sep 2023 07:47:35 +0100
Subject: [PATCH 011/158] more fiddle
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 7 ++++---
.../CombatExtended/Defs/RecoilAdjustExtension.cs | 1 +
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index ed7369860d..adf53a46da 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -752,14 +752,14 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
new CurvePoint(2f, 4f)
};
- const float RecoilMagicNumber = 2;
+ const float RecoilMagicNumber = 2.6f;
public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle)
{
drawOffset = Vector3.zero;
angleOffset = 0f;
float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;
- recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp(weaponDef.verbs[0].ticksBetweenBurstShots, 1, 10);
+ recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp((float)Math.Log10(weaponDef.verbs[0].ticksBetweenBurstShots), 0.1f, 10);
float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
RecoilAdjustExtension RecoilAdjustExtension = weaponDef.GetModExtension();
@@ -767,6 +767,7 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
{
recoil *= RecoilAdjustExtension.recoilModifier;
recoilRelaxation = RecoilAdjustExtension.recoilTick > 0 ? RecoilAdjustExtension.recoilTick : recoilRelaxation;
+ recoil = RecoilAdjustExtension.recoilScale > 0 ? RecoilAdjustExtension.recoilScale : recoil;
}
//Prevents recoil for something with absurd ROF, it's too fast for any meaningful recoil animation
@@ -788,7 +789,7 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
drawOffset = drawOffset.RotatedBy(aimAngle);
aimAngle += angleOffset;
- Log.Message(recoil.ToString() + " " + recoilRelaxation.ToString());
+ Log.Message(recoil.ToString() + " " + Mathf.Clamp((float)Math.Log10(weaponDef.verbs[0].ticksBetweenBurstShots), 0.1f, 10).ToString());
}
}
finally
diff --git a/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs b/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
index 07461ed357..7b87c73a86 100644
--- a/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
+++ b/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
@@ -10,6 +10,7 @@ namespace CombatExtended
public class RecoilAdjustExtension : DefModExtension
{
public float recoilModifier = 1;
+ public float recoilScale = -1;
public int recoilTick = -1;
}
}
From 6fabd7c93ff7389153b2857dc5688886d865541b Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sun, 10 Sep 2023 19:40:49 +0100
Subject: [PATCH 012/158] handheld weapon recoil
the harmony transpiler is in a mess. probably need someone better at coding to clean it up
---
.../CombatExtended/CE_Utility.cs | 29 ++++++-----
.../CombatExtended/Defs/GunDrawExtension.cs | 5 ++
.../Defs/RecoilAdjustExtension.cs | 16 ------
.../Things/Building_TurretGunCE.cs | 2 +-
.../Harmony/Harmony_PawnRenderer.cs | 51 ++++++++++++++++++-
5 files changed, 72 insertions(+), 31 deletions(-)
delete mode 100644 Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index adf53a46da..fda4ca90c6 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -754,22 +754,18 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
const float RecoilMagicNumber = 2.6f;
- public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle)
+ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle, bool handheld)
{
drawOffset = Vector3.zero;
angleOffset = 0f;
float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;
recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp((float)Math.Log10(weaponDef.verbs[0].ticksBetweenBurstShots), 0.1f, 10);
- float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
- RecoilAdjustExtension RecoilAdjustExtension = weaponDef.GetModExtension();
-
- if (RecoilAdjustExtension != null)
+ if (recoil > 5 && handheld)
{
- recoil *= RecoilAdjustExtension.recoilModifier;
- recoilRelaxation = RecoilAdjustExtension.recoilTick > 0 ? RecoilAdjustExtension.recoilTick : recoilRelaxation;
- recoil = RecoilAdjustExtension.recoilScale > 0 ? RecoilAdjustExtension.recoilScale : recoil;
+ recoil = 5;
}
+ float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
//Prevents recoil for something with absurd ROF, it's too fast for any meaningful recoil animation
if (recoil <= 0f || shootVerb == null || recoilRelaxation < 2)
{
@@ -779,17 +775,24 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
Rand.PushState(shootVerb.LastShotTick);
try
{
+ float muzzleJumpModifier = 1;
+ GunDrawExtension recoilAdjustExtension = weaponDef.GetModExtension();
+ if (recoilAdjustExtension != null)
+ {
+ recoil *= recoilAdjustExtension.recoilModifier;
+ recoilRelaxation = recoilAdjustExtension.recoilTick > 0 ? recoilAdjustExtension.recoilTick : recoilRelaxation;
+ recoil = recoilAdjustExtension.recoilScale > 0 ? recoilAdjustExtension.recoilScale : recoil;
+ muzzleJumpModifier = recoilAdjustExtension.muzzleJumpModifier > 0 ? recoilAdjustExtension.muzzleJumpModifier : 1;
+ }
int num = Find.TickManager.TicksGame - shootVerb.LastShotTick;
- if ((float)num < recoilRelaxation)
+ if (num < recoilRelaxation)
{
- float num2 = Mathf.Clamp01((float)num / recoilRelaxation);
+ float num2 = Mathf.Clamp01(num / recoilRelaxation);
float num3 = Mathf.Lerp(recoil, 0f, num2);
drawOffset = new Vector3(0f, 0f, 0f - RecoilCurveAxisY.Evaluate(num2)) * num3;
- angleOffset = (float)Rand.Sign * RecoilCurveRotation.Evaluate(num2) * num3;
+ angleOffset = (handheld ? -1 : Rand.Sign) * RecoilCurveRotation.Evaluate(num2) * num3 * recoil * 0.6f * muzzleJumpModifier;
drawOffset = drawOffset.RotatedBy(aimAngle);
aimAngle += angleOffset;
-
- Log.Message(recoil.ToString() + " " + Mathf.Clamp((float)Math.Log10(weaponDef.verbs[0].ticksBetweenBurstShots), 0.1f, 10).ToString());
}
}
finally
diff --git a/Source/CombatExtended/CombatExtended/Defs/GunDrawExtension.cs b/Source/CombatExtended/CombatExtended/Defs/GunDrawExtension.cs
index ccb65a230f..026fe93f70 100644
--- a/Source/CombatExtended/CombatExtended/Defs/GunDrawExtension.cs
+++ b/Source/CombatExtended/CombatExtended/Defs/GunDrawExtension.cs
@@ -7,5 +7,10 @@ public class GunDrawExtension : DefModExtension
{
public Vector2 DrawSize = Vector2.one;
public Vector2 DrawOffset = Vector2.zero;
+
+ public float recoilModifier = 1;
+ public float recoilScale = -1;
+ public int recoilTick = -1;
+ public float muzzleJumpModifier = -1;
}
}
diff --git a/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs b/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
deleted file mode 100644
index 7b87c73a86..0000000000
--- a/Source/CombatExtended/CombatExtended/Defs/RecoilAdjustExtension.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Verse;
-
-namespace CombatExtended
-{
- public class RecoilAdjustExtension : DefModExtension
- {
- public float recoilModifier = 1;
- public float recoilScale = -1;
- public int recoilTick = -1;
- }
-}
diff --git a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
index bd0ca7af6f..e6c110a32e 100644
--- a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
+++ b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
@@ -576,7 +576,7 @@ public override void Draw()
{
Vector3 drawOffset = Vector3.zero;
float angleOffset = 0f;
- CE_Utility.Recoil(def.building.turretGunDef, AttackVerb, out drawOffset, out angleOffset, top.CurRotation);
+ CE_Utility.Recoil(def.building.turretGunDef, AttackVerb, out drawOffset, out angleOffset, top.CurRotation, false);
top.DrawTurret(drawOffset, angleOffset);
base.Draw();
}
diff --git a/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs b/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs
index 463d2a2216..f434daa3e1 100644
--- a/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs
+++ b/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs
@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
@@ -413,8 +414,27 @@ public static void Prefix(PawnRenderer __instance, Thing eq)
equipment = eq;
}
+ private static void RecoilCE(Thing eq, Vector3 position, float aimAngle, float num, CompEquippable compEquippable)
+ {
+ if (compEquippable.PrimaryVerb.verbProps is VerbPropertiesCE)
+ {
+ //CE_Utility.Recoil(eq.def, compEquippable.PrimaryVerb, out var drawOffset, out var angleOffset, aimAngle);
+ //drawLoc += drawOffset;
+ //num += angleOffset;
+ }
+ }
+
private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int layer, Thing eq, Vector3 position, float aimAngle)
{
+ CompEquippable compEquippable = eq.TryGetComp();
+ Vector3 recoilOffset = new Vector3();
+ float muzzleJump = 0;
+ if (compEquippable.PrimaryVerb.verbProps is VerbPropertiesCE)
+ {
+ CE_Utility.Recoil(eq.def, compEquippable.PrimaryVerb, out var drawOffset, out var angleOffset, aimAngle, true);
+ recoilOffset = drawOffset;
+ muzzleJump = angleOffset;
+ }
GunDrawExtension drawData = eq.def.GetModExtension() ?? new GunDrawExtension() { DrawSize = eq.def.graphicData.drawSize };
if (drawData.DrawSize == Vector2.one) { drawData.DrawSize = eq.def.graphicData.drawSize; }
Vector3 scale = new Vector3(drawData.DrawSize.x, 1, drawData.DrawSize.y);
@@ -422,8 +442,9 @@ private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int laye
if (aimAngle > 200 && aimAngle < 340)
{
posVec.x *= -1;
+ muzzleJump = -muzzleJump;
}
- matrix.SetTRS(position + posVec.RotatedBy(matrix.rotation.eulerAngles.y), matrix.rotation, scale);
+ matrix.SetTRS(position + posVec.RotatedBy(matrix.rotation.eulerAngles.y) + recoilOffset, Quaternion.AngleAxis(matrix.rotation.eulerAngles.y + muzzleJump, Vector3.up), scale);
if (eq is WeaponPlatform platform)
{
platform.DrawPlatform(matrix, mesh == MeshPool.plane10Flip, layer);
@@ -434,12 +455,40 @@ private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int laye
}
}
+
/*
* This replace the last DrawMesh in
*/
internal static IEnumerable Transpiler(IEnumerable instructions)
{
var codes = instructions.ToList();
+ /*
+ var recoil_opcodes = new CodeInstruction[]
+ {
+ new CodeInstruction(OpCodes.Ldarg_1),
+ new CodeInstruction(OpCodes.Ldarg_2),
+ new CodeInstruction(OpCodes.Ldarg_3),
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new CodeInstruction(OpCodes.Ldloc_2),
+ new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Harmony_PawnRenderer_DrawEquipmentAiming), nameof(RecoilCE)))
+ };
+ bool foundRecoil = false;
+ int index = 0;
+ for (int i = 0; i < codes.Count; i++)
+ {
+ CodeInstruction code = codes[i];
+ if (foundRecoil && code.opcode == OpCodes.Stloc_1)
+ {
+ index = i + 1;
+ break;
+ }
+ else if (code.opcode == OpCodes.Call && ReferenceEquals(code.operand, typeof(EquipmentUtility).GetMethod("Recoil")))
+ {
+ foundRecoil = true;
+ }
+ }
+ codes.InsertRange(index, recoil_opcodes);
+ */
codes[codes.Count - 2].operand =
AccessTools.Method(typeof(Harmony_PawnRenderer_DrawEquipmentAiming), nameof(DrawMesh));
codes.InsertRange(codes.Count - 2, new[]
From 374483c09a3da527735d0693ef1fffea607e1326 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sun, 10 Sep 2023 21:39:11 +0100
Subject: [PATCH 013/158] upgrade?
tidied up code
make single hand weapon behave differently
muzzle rise no longer affected by recoil modifier
semi-auto weapons are affected by RoF based recoil reduction too, fixing chain shotgun
included mod setting to turn this off
---
.../CombatExtended/CE_Utility.cs | 36 ++++++++++++++-----
.../CombatExtended/CombatExtended/Settings.cs | 5 +++
.../Things/Building_TurretGunCE.cs | 5 ++-
.../Harmony/Harmony_PawnRenderer.cs | 23 +++++-------
4 files changed, 45 insertions(+), 24 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index fda4ca90c6..420e29f9cc 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -753,19 +753,18 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
};
const float RecoilMagicNumber = 2.6f;
+ const float MuzzleRiseMagicNumber = 0.04f;
public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle, bool handheld)
{
drawOffset = Vector3.zero;
angleOffset = 0f;
float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;
- recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp((float)Math.Log10(weaponDef.verbs[0].ticksBetweenBurstShots), 0.1f, 10);
- if (recoil > 5 && handheld)
- {
- recoil = 5;
- }
float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
+
+ recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp((float)Math.Log10(recoilRelaxation), 0.1f, 10);
+
//Prevents recoil for something with absurd ROF, it's too fast for any meaningful recoil animation
if (recoil <= 0f || shootVerb == null || recoilRelaxation < 2)
{
@@ -775,24 +774,45 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
Rand.PushState(shootVerb.LastShotTick);
try
{
- float muzzleJumpModifier = 1;
+ float muzzleJumpModifier = recoil;
GunDrawExtension recoilAdjustExtension = weaponDef.GetModExtension();
if (recoilAdjustExtension != null)
{
recoil *= recoilAdjustExtension.recoilModifier;
recoilRelaxation = recoilAdjustExtension.recoilTick > 0 ? recoilAdjustExtension.recoilTick : recoilRelaxation;
recoil = recoilAdjustExtension.recoilScale > 0 ? recoilAdjustExtension.recoilScale : recoil;
- muzzleJumpModifier = recoilAdjustExtension.muzzleJumpModifier > 0 ? recoilAdjustExtension.muzzleJumpModifier : 1;
+ muzzleJumpModifier *= recoilAdjustExtension.muzzleJumpModifier > 0 ? recoilAdjustExtension.muzzleJumpModifier : 1;
}
+
+
+ if (handheld)
+ {
+ if (weaponDef.weaponTags.Contains("CE_OneHandedWeapon"))
+ {
+ recoil /= 3;
+ muzzleJumpModifier *= 1.5f;
+ }
+ else
+ {
+ recoil /= 1.3f;
+ muzzleJumpModifier /= 1.5f;
+ }
+ if (recoil > 15)
+ {
+ recoil = 15;
+ }
+ }
+
int num = Find.TickManager.TicksGame - shootVerb.LastShotTick;
if (num < recoilRelaxation)
{
float num2 = Mathf.Clamp01(num / recoilRelaxation);
float num3 = Mathf.Lerp(recoil, 0f, num2);
drawOffset = new Vector3(0f, 0f, 0f - RecoilCurveAxisY.Evaluate(num2)) * num3;
- angleOffset = (handheld ? -1 : Rand.Sign) * RecoilCurveRotation.Evaluate(num2) * num3 * recoil * 0.6f * muzzleJumpModifier;
+ angleOffset = (handheld ? -1 : Rand.Sign) * RecoilCurveRotation.Evaluate(num2) * num3 * MuzzleRiseMagicNumber * muzzleJumpModifier;
drawOffset = drawOffset.RotatedBy(aimAngle);
aimAngle += angleOffset;
+ Log.Message(recoil.ToString());
}
}
finally
diff --git a/Source/CombatExtended/CombatExtended/Settings.cs b/Source/CombatExtended/CombatExtended/Settings.cs
index 33c0f1d18c..52f838ef68 100644
--- a/Source/CombatExtended/CombatExtended/Settings.cs
+++ b/Source/CombatExtended/CombatExtended/Settings.cs
@@ -19,6 +19,7 @@ public class Settings : ModSettings, ISettingsCE
private bool autosetup = true;
private bool showCasings = true;
private bool createCasingsFilth = true;
+ private bool recoilAnim = true;
private bool showTaunts = true;
private bool allowMeleeHunting = false;
private bool smokeEffects = true;
@@ -128,6 +129,8 @@ public class Settings : ModSettings, ISettingsCE
public bool CreateCasingsFilth => createCasingsFilth;
+ public bool RecoilAnim => recoilAnim;
+
#endregion
private bool lastAmmoSystemStatus;
@@ -139,6 +142,7 @@ public override void ExposeData()
base.ExposeData();
Scribe_Values.Look(ref showCasings, "showCasings", true);
Scribe_Values.Look(ref createCasingsFilth, "createCasingsFilth", true);
+ Scribe_Values.Look(ref recoilAnim, "recoilAnim", true);
Scribe_Values.Look(ref showTaunts, "showTaunts", true);
Scribe_Values.Look(ref allowMeleeHunting, "allowMeleeHunting", false);
Scribe_Values.Look(ref smokeEffects, "smokeEffects", true);
@@ -210,6 +214,7 @@ public void DoWindowContents(Rect canvas, ref int offset)
list.CheckboxLabeled("CE_Settings_PartialStats_Title".Translate(), ref partialstats, "CE_Settings_PartialStats_Desc".Translate());
list.CheckboxLabeled("CE_Settings_ShowCasings_Title".Translate(), ref showCasings, "CE_Settings_ShowCasings_Desc".Translate());
list.CheckboxLabeled("CE_Settings_СreateCasingsFilth_Title".Translate(), ref createCasingsFilth, "CE_Settings_СreateCasingsFilth_Desc".Translate());
+ list.CheckboxLabeled("CE_Settings_RecoilAnim_Title".Translate(), ref recoilAnim, "CE_Settings_RecoilAnim_Desc".Translate());
list.CheckboxLabeled("CE_Settings_ShowTaunts_Title".Translate(), ref showTaunts, "CE_Settings_ShowTaunts_Desc".Translate());
list.CheckboxLabeled("CE_Settings_AllowMeleeHunting_Title".Translate(), ref allowMeleeHunting, "CE_Settings_AllowMeleeHunting_Desc".Translate());
list.CheckboxLabeled("CE_Settings_SmokeEffects_Title".Translate(), ref smokeEffects, "CE_Settings_SmokeEffects_Desc".Translate());
diff --git a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
index e6c110a32e..f63514568a 100644
--- a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
+++ b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
@@ -576,7 +576,10 @@ public override void Draw()
{
Vector3 drawOffset = Vector3.zero;
float angleOffset = 0f;
- CE_Utility.Recoil(def.building.turretGunDef, AttackVerb, out drawOffset, out angleOffset, top.CurRotation, false);
+ if (Controller.settings.RecoilAnim)
+ {
+ CE_Utility.Recoil(def.building.turretGunDef, AttackVerb, out drawOffset, out angleOffset, top.CurRotation, false);
+ }
top.DrawTurret(drawOffset, angleOffset);
base.Draw();
}
diff --git a/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs b/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs
index f434daa3e1..e548680486 100644
--- a/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs
+++ b/Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs
@@ -404,6 +404,10 @@ internal static class Harmony_PawnRenderer_DrawEquipmentAiming
private static Pawn pawn;
+ private static Vector3 recoilOffset = new Vector3();
+
+ private static float muzzleJump = 0;
+
private static readonly Matrix4x4 TBot5 = Matrix4x4.Translate(new Vector3(0, -0.006f, 0));
private static readonly Matrix4x4 TBot3 = Matrix4x4.Translate(new Vector3(0, -0.004f, 0));
@@ -416,25 +420,16 @@ public static void Prefix(PawnRenderer __instance, Thing eq)
private static void RecoilCE(Thing eq, Vector3 position, float aimAngle, float num, CompEquippable compEquippable)
{
- if (compEquippable.PrimaryVerb.verbProps is VerbPropertiesCE)
+ if (Controller.settings.RecoilAnim && compEquippable.PrimaryVerb.verbProps is VerbPropertiesCE)
{
- //CE_Utility.Recoil(eq.def, compEquippable.PrimaryVerb, out var drawOffset, out var angleOffset, aimAngle);
- //drawLoc += drawOffset;
- //num += angleOffset;
+ CE_Utility.Recoil(eq.def, compEquippable.PrimaryVerb, out var drawOffset, out var angleOffset, aimAngle, true);
+ recoilOffset = drawOffset;
+ muzzleJump = angleOffset;
}
}
private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int layer, Thing eq, Vector3 position, float aimAngle)
{
- CompEquippable compEquippable = eq.TryGetComp();
- Vector3 recoilOffset = new Vector3();
- float muzzleJump = 0;
- if (compEquippable.PrimaryVerb.verbProps is VerbPropertiesCE)
- {
- CE_Utility.Recoil(eq.def, compEquippable.PrimaryVerb, out var drawOffset, out var angleOffset, aimAngle, true);
- recoilOffset = drawOffset;
- muzzleJump = angleOffset;
- }
GunDrawExtension drawData = eq.def.GetModExtension() ?? new GunDrawExtension() { DrawSize = eq.def.graphicData.drawSize };
if (drawData.DrawSize == Vector2.one) { drawData.DrawSize = eq.def.graphicData.drawSize; }
Vector3 scale = new Vector3(drawData.DrawSize.x, 1, drawData.DrawSize.y);
@@ -462,7 +457,6 @@ private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int laye
internal static IEnumerable Transpiler(IEnumerable instructions)
{
var codes = instructions.ToList();
- /*
var recoil_opcodes = new CodeInstruction[]
{
new CodeInstruction(OpCodes.Ldarg_1),
@@ -488,7 +482,6 @@ internal static IEnumerable Transpiler(IEnumerable
Date: Sun, 10 Sep 2023 21:59:02 +0100
Subject: [PATCH 014/158] Update CE_Utility.cs
slightly increased muzzle rise for low recoil guns and reduce it on high recoil ones, for visual effect
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 420e29f9cc..734b2dddb1 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -753,7 +753,7 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
};
const float RecoilMagicNumber = 2.6f;
- const float MuzzleRiseMagicNumber = 0.04f;
+ const float MuzzleRiseMagicNumber = 0.1f;
public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle, bool handheld)
{
@@ -774,7 +774,7 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
Rand.PushState(shootVerb.LastShotTick);
try
{
- float muzzleJumpModifier = recoil;
+ float muzzleJumpModifier = 10 * (float)Math.Log10(recoil) + 3;
GunDrawExtension recoilAdjustExtension = weaponDef.GetModExtension();
if (recoilAdjustExtension != null)
{
@@ -795,7 +795,6 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
else
{
recoil /= 1.3f;
- muzzleJumpModifier /= 1.5f;
}
if (recoil > 15)
{
@@ -812,7 +811,6 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
angleOffset = (handheld ? -1 : Rand.Sign) * RecoilCurveRotation.Evaluate(num2) * num3 * MuzzleRiseMagicNumber * muzzleJumpModifier;
drawOffset = drawOffset.RotatedBy(aimAngle);
aimAngle += angleOffset;
- Log.Message(recoil.ToString());
}
}
finally
From 7167e2a4c16e3eac4f04e3460effa0ce3de0f8f0 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sun, 10 Sep 2023 22:12:59 +0100
Subject: [PATCH 015/158] fixed breaking when holding melee weapon
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 734b2dddb1..592d94ed4c 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -759,6 +759,10 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
{
drawOffset = Vector3.zero;
angleOffset = 0f;
+ if (shootVerb == null || shootVerb.IsMeleeAttack)
+ {
+ return;
+ }
float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;
float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;
@@ -766,7 +770,7 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp((float)Math.Log10(recoilRelaxation), 0.1f, 10);
//Prevents recoil for something with absurd ROF, it's too fast for any meaningful recoil animation
- if (recoil <= 0f || shootVerb == null || recoilRelaxation < 2)
+ if (recoilRelaxation < 2)
{
return;
}
@@ -782,6 +786,7 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
recoilRelaxation = recoilAdjustExtension.recoilTick > 0 ? recoilAdjustExtension.recoilTick : recoilRelaxation;
recoil = recoilAdjustExtension.recoilScale > 0 ? recoilAdjustExtension.recoilScale : recoil;
muzzleJumpModifier *= recoilAdjustExtension.muzzleJumpModifier > 0 ? recoilAdjustExtension.muzzleJumpModifier : 1;
+ if (recoil <= 0) { return; }
}
From 9f6905c886b7d81496fe0d1fd4e6f81a0c36b14a Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Sun, 10 Sep 2023 22:17:16 +0100
Subject: [PATCH 016/158] fix turret without recoil
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 592d94ed4c..00aca19d41 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -786,9 +786,9 @@ public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOf
recoilRelaxation = recoilAdjustExtension.recoilTick > 0 ? recoilAdjustExtension.recoilTick : recoilRelaxation;
recoil = recoilAdjustExtension.recoilScale > 0 ? recoilAdjustExtension.recoilScale : recoil;
muzzleJumpModifier *= recoilAdjustExtension.muzzleJumpModifier > 0 ? recoilAdjustExtension.muzzleJumpModifier : 1;
- if (recoil <= 0) { return; }
}
+ if (recoil <= 0) { return; }
if (handheld)
{
From a402ecd09e33dee4065974491a9e2f7532de0e58 Mon Sep 17 00:00:00 2001
From: SokyranTheDragon
Date: Mon, 11 Sep 2023 17:17:49 +0200
Subject: [PATCH 017/158] Fix Multiplayer issue with Fire at Will gizmo
---
.../Compatibility/Multiplayer.cs | 18 +++++++++++++++++-
.../Harmony/Harmony_Pawn_DraftController.cs | 11 +++++++++++
.../MultiplayerCompat/MultiplayerCompat.cs | 2 +-
3 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/Compatibility/Multiplayer.cs b/Source/CombatExtended/Compatibility/Multiplayer.cs
index f44b1e6d3a..c4d4c05e7f 100644
--- a/Source/CombatExtended/Compatibility/Multiplayer.cs
+++ b/Source/CombatExtended/Compatibility/Multiplayer.cs
@@ -40,6 +40,19 @@ public static bool InMultiplayer
}
}
+ public static bool IsExecutingCommands
+ {
+ get
+ {
+ if (isMultiplayerActive)
+ {
+ return _isExecutingCommands();
+ }
+
+ return false;
+ }
+ }
+
public static bool IsExecutingCommandsIssuedBySelf
{
get
@@ -52,14 +65,17 @@ public static bool IsExecutingCommandsIssuedBySelf
}
}
- public static void registerCallbacks(Func inMP, Func iecibs)
+ public static void registerCallbacks(Func inMP, Func iec, Func iecibs)
{
_inMultiplayer = inMP;
+ _isExecutingCommands = iec;
_isExecutingCommandsIssuedBySelf = iecibs;
}
private static Func _inMultiplayer = null;
+ private static Func _isExecutingCommands = null;
+
private static Func _isExecutingCommandsIssuedBySelf = null;
[AttributeUsage(AttributeTargets.Method)]
diff --git a/Source/CombatExtended/Harmony/Harmony_Pawn_DraftController.cs b/Source/CombatExtended/Harmony/Harmony_Pawn_DraftController.cs
index 73608b7ad0..4f5c0d031f 100644
--- a/Source/CombatExtended/Harmony/Harmony_Pawn_DraftController.cs
+++ b/Source/CombatExtended/Harmony/Harmony_Pawn_DraftController.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using CombatExtended.Compatibility;
using Verse.AI;
namespace CombatExtended.HarmonyCE
@@ -16,6 +17,16 @@ public static void Postfix(Pawn_DraftController __instance, bool ___fireAtWillIn
{
if (!___fireAtWillInt)
{
+ // In Multiplayer, the FireAtWill setter is marked as a sync method.
+ // Due to how those work - the method will be stopped from running, the call synchronized to all players and then will run
+ // fully. However, because of how Harmony works all prefixes, postfixes, finalizers will still run - which will cause issues
+ // as this postfix will run before it ends up being synchronized in Multiplayer.
+ // Once Multiplayer API exposes `InInterface` method, it should replace this check (as it'll better handle a few edge cases).
+ if (Multiplayer.InMultiplayer && !Multiplayer.IsExecutingCommands)
+ {
+ return;
+ }
+
var jobTracker = __instance.pawn.jobs;
foreach (var queuedJob in jobTracker.jobQueue.ToList())
{
diff --git a/Source/MultiplayerCompat/MultiplayerCompat/MultiplayerCompat.cs b/Source/MultiplayerCompat/MultiplayerCompat/MultiplayerCompat.cs
index 37787a498f..6612c50c92 100644
--- a/Source/MultiplayerCompat/MultiplayerCompat/MultiplayerCompat.cs
+++ b/Source/MultiplayerCompat/MultiplayerCompat/MultiplayerCompat.cs
@@ -65,7 +65,7 @@ public void SlowInit(ModContentPack content)
MP.RegisterAll();
- global::CombatExtended.Compatibility.Multiplayer.registerCallbacks((() => MP.IsInMultiplayer), (() => MP.IsExecutingSyncCommandIssuedBySelf));
+ global::CombatExtended.Compatibility.Multiplayer.registerCallbacks((() => MP.IsInMultiplayer), (() => MP.IsExecutingSyncCommand), (() => MP.IsExecutingSyncCommandIssuedBySelf));
}
From 1eaf226e4a6fca14987b9864ca1c56e13ad408eb Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Tue, 12 Sep 2023 18:04:33 +0100
Subject: [PATCH 018/158] recoil animation for trycastshotglobal
---
Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs
index 134917c780..2f548cd217 100644
--- a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs
+++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs
@@ -215,6 +215,7 @@ public virtual bool TryCastGlobalShot()
CompReloadable.UsedOnce();
}
}
+ lastShotTick = Find.TickManager.TicksGame;
return true;
}
From ee60e3bd75130a3d836bb729d41dd0c758d8f212 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Tue, 12 Sep 2023 20:04:26 +0100
Subject: [PATCH 019/158] Update WorldObjectDamageWorker.cs
---
.../CombatExtended/WorldObjects/WorldObjectDamageWorker.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
index d4370b5e48..fdfd57740c 100644
--- a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
+++ b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
@@ -37,12 +37,12 @@ public virtual float CalculateDamage(ThingDef projectile, Faction faction)
var result = FragmentsPotentialDamage(projectile) + ExplosionPotentialDamage(projectile) + FirePotentialDamage(projectile) + EMPPotentialDamage(projectile, empModifier);
//Damage calculated as in-map damage, needs to be converted into world object damage. 3500f experimentally obtained
result /= 3500f;
- //Crit/Miss imitation
- result *= Rand.Range(0.4f, 1.5f);
if (projectile.projectile is ProjectilePropertiesCE projectileProperties && projectileProperties.shellingProps.damage > 0f)
{
- result *= projectileProperties.shellingProps.damage;
+ result = projectileProperties.shellingProps.damage;
}
+ //Crit/Miss imitation
+ result *= Rand.Range(0.4f, 1.5f);
return result;
}
protected const float fragDamageMultipler = 0.04f;
From 71f53812edbb61c886de23495ac56b0947af91b5 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Tue, 12 Sep 2023 20:04:41 +0100
Subject: [PATCH 020/158] Revert "Update WorldObjectDamageWorker.cs"
This reverts commit ee60e3bd75130a3d836bb729d41dd0c758d8f212.
---
.../CombatExtended/WorldObjects/WorldObjectDamageWorker.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
index fdfd57740c..d4370b5e48 100644
--- a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
+++ b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
@@ -37,12 +37,12 @@ public virtual float CalculateDamage(ThingDef projectile, Faction faction)
var result = FragmentsPotentialDamage(projectile) + ExplosionPotentialDamage(projectile) + FirePotentialDamage(projectile) + EMPPotentialDamage(projectile, empModifier);
//Damage calculated as in-map damage, needs to be converted into world object damage. 3500f experimentally obtained
result /= 3500f;
+ //Crit/Miss imitation
+ result *= Rand.Range(0.4f, 1.5f);
if (projectile.projectile is ProjectilePropertiesCE projectileProperties && projectileProperties.shellingProps.damage > 0f)
{
- result = projectileProperties.shellingProps.damage;
+ result *= projectileProperties.shellingProps.damage;
}
- //Crit/Miss imitation
- result *= Rand.Range(0.4f, 1.5f);
return result;
}
protected const float fragDamageMultipler = 0.04f;
From c99709ba0bd5991f7c7a49915ff6d66448f75edf Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Tue, 12 Sep 2023 20:06:02 +0100
Subject: [PATCH 021/158] Update WorldObjectDamageWorker.cs
---
.../CombatExtended/WorldObjects/WorldObjectDamageWorker.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
index d4370b5e48..fdfd57740c 100644
--- a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
+++ b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
@@ -37,12 +37,12 @@ public virtual float CalculateDamage(ThingDef projectile, Faction faction)
var result = FragmentsPotentialDamage(projectile) + ExplosionPotentialDamage(projectile) + FirePotentialDamage(projectile) + EMPPotentialDamage(projectile, empModifier);
//Damage calculated as in-map damage, needs to be converted into world object damage. 3500f experimentally obtained
result /= 3500f;
- //Crit/Miss imitation
- result *= Rand.Range(0.4f, 1.5f);
if (projectile.projectile is ProjectilePropertiesCE projectileProperties && projectileProperties.shellingProps.damage > 0f)
{
- result *= projectileProperties.shellingProps.damage;
+ result = projectileProperties.shellingProps.damage;
}
+ //Crit/Miss imitation
+ result *= Rand.Range(0.4f, 1.5f);
return result;
}
protected const float fragDamageMultipler = 0.04f;
From 0cd0e775b6e56b1601de0af14044066f5d9a6538 Mon Sep 17 00:00:00 2001
From: Keshash <54061981+Keshash@users.noreply.github.com>
Date: Tue, 12 Sep 2023 22:51:08 +0300
Subject: [PATCH 022/158] Turn Motes into Flecks
---
Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml | 29 ++++
Defs/Ammo/Rifle/762x51mmNATO.xml | 29 ++++
Defs/Ammo/Shotgun/12Gauge.xml | 20 +++
Defs/Effects/ProjectileFX_Flecks.xml | 27 ++++
Defs/SoundDefs/AmmoSounds.xml | 17 +++
Languages/English/Keyed/ModMenu.xml | 3 +
.../Projectiles/ProjectileCE.cs | 3 +-
.../CombatExtended/CombatExtended/Settings.cs | 6 +-
.../EffectProjectileExtension.cs | 126 ++++++++----------
.../ProjectileImpactFX/TrailThrower.cs | 28 ++--
.../TrailerProjectileExtension.cs | 2 +-
11 files changed, 196 insertions(+), 94 deletions(-)
create mode 100644 Defs/Effects/ProjectileFX_Flecks.xml
diff --git a/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml b/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml
index 9889e37f1f..058b383ebe 100644
--- a/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml
+++ b/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml
@@ -126,6 +126,12 @@
Fleck_RifleAmmoCasings_HighCal
Filth_RifleAmmoCasings_HighCal
+
+
+ DustPuff
+ 3
+
+
@@ -162,6 +168,17 @@
+
+
+ true
+ 0.5
+ MicroSparksFast
+ AirPuff
+ 2
+ CE_HeatGlow_API
+ 1
+
+
@@ -178,6 +195,18 @@
+
+
+ true
+ 0.5
+ BlastFlame
+ AirPuff
+ 2
+ ExplosionFlash
+ 5
+ CE_Explosion_APHE
+
+
diff --git a/Defs/Ammo/Rifle/762x51mmNATO.xml b/Defs/Ammo/Rifle/762x51mmNATO.xml
index 2932762268..6306137d0d 100644
--- a/Defs/Ammo/Rifle/762x51mmNATO.xml
+++ b/Defs/Ammo/Rifle/762x51mmNATO.xml
@@ -138,6 +138,12 @@
156
true
+
+
+ DustPuff
+ 2
+
+
@@ -184,6 +190,17 @@
+
+
+ true
+ 0.5
+ MicroSparksFast
+ AirPuff
+ 1
+ CE_HeatGlow_API
+ 1
+
+
@@ -200,6 +217,18 @@
+
+
+ true
+ AirPuff
+ 1
+ BlastFlame
+ 0.25
+ ExplosionFlash
+ 2
+ CE_Explosion_APHE
+
+
diff --git a/Defs/Ammo/Shotgun/12Gauge.xml b/Defs/Ammo/Shotgun/12Gauge.xml
index ab6a1cbcd6..90dfcb4069 100644
--- a/Defs/Ammo/Shotgun/12Gauge.xml
+++ b/Defs/Ammo/Shotgun/12Gauge.xml
@@ -127,6 +127,12 @@
4.52
8.9
+
+
+ DustPuff
+ 1
+
+
@@ -144,6 +150,12 @@
Fleck_ShotgunShell_Green
Filth_ShotgunAmmoCasings_Green
+
+
+ DustPuff
+ 2
+
+
@@ -182,6 +194,14 @@
Fleck_ShotgunShell_Black
Filth_ShotgunAmmoCasings_Black
+
+
+ ElectricalSpark
+ 2
+ CE_ElectricGlow_EMP
+ 4
+
+
diff --git a/Defs/Effects/ProjectileFX_Flecks.xml b/Defs/Effects/ProjectileFX_Flecks.xml
new file mode 100644
index 0000000000..749ba2a95f
--- /dev/null
+++ b/Defs/Effects/ProjectileFX_Flecks.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+ CE_ElectricGlow_EMP
+
+ Things/Mote/LightningGlow
+ MoteGlow
+
+ MoteOverhead
+ 0.1
+ 0.1
+
+
+
+ CE_HeatGlow_API
+
+ Things/Mote/HeatGlow
+ MoteGlow
+
+ MoteOverhead
+ 0.15
+ 0.2
+
+
+
\ No newline at end of file
diff --git a/Defs/SoundDefs/AmmoSounds.xml b/Defs/SoundDefs/AmmoSounds.xml
index 017150e4a5..4f420b6ce0 100644
--- a/Defs/SoundDefs/AmmoSounds.xml
+++ b/Defs/SoundDefs/AmmoSounds.xml
@@ -49,4 +49,21 @@
+
+ CE_Explosion_APHE
+ MapOnly
+ 1
+
+
+
+
+ Weapon/RocketswarmLauncher/Explosion
+
+
+ 15
+ 1.5~2
+
+
+
+
\ No newline at end of file
diff --git a/Languages/English/Keyed/ModMenu.xml b/Languages/English/Keyed/ModMenu.xml
index 5a269914f9..ca1df7336e 100644
--- a/Languages/English/Keyed/ModMenu.xml
+++ b/Languages/English/Keyed/ModMenu.xml
@@ -82,6 +82,9 @@
If true, subsequent shots at the same, or nearby, targets are faster.
Faster subsequent shots
+ Show additional projectile visual effects
+ Bullets and other projectiles will display additional effects, such as bullets kicking up dust or incendiaries throwing sparks. Cosmetic only, does not affect gameplay.
+
Bipod settings
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 87b36d30df..30023366c5 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -1245,9 +1245,8 @@ public virtual void Impact(Thing hitThing)
//}
if (def.HasModExtension())
{
- def.GetModExtension()?.ThrowMote(ExactPosition,
+ def.GetModExtension()?.ThrowFleck(ExactPosition,
Map,
- def.projectile.damageDef.explosionCellMote,
def.projectile.damageDef.explosionColorCenter,
def.projectile.damageDef.soundExplosion,
hitThing);
diff --git a/Source/CombatExtended/CombatExtended/Settings.cs b/Source/CombatExtended/CombatExtended/Settings.cs
index 33c0f1d18c..5bed69bdc0 100644
--- a/Source/CombatExtended/CombatExtended/Settings.cs
+++ b/Source/CombatExtended/CombatExtended/Settings.cs
@@ -28,6 +28,7 @@ public class Settings : ModSettings, ISettingsCE
private bool showTacticalVests = true;
private bool genericammo = false;
private bool partialstats = true;
+ private bool enableExtraEffects = true;
private bool showExtraTooltips = false;
@@ -52,6 +53,7 @@ public class Settings : ModSettings, ISettingsCE
public bool ShowTacticalVests => showTacticalVests;
public bool PartialStat => partialstats;
+ public bool EnableExtraEffects => enableExtraEffects;
public bool ShowExtraTooltips => showExtraTooltips;
public bool ShowExtraStats => showExtraStats;
@@ -147,7 +149,7 @@ public override void ExposeData()
Scribe_Values.Look(ref showBackpacks, "showBackpacks", true);
Scribe_Values.Look(ref showTacticalVests, "showTacticalVests", true);
Scribe_Values.Look(ref partialstats, "PartialArmor", true);
-
+ Scribe_Values.Look(ref enableExtraEffects, "enableExtraEffects", true);
Scribe_Values.Look(ref showExtraTooltips, "showExtraTooltips", false);
Scribe_Values.Look(ref showExtraStats, "showExtraStats", false);
@@ -218,7 +220,7 @@ public void DoWindowContents(Rect canvas, ref int offset)
list.CheckboxLabeled("CE_Settings_ShowExtraTooltips_Title".Translate(), ref showExtraTooltips, "CE_Settings_ShowExtraTooltips_Desc".Translate());
list.CheckboxLabeled("CE_Settings_ShowExtraStats_Title".Translate(), ref showExtraStats, "CE_Settings_ShowExtraStats_Desc".Translate());
list.CheckboxLabeled("CE_Settings_FasterRepeatShots_Title".Translate(), ref fasterRepeatShots, "CE_Settings_FasterRepeatShots_Desc".Translate());
-
+ list.CheckboxLabeled("CE_Settings_EnableExtraEffects_Title".Translate(), ref enableExtraEffects, "CE_Settings_EnableExtraEffects_Desc".Translate());
// Only Allow these settings to be changed in the main menu since doing while a
// map is loaded will result in rendering issues.
if (Current.Game == null)
diff --git a/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs b/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs
index 9cbed40350..7b8fcb3328 100644
--- a/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs
+++ b/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs
@@ -1,5 +1,4 @@
using RimWorld;
-using System;
using UnityEngine;
using Verse;
using Verse.Sound;
@@ -9,17 +8,18 @@ namespace ProjectileImpactFX
// ProjectileImpactFX.EffectProjectileExtension
public class EffectProjectileExtension : DefModExtension
{
- public bool explosionMote = false;
- public string explosionMoteDef = string.Empty;
- public float explosionMoteSize = 1f;
+ public bool explosionFleck = false;
+ public string explosionFleckDef = string.Empty;
+ public float explosionFleckSize = 1f;
public EffecterDef explosionEffecter;
- public FloatRange? explosionMoteSizeRange;
- public string ImpactMoteDef = string.Empty;
- public float ImpactMoteSize = 1f;
- public FloatRange? ImpactMoteSizeRange;
- public string ImpactGlowMoteDef = string.Empty;
- public float ImpactGlowMoteSize = 1f;
- public FloatRange? ImpactGlowMoteSizeRange;
+ public FloatRange? explosionFleckSizeRange;
+ public string ImpactFleckDef = string.Empty;
+ public float ImpactFleckSize = 1f;
+ public FloatRange? ImpactFleckSizeRange;
+ public string impactSoundDef = string.Empty;
+ public string ImpactGlowFleckDef = string.Empty;
+ public float ImpactGlowFleckSize = 1f;
+ public FloatRange? ImpactGlowFleckSizeRange;
public bool muzzleFlare = false;
public string muzzleFlareDef = string.Empty;
public float muzzleFlareSize = 1f;
@@ -27,85 +27,65 @@ public class EffectProjectileExtension : DefModExtension
public string muzzleSmokeDef = string.Empty;
public float muzzleSmokeSize = 0.35f;
- public void ThrowMote(Vector3 loc, Map map, ThingDef explosionMoteDef, Color color, SoundDef sound, Thing hitThing = null)
+ public void ThrowFleck(Vector3 loc, Map map, Color color, SoundDef sound, Thing hitThing = null)
{
- ThingDef explosionmoteDef = explosionMoteDef;
- ThingDef ImpactMoteDef = DefDatabase.GetNamedSilentFail(this.ImpactMoteDef) ?? null;
- ThingDef ImpactGlowMoteDef = DefDatabase.GetNamedSilentFail(this.ImpactGlowMoteDef) ?? null;
- float explosionSize = this.explosionMoteSize;
- float ImpactMoteSize = this.ImpactMoteSizeRange?.RandomInRange ?? this.ImpactMoteSize;
- float ImpactGlowMoteSize = this.ImpactGlowMoteSizeRange?.RandomInRange ?? this.ImpactGlowMoteSize;
+ FleckDef ExplosionFleck = explosionFleckDef != string.Empty ? DefDatabase.GetNamed(this.explosionFleckDef) : null;
+ FleckDef ImpactFleck = ImpactFleckDef != string.Empty ? DefDatabase.GetNamed(this.ImpactFleckDef) : null;
+ FleckDef ImpactGlowFleck = ImpactGlowFleckDef != string.Empty ? DefDatabase.GetNamed(this.ImpactGlowFleckDef) : null;
+ SoundDef ImpactSound = impactSoundDef != string.Empty ? DefDatabase.GetNamed(this.impactSoundDef) : null;
+ float explosionSize = this.explosionFleckSizeRange?.RandomInRange ?? this.explosionFleckSize;
+ float ImpactFleckSize = this.ImpactFleckSizeRange?.RandomInRange ?? this.ImpactFleckSize;
+ float ImpactGlowFleckSize = this.ImpactGlowFleckSizeRange?.RandomInRange ?? this.ImpactGlowFleckSize;
if (!loc.ShouldSpawnMotesAt(map) || map.moteCounter.SaturatedLowPriority)
{
return;
}
Rand.PushState();
float rotationRate = Rand.Range(-30f, 30f);
- float VelocityAngel = (float)Rand.Range(0, 360);
- float VelocitySpeed = Rand.Range(0.48f, 0.72f);
+ float initialRotation = Rand.Range(0, 360);
+ float VelocityAngle = (float)Rand.Range(-30, 30);
+ float VelocitySpeed = Rand.Range(0.5f, 1f);
Rand.PopState();
- if (ImpactGlowMoteDef != null)
+ if (ImpactFleck != null)
{
- MoteMaker.MakeStaticMote(loc, map, ImpactGlowMoteDef, ImpactGlowMoteSize);
- }
- if (explosionMote)
- {
- if (!this.explosionMoteDef.NullOrEmpty())
+ if (explosionEffecter != null)
{
- ThingDef def = DefDatabase.GetNamedSilentFail(this.explosionMoteDef);
- if (def != null)
- {
- explosionmoteDef = def;
- }
- }
- if (explosionmoteDef != null)
- {
- MoteThrown moteThrown;
- moteThrown = (MoteThrown)ThingMaker.MakeThing(explosionmoteDef, null);
- moteThrown.Scale = explosionSize;
- Rand.PushState();
- moteThrown.rotationRate = Rand.Range(-30f, 30f);
- Rand.PopState();
- moteThrown.exactPosition = loc;
- moteThrown.instanceColor = color;
- moteThrown.SetVelocity(VelocityAngel, VelocitySpeed);
- GenSpawn.Spawn(moteThrown, loc.ToIntVec3(), map, WipeMode.Vanish);
+ TriggerEffect(explosionEffecter, loc, map);
}
+ FleckCreationData creationData = FleckMaker.GetDataStatic(loc, map, ImpactFleck);
+ creationData.rotation = initialRotation;
+ creationData.velocityAngle = VelocityAngle;
+ creationData.velocitySpeed = VelocitySpeed;
+ creationData.scale = ImpactFleckSize;
+ creationData.spawnPosition = loc;
+ map.flecks.CreateFleck(creationData);
+ //}
}
- if (ImpactMoteDef != null)
+ if (ImpactGlowFleck != null)
{
- if (hitThing != null && hitThing is Pawn pawn)
- {
- ImpactMoteDef = ThingDef.Named("Mote_BloodPuff");
- if (sound != null)
- {
- sound.PlayOneShot(new TargetInfo(loc.ToIntVec3(), map, false));
- }
- MoteThrown moteThrown;
- moteThrown = (MoteThrown)ThingMaker.MakeThing(ImpactMoteDef, null);
- moteThrown.Scale = ImpactMoteSize;
- Rand.PushState();
- moteThrown.rotationRate = Rand.Range(-30f, 30f);
- Rand.PopState();
- moteThrown.exactPosition = loc;
- moteThrown.instanceColor = pawn.RaceProps.BloodDef.graphic.color;
- moteThrown.SetVelocity(VelocityAngel, VelocitySpeed);
- GenSpawn.Spawn(moteThrown, loc.ToIntVec3(), map, WipeMode.Vanish);
- if (explosionEffecter != null)
- {
- TriggerEffect(explosionEffecter, loc, map, hitThing);
- }
- }
- else
+ FleckCreationData creationData = FleckMaker.GetDataStatic(loc, map, ImpactGlowFleck);
+ creationData.scale = ImpactGlowFleckSize;
+ map.flecks.CreateFleck(creationData);
+ }
+ if (explosionFleck)
+ {
+ if (ExplosionFleck != null)
{
- if (explosionEffecter != null)
- {
- TriggerEffect(explosionEffecter, loc, map);
- }
- MoteMaker.MakeStaticMote(loc, map, ImpactMoteDef, ImpactMoteSize);
+ FleckCreationData creationData = FleckMaker.GetDataStatic(loc, map, ExplosionFleck);
+ creationData.scale = explosionSize;
+ creationData.rotationRate = rotationRate;
+ creationData.spawnPosition = loc;
+ creationData.instanceColor = color;
+ creationData.velocityAngle = VelocityAngle;
+ creationData.velocitySpeed = VelocitySpeed;
+ map.flecks.CreateFleck(creationData);
}
}
+ if (ImpactSound != null)
+ {
+ ImpactSound.PlayOneShot(new TargetInfo(loc.ToIntVec3(), map));
+ }
}
void TriggerEffect(EffecterDef effect, Vector3 position, Map map, Thing hitThing = null)
diff --git a/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs b/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs
index f73a984297..a030e99890 100644
--- a/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs
+++ b/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs
@@ -11,31 +11,27 @@ namespace ProjectileImpactFX
{
public class TrailThrower
{
- public static void ThrowSmoke(Vector3 loc, float size, Map map, string DefName)
+ public static void ThrowSmoke(Vector3 loc, float size, Map map, string defName)
{
if (!loc.ShouldSpawnMotesAt(map) || map.moteCounter.SaturatedLowPriority)
{
return;
}
+ FleckDef fleck = DefDatabase.GetNamed(defName) ?? null;
- //Rand.PushState();
- //MoteThrown moteThrown = (MoteThrown)ThingMaker.MakeThing(ThingDefOf.Mote_Smoke, null);
- //moteThrown.Scale = Rand.Range(1.5f, 2.5f) * size;
- //moteThrown.rotationRate = Rand.Range(-30f, 30f);
- //moteThrown.exactPosition = loc;
- //moteThrown.SetVelocity((float)Rand.Range(30, 40), Rand.Range(0.5f, 0.7f));
- //Rand.PopState();
- //GenSpawn.Spawn(moteThrown, loc.ToIntVec3(), map, WipeMode.Vanish);
+ if (fleck != null)
+ {
+ Rand.PushState();
+ FleckCreationData dataStatic = FleckMaker.GetDataStatic(loc, map, fleck, Rand.Range(1.5f, 2.5f) * size);
- Rand.PushState();
- FleckCreationData dataStatic = FleckMaker.GetDataStatic(loc, map, FleckDefOf.Smoke, Rand.Range(1.5f, 2.5f) * size);
+ dataStatic.rotationRate = Rand.Range(-30f, 30f);
+ dataStatic.velocityAngle = (float)Rand.Range(30, 40);
+ dataStatic.velocitySpeed = Rand.Range(0.5f, 0.7f);
- dataStatic.rotationRate = Rand.Range(-30f, 30f);
- dataStatic.velocityAngle = (float)Rand.Range(30, 40);
- dataStatic.velocitySpeed = Rand.Range(0.5f, 0.7f);
+ Rand.PopState();
+ map.flecks.CreateFleck(dataStatic);
+ }
- Rand.PopState();
- map.flecks.CreateFleck(dataStatic);
}
}
}
diff --git a/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailerProjectileExtension.cs b/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailerProjectileExtension.cs
index 6ece276fcd..c83e4ed5b2 100644
--- a/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailerProjectileExtension.cs
+++ b/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailerProjectileExtension.cs
@@ -9,7 +9,7 @@ namespace ProjectileImpactFX
// ProjectileImpactFX.TrailerProjectileExtension
public class TrailerProjectileExtension : DefModExtension
{
- public string trailMoteDef = "Mote_Smoke";
+ public string trailMoteDef = "AirPuff";
public float trailMoteSize = 0.5f;
public int trailerMoteInterval = 30;
public int motesThrown = 1;
From be1855f50a391ee4277129f45a8af68ccb9eb1b0 Mon Sep 17 00:00:00 2001
From: Keshash <54061981+Keshash@users.noreply.github.com>
Date: Wed, 13 Sep 2023 00:05:54 +0300
Subject: [PATCH 023/158] rename field
---
Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml | 2 +-
Defs/Ammo/Rifle/762x51mmNATO.xml | 2 +-
.../Contrib/ProjectileImpactFX/EffectProjectileExtension.cs | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml b/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml
index 058b383ebe..d3275cec70 100644
--- a/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml
+++ b/Defs/Ammo/HighCaliber/14.5x114mmSoviet.xml
@@ -204,7 +204,7 @@
2
ExplosionFlash
5
- CE_Explosion_APHE
+ CE_Explosion_APHE
diff --git a/Defs/Ammo/Rifle/762x51mmNATO.xml b/Defs/Ammo/Rifle/762x51mmNATO.xml
index 6306137d0d..fd30fe113c 100644
--- a/Defs/Ammo/Rifle/762x51mmNATO.xml
+++ b/Defs/Ammo/Rifle/762x51mmNATO.xml
@@ -226,7 +226,7 @@
0.25
ExplosionFlash
2
- CE_Explosion_APHE
+ CE_Explosion_APHE
diff --git a/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs b/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs
index 7b8fcb3328..34b3b980ad 100644
--- a/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs
+++ b/Source/CombatExtended/Contrib/ProjectileImpactFX/EffectProjectileExtension.cs
@@ -16,7 +16,7 @@ public class EffectProjectileExtension : DefModExtension
public string ImpactFleckDef = string.Empty;
public float ImpactFleckSize = 1f;
public FloatRange? ImpactFleckSizeRange;
- public string impactSoundDef = string.Empty;
+ public string ImpactSoundDef = string.Empty;
public string ImpactGlowFleckDef = string.Empty;
public float ImpactGlowFleckSize = 1f;
public FloatRange? ImpactGlowFleckSizeRange;
@@ -32,7 +32,7 @@ public void ThrowFleck(Vector3 loc, Map map, Color color, SoundDef sound, Thing
FleckDef ExplosionFleck = explosionFleckDef != string.Empty ? DefDatabase.GetNamed(this.explosionFleckDef) : null;
FleckDef ImpactFleck = ImpactFleckDef != string.Empty ? DefDatabase.GetNamed(this.ImpactFleckDef) : null;
FleckDef ImpactGlowFleck = ImpactGlowFleckDef != string.Empty ? DefDatabase.GetNamed(this.ImpactGlowFleckDef) : null;
- SoundDef ImpactSound = impactSoundDef != string.Empty ? DefDatabase.GetNamed(this.impactSoundDef) : null;
+ SoundDef ImpactSound = ImpactSoundDef != string.Empty ? DefDatabase.GetNamed(this.ImpactSoundDef) : null;
float explosionSize = this.explosionFleckSizeRange?.RandomInRange ?? this.explosionFleckSize;
float ImpactFleckSize = this.ImpactFleckSizeRange?.RandomInRange ?? this.ImpactFleckSize;
float ImpactGlowFleckSize = this.ImpactGlowFleckSizeRange?.RandomInRange ?? this.ImpactGlowFleckSize;
From 9e0f50c0cab7e70ff694e7bc01fdf8fc6e39540e Mon Sep 17 00:00:00 2001
From: Keshash <54061981+Keshash@users.noreply.github.com>
Date: Wed, 13 Sep 2023 00:08:46 +0300
Subject: [PATCH 024/158] code style
---
.../Contrib/ProjectileImpactFX/TrailThrower.cs | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs b/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs
index a030e99890..b3eeb23957 100644
--- a/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs
+++ b/Source/CombatExtended/Contrib/ProjectileImpactFX/TrailThrower.cs
@@ -1,9 +1,4 @@
using RimWorld;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using UnityEngine;
using Verse;
@@ -17,9 +12,9 @@ public static void ThrowSmoke(Vector3 loc, float size, Map map, string defName)
{
return;
}
- FleckDef fleck = DefDatabase.GetNamed(defName) ?? null;
+ FleckDef fleck = DefDatabase.GetNamed(defName) ?? null;
- if (fleck != null)
+ if (fleck != null)
{
Rand.PushState();
FleckCreationData dataStatic = FleckMaker.GetDataStatic(loc, map, fleck, Rand.Range(1.5f, 2.5f) * size);
From c5121a846eb729a8ed2d773011dad80cd2a2a648 Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Wed, 13 Sep 2023 13:15:31 +0100
Subject: [PATCH 025/158] Update WorldObjectDamageWorker.cs
---
.../CombatExtended/WorldObjects/WorldObjectDamageWorker.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
index fdfd57740c..be010fb702 100644
--- a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
+++ b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
@@ -35,12 +35,13 @@ public virtual float CalculateDamage(ThingDef projectile, Faction faction)
}
}
var result = FragmentsPotentialDamage(projectile) + ExplosionPotentialDamage(projectile) + FirePotentialDamage(projectile) + EMPPotentialDamage(projectile, empModifier);
- //Damage calculated as in-map damage, needs to be converted into world object damage. 3500f experimentally obtained
- result /= 3500f;
if (projectile.projectile is ProjectilePropertiesCE projectileProperties && projectileProperties.shellingProps.damage > 0f)
{
result = projectileProperties.shellingProps.damage;
}
+ //Damage calculated as in-map damage, needs to be converted into world object damage. 3500f experimentally obtained
+ result /= 3500f;
+ Log.Message(result.ToString());
//Crit/Miss imitation
result *= Rand.Range(0.4f, 1.5f);
return result;
From fcc85d4a032028110c0766400aec3a04a367ce55 Mon Sep 17 00:00:00 2001
From: SokyranTheDragon
Date: Fri, 15 Sep 2023 01:04:48 +0200
Subject: [PATCH 026/158] Fixed desync with multiplayer 0-tick impact
projectiles
---
.../CombatExtended/Projectiles/ProjectileCE.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 87b36d30df..3520de6655 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -170,7 +170,12 @@ public float StartingTicksToImpact
{
destinationInt = origin;
startingTicksToImpactInt = 0f;
- ImpactSomething();
+ // During drawing in Multiplayer - impact causes issues. Will get handled inside of the `Tick` call.
+ // In the future, replace this with `!InInterface` call, as it's more fitting here.
+ if (!Multiplayer.InMultiplayer)
+ {
+ ImpactSomething();
+ }
return 0f;
}
// Multiplied by ticksPerSecond since the calculated time is actually in seconds.
From 7e6f6932f9858922b79cd4b4e081143fa9f9dddb Mon Sep 17 00:00:00 2001
From: CMDR-Bill-Doors
Date: Tue, 19 Sep 2023 20:29:16 +0100
Subject: [PATCH 027/158] remove a log I forgot to remove
---
.../CombatExtended/WorldObjects/WorldObjectDamageWorker.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
index be010fb702..6d615890b8 100644
--- a/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
+++ b/Source/CombatExtended/CombatExtended/WorldObjects/WorldObjectDamageWorker.cs
@@ -41,7 +41,6 @@ public virtual float CalculateDamage(ThingDef projectile, Faction faction)
}
//Damage calculated as in-map damage, needs to be converted into world object damage. 3500f experimentally obtained
result /= 3500f;
- Log.Message(result.ToString());
//Crit/Miss imitation
result *= Rand.Range(0.4f, 1.5f);
return result;
From 4468d40608e26976e18facb6b5a11a77196633d0 Mon Sep 17 00:00:00 2001
From: Aelanna
Date: Fri, 29 Sep 2023 18:50:33 -0500
Subject: [PATCH 028/158] Updates to CheckForCollisionBetween and
BlockerRegistry
CheckForCollisionBetween is now consistent with RimWorld 1.4, which causes explosive projectiles to detonate against shields instead of vanishing.
BlockerRegistry has been updated with a proper hook for adding vanilla-style projectile interceptors, including the option to choose whether projectiles should impact or vanish.
---
.../Projectiles/ProjectileCE.cs | 15 +++++++++-
.../Compatibility/BlockerRegistry.cs | 28 +++++++++++++++++++
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 87b36d30df..5a904c828e 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -761,10 +761,23 @@ private bool CheckForCollisionBetween()
{
if (CheckIntercept(list[i], list[i].TryGetComp()))
{
- this.Destroy(DestroyMode.Vanish);
+ this.Impact(null);
return true;
}
}
+ (bool intercepted, bool destroyed) = BlockerRegistry.CheckForCollisionBetweenCallback(this, LastPos, ExactPosition);
+ if (intercepted)
+ {
+ if (destroyed)
+ {
+ this.Destroy(DestroyMode.Vanish);
+ }
+ else
+ {
+ this.Impact(null);
+ }
+ return true;
+ }
#region Sanity checks
if (ticksToImpact < 0 || def.projectile.flyOverhead)
diff --git a/Source/CombatExtended/Compatibility/BlockerRegistry.cs b/Source/CombatExtended/Compatibility/BlockerRegistry.cs
index ab526dff6d..6027577873 100644
--- a/Source/CombatExtended/Compatibility/BlockerRegistry.cs
+++ b/Source/CombatExtended/Compatibility/BlockerRegistry.cs
@@ -12,16 +12,27 @@ namespace CombatExtended.Compatibility
public static class BlockerRegistry
{
private static bool enabled = false;
+ private static List> checkForCollisionBetweenCallbacks;
private static List> checkCellForCollisionCallbacks;
private static List> impactSomethingCallbacks;
private static void Enable()
{
enabled = true;
+ checkForCollisionBetweenCallbacks = new List>();
impactSomethingCallbacks = new List>();
checkCellForCollisionCallbacks = new List>();
}
+ public static void RegisterCheckForCollisionBetweenCallback(Func f)
+ {
+ if (!enabled)
+ {
+ Enable();
+ }
+ checkForCollisionBetweenCallbacks.Add(f);
+ }
+
public static void RegisterCheckForCollisionCallback(Func f)
{
if (!enabled)
@@ -40,6 +51,23 @@ public static void RegisterImpactSomethingCallback(Func
Date: Sat, 30 Sep 2023 00:38:45 -0500
Subject: [PATCH 029/158] Separated intercept/impact logic from collision check
Created methods allowing shield/interceptor implementations to both directly control what happens to an intercepted projectile (destroyed vs impacted) as well as centralizing logic to handle impact positioning.
Methods are virtual to allow projectiles to override default intercept behavior if necessary.
Moved a utility method that was a private static on ProjectileCE out to CE_Utility so external shield implementations can use the same calculations for radial intercepts.
---
.../CombatExtended/CE_Utility.cs | 25 ++++++++
.../Projectiles/ProjectileCE.cs | 59 ++++++++++---------
.../Compatibility/BlockerRegistry.cs | 17 +++---
3 files changed, 64 insertions(+), 37 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 25429f39d5..bd8ab7e0de 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -883,6 +883,31 @@ public static float GetCollisionWidth(Thing thing)
return 1f; //Buildings, etc. fill out a full square
}
+ ///
+ /// Calculates whether a line segment intercepts a radius circle. Used to determine if a projectile crosses a shield.
+ ///
+ /// The center of the circle
+ /// The radius of the circle
+ /// The first point of the line segment
+ /// The second point of the line segment
+ /// True if the line segment intercepts the circle
+ public static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
+ {
+ var pointAInShield = (center - pointA).sqrMagnitude <= Mathf.Pow(radius, 2);
+ var pointBInShield = (center - pointB).sqrMagnitude <= Mathf.Pow(radius, 2);
+
+ if (pointAInShield && pointBInShield)
+ {
+ return false;
+ }
+ if (!pointAInShield && !pointBInShield)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
///
/// Calculates body scale factors based on body type
///
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 5a904c828e..d740f2aa9d 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -644,6 +644,34 @@ public virtual void Launch(Thing launcher, Vector2 origin, Thing equipment = nul
#endregion
#region Collisions
+ public virtual void InterceptProjectile(object interceptor, Vector3 impactPosition, bool destroyCompletely = false)
+ {
+ if (destroyCompletely)
+ {
+ this.Destroy(DestroyMode.Vanish);
+ }
+ else
+ {
+ this.Impact(null);
+ ExactPosition = impactPosition;
+ landed = true;
+ }
+ }
+ public virtual void InterceptProjectile(object interceptor, Vector3 shieldPosition, float shieldRadius, bool destroyCompletely = false)
+ {
+ if (destroyCompletely)
+ {
+ this.Destroy(DestroyMode.Vanish);
+ }
+ else
+ {
+ this.Impact(null);
+ ExactPosition = BlockerRegistry.GetExactPosition(OriginIV3.ToVector3(), ExactPosition, shieldPosition, shieldRadius);
+ landed = true;
+ }
+ }
+
+
private bool CheckIntercept(Thing interceptorThing, CompProjectileInterceptor interceptorComp, bool withDebug = false)
{
Vector3 shieldPosition = interceptorThing.Position.ToVector3ShiftedWithAltitude(0.5f);
@@ -679,7 +707,7 @@ private bool CheckIntercept(Thing interceptorThing, CompProjectileInterceptor in
{
return false;
}
- if (!IntersectLineSphericalOutline(shieldPosition, radius, lastExactPos, newExactPos))
+ if (!CE_Utility.IntersectLineSphericalOutline(shieldPosition, radius, lastExactPos, newExactPos))
{
return false;
}
@@ -731,23 +759,6 @@ private bool CheckIntercept(Thing interceptorThing, CompProjectileInterceptor in
return true;
}
- private static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
- {
- var pointAInShield = (center - pointA).sqrMagnitude <= Mathf.Pow(radius, 2);
- var pointBInShield = (center - pointB).sqrMagnitude <= Mathf.Pow(radius, 2);
-
- if (pointAInShield && pointBInShield)
- {
- return false;
- }
- if (!pointAInShield && !pointBInShield)
- {
- return false;
- }
-
- return true;
- }
-
//Removed minimum collision distance
private bool CheckForCollisionBetween()
{
@@ -762,20 +773,12 @@ private bool CheckForCollisionBetween()
if (CheckIntercept(list[i], list[i].TryGetComp()))
{
this.Impact(null);
+ landed = true;
return true;
}
}
- (bool intercepted, bool destroyed) = BlockerRegistry.CheckForCollisionBetweenCallback(this, LastPos, ExactPosition);
- if (intercepted)
+ if (BlockerRegistry.CheckForCollisionBetweenCallback(this, LastPos, ExactPosition))
{
- if (destroyed)
- {
- this.Destroy(DestroyMode.Vanish);
- }
- else
- {
- this.Impact(null);
- }
return true;
}
diff --git a/Source/CombatExtended/Compatibility/BlockerRegistry.cs b/Source/CombatExtended/Compatibility/BlockerRegistry.cs
index 6027577873..f120fd5339 100644
--- a/Source/CombatExtended/Compatibility/BlockerRegistry.cs
+++ b/Source/CombatExtended/Compatibility/BlockerRegistry.cs
@@ -12,19 +12,19 @@ namespace CombatExtended.Compatibility
public static class BlockerRegistry
{
private static bool enabled = false;
- private static List> checkForCollisionBetweenCallbacks;
+ private static List> checkForCollisionBetweenCallbacks;
private static List> checkCellForCollisionCallbacks;
private static List> impactSomethingCallbacks;
private static void Enable()
{
enabled = true;
- checkForCollisionBetweenCallbacks = new List>();
+ checkForCollisionBetweenCallbacks = new List>();
impactSomethingCallbacks = new List>();
checkCellForCollisionCallbacks = new List>();
}
- public static void RegisterCheckForCollisionBetweenCallback(Func f)
+ public static void RegisterCheckForCollisionBetweenCallback(Func f)
{
if (!enabled)
{
@@ -51,21 +51,20 @@ public static void RegisterImpactSomethingCallback(Func
Date: Fri, 29 Sep 2023 23:01:33 -0700
Subject: [PATCH 030/158] Make IntersectLinesphericaloutline branchless
---
.../CombatExtended/CombatExtended/CE_Utility.cs | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index bd8ab7e0de..2854fbbf9f 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -893,19 +893,8 @@ public static float GetCollisionWidth(Thing thing)
/// True if the line segment intercepts the circle
public static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
{
- var pointAInShield = (center - pointA).sqrMagnitude <= Mathf.Pow(radius, 2);
- var pointBInShield = (center - pointB).sqrMagnitude <= Mathf.Pow(radius, 2);
-
- if (pointAInShield && pointBInShield)
- {
- return false;
- }
- if (!pointAInShield && !pointBInShield)
- {
- return false;
- }
-
- return true;
+ var radSq = radius * radius;
+ return ((center - pointA).sqrMagnitude > radSq) != ((center - pointB).sqrMagnitude > radSq);
}
///
From eb62205af4db0c4a045bfe48565ef73ac7e5214e Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Fri, 29 Sep 2023 23:01:57 -0700
Subject: [PATCH 031/158] Defer to default InterceptProjectile Implementation
with calculated position
---
.../CombatExtended/Projectiles/ProjectileCE.cs | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index d740f2aa9d..6542086f62 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -659,18 +659,8 @@ public virtual void InterceptProjectile(object interceptor, Vector3 impactPositi
}
public virtual void InterceptProjectile(object interceptor, Vector3 shieldPosition, float shieldRadius, bool destroyCompletely = false)
{
- if (destroyCompletely)
- {
- this.Destroy(DestroyMode.Vanish);
- }
- else
- {
- this.Impact(null);
- ExactPosition = BlockerRegistry.GetExactPosition(OriginIV3.ToVector3(), ExactPosition, shieldPosition, shieldRadius);
- landed = true;
- }
- }
-
+ InterceptProjectile(interceptor, BlockerRegistry.GetExactPosition(OriginIV3.ToVector3(), ExactPosition, shieldPosition, shieldRadius * shieldRadius));
+ }
private bool CheckIntercept(Thing interceptorThing, CompProjectileInterceptor interceptorComp, bool withDebug = false)
{
From 918d9339707484b21f56d1a6ffab1819fba77a9f Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Fri, 29 Sep 2023 23:02:17 -0700
Subject: [PATCH 032/158] Always set landed and exact position, before calling
impact or destroy
---
.../CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 6542086f62..d5a5524d8b 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -646,6 +646,8 @@ public virtual void Launch(Thing launcher, Vector2 origin, Thing equipment = nul
#region Collisions
public virtual void InterceptProjectile(object interceptor, Vector3 impactPosition, bool destroyCompletely = false)
{
+ ExactPosition = impactPosition;
+ landed = true;
if (destroyCompletely)
{
this.Destroy(DestroyMode.Vanish);
@@ -653,8 +655,6 @@ public virtual void InterceptProjectile(object interceptor, Vector3 impactPositi
else
{
this.Impact(null);
- ExactPosition = impactPosition;
- landed = true;
}
}
public virtual void InterceptProjectile(object interceptor, Vector3 shieldPosition, float shieldRadius, bool destroyCompletely = false)
From 3ab3a3d5df340eee5a508df875932d89a3eee7e8 Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Fri, 29 Sep 2023 23:56:31 -0700
Subject: [PATCH 033/158] Use determinate to identify spherical outline
intersections
---
Source/CombatExtended/CombatExtended/CE_Utility.cs | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 2854fbbf9f..911ddcb1a5 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -893,8 +893,14 @@ public static float GetCollisionWidth(Thing thing)
/// True if the line segment intercepts the circle
public static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
{
- var radSq = radius * radius;
- return ((center - pointA).sqrMagnitude > radSq) != ((center - pointB).sqrMagnitude > radSq);
+ Vector3 displacement = pointB - pointA;
+ double a = displacement.sqrMagnitude;
+ double b = 2 * (displacement.x * (pointA.x - center.x) + displacement.y * (pointA.y - center.y) + displacement.z * (pointA.z - center.z));
+ double c = (center - pointA).sqrMagnitude - radius * radius;
+ double det = b * b - 4 * a * c;
+ return det >= 0;
+// var radSq = radius * radius;
+// return ((center - pointA).sqrMagnitude > radSq) != ((center - pointB).sqrMagnitude > radSq);
}
///
From abf18a10d891356cc17720980dcafc238c4109ae Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Fri, 29 Sep 2023 23:57:58 -0700
Subject: [PATCH 034/158] Precalculate and store the squared radius
---
.../CombatExtended/Projectiles/ProjectileCE.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index d5a5524d8b..505ff4724d 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -667,10 +667,11 @@ private bool CheckIntercept(Thing interceptorThing, CompProjectileInterceptor in
Vector3 shieldPosition = interceptorThing.Position.ToVector3ShiftedWithAltitude(0.5f);
float radius = interceptorComp.Props.radius;
float blockRadius = radius + def.projectile.SpeedTilesPerTick + 0.1f;
+ float radiusSq = blockRadius * blockRadius;
var newExactPos = ExactPosition;
- if ((newExactPos - shieldPosition).sqrMagnitude > Mathf.Pow(blockRadius, 2))
+ if ((newExactPos - shieldPosition).sqrMagnitude > radiusSq)
{
return false;
}
@@ -693,7 +694,7 @@ private bool CheckIntercept(Thing interceptorThing, CompProjectileInterceptor in
{
return false;
}
- if (!interceptorComp.Props.interceptOutgoingProjectiles && (shieldPosition - lastExactPos).sqrMagnitude <= Mathf.Pow((float)radius, 2))
+ if (!interceptorComp.Props.interceptOutgoingProjectiles && (shieldPosition - lastExactPos).sqrMagnitude <= radius * radius)
{
return false;
}
From b0bb86a767ab54b47ace1e2f2440ffdc23c945d4 Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Fri, 29 Sep 2023 23:58:21 -0700
Subject: [PATCH 035/158] Set landed before calling impact
---
.../CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 505ff4724d..9f76253d3f 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -763,8 +763,8 @@ private bool CheckForCollisionBetween()
{
if (CheckIntercept(list[i], list[i].TryGetComp()))
{
- this.Impact(null);
landed = true;
+ this.Impact(null);
return true;
}
}
From f8018e8113ee57df6bbf8eb23b9d901e483c11e3 Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Fri, 29 Sep 2023 23:59:07 -0700
Subject: [PATCH 036/158] Use collisionbetween callback for ED SHields
---
Source/CombatExtended/Compatibility/EDShields.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/Compatibility/EDShields.cs b/Source/CombatExtended/Compatibility/EDShields.cs
index 4f418ee23f..162a8f008e 100644
--- a/Source/CombatExtended/Compatibility/EDShields.cs
+++ b/Source/CombatExtended/Compatibility/EDShields.cs
@@ -33,7 +33,7 @@ public bool CanInstall()
}
public void Install()
{
- BlockerRegistry.RegisterCheckForCollisionCallback(EDShields.CheckForCollisionCallback);
+ BlockerRegistry.RegisterCheckForCollisionBetweenCallback(EDShields.CheckForCollisionBetweenCallback);
BlockerRegistry.RegisterImpactSomethingCallback(EDShields.ImpactSomethingCallback);
Type t = Type.GetType("Jaxxa.EnhancedDevelopment.Shields.Shields.ShieldManagerMapComp, ED-Shields");
HitSoundDef = (SoundDef)t.GetField("HitSoundDef", BindingFlags.Static | BindingFlags.Public).GetValue(null);
@@ -42,10 +42,11 @@ public IEnumerable GetCompatList()
{
yield break;
}
- public static bool CheckForCollisionCallback(ProjectileCE projectile, IntVec3 cell, Thing launcher)
+ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vector3 from, Vector3 to)
{
/* Check if an active shield can block this projectile, we don't check if the projectile flies overhead, as those projectiles don't call this function
*/
+ Thing launcher = projectile.launcher;
Map map = projectile.Map;
Vector3 exactPosition = projectile.ExactPosition;
IntVec3 origin = projectile.OriginIV3;
From 710842ad8dae04339ce29c9e923a6c5e53758be9 Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Fri, 29 Sep 2023 23:59:46 -0700
Subject: [PATCH 037/158] Don't stop fly-overhead projectiles at the edge of
the shield
---
Source/CombatExtended/Compatibility/EDShields.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/Source/CombatExtended/Compatibility/EDShields.cs b/Source/CombatExtended/Compatibility/EDShields.cs
index 162a8f008e..f2743b036d 100644
--- a/Source/CombatExtended/Compatibility/EDShields.cs
+++ b/Source/CombatExtended/Compatibility/EDShields.cs
@@ -44,8 +44,11 @@ public IEnumerable GetCompatList()
}
public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vector3 from, Vector3 to)
{
- /* Check if an active shield can block this projectile, we don't check if the projectile flies overhead, as those projectiles don't call this function
+ /* Check if an active shield can block this projectile
*/
+ if (projectile.def.projectile.flyOverhead) {
+ return false;
+ }
Thing launcher = projectile.launcher;
Map map = projectile.Map;
Vector3 exactPosition = projectile.ExactPosition;
From 953b8137ab191413fdea689db4961d817de7a06c Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sat, 30 Sep 2023 00:02:27 -0700
Subject: [PATCH 038/158] Use SphericalOutline function to check for intercept
---
.../CombatExtended/Compatibility/EDShields.cs | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/Source/CombatExtended/Compatibility/EDShields.cs b/Source/CombatExtended/Compatibility/EDShields.cs
index f2743b036d..560de23a19 100644
--- a/Source/CombatExtended/Compatibility/EDShields.cs
+++ b/Source/CombatExtended/Compatibility/EDShields.cs
@@ -70,18 +70,15 @@ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vec
continue;
}
int fieldRadius = (int)generator.FieldRadius_Active();
+ Vector3 shieldPosition2D = new Vector3(shield.Position.x, 0, shield.Position.z);
+
+ if (!CE_Utility.IntersectLineSphericalOutline(shieldPosition2D, fieldRadius, from, to))
+ {
+ continue;
+ }
+
int fieldRadiusSq = fieldRadius * fieldRadius;
- float DistanceSq = projectile.Position.DistanceToSquared(shield.Position) - fieldRadiusSq;
- float originDistanceSq = origin.DistanceToSquared(shield.Position) - fieldRadiusSq;
- if (DistanceSq > 0)
- {
- continue;
- }
- if (originDistanceSq < 0)
- {
- continue;
- }
- Vector3 shieldPosition2D = new Vector3(shield.Position.x, 0, shield.Position.z);
+
Quaternion targetAngle = projectile.ExactRotation;
Quaternion shieldProjAng = Quaternion.LookRotation(exactPosition - shieldPosition2D);
if ((Quaternion.Angle(targetAngle, shieldProjAng) > 90))
From 469a6ed1701b295541fd5c30f952e2be757b9632 Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sat, 30 Sep 2023 00:02:50 -0700
Subject: [PATCH 039/158] Use InterceptProjectile to handle marking the
projectile landed and set its position
---
Source/CombatExtended/Compatibility/EDShields.cs | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/Source/CombatExtended/Compatibility/EDShields.cs b/Source/CombatExtended/Compatibility/EDShields.cs
index 560de23a19..0d99eceac1 100644
--- a/Source/CombatExtended/Compatibility/EDShields.cs
+++ b/Source/CombatExtended/Compatibility/EDShields.cs
@@ -83,17 +83,15 @@ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vec
Quaternion shieldProjAng = Quaternion.LookRotation(exactPosition - shieldPosition2D);
if ((Quaternion.Angle(targetAngle, shieldProjAng) > 90))
{
-
HitSoundDef.PlayOneShot((SoundInfo)new TargetInfo(shield.Position, map, false));
-
int damage = (projectile.def.projectile.GetDamageAmount(launcher));
generator.FieldIntegrity_Current -= damage;
- exactPosition = BlockerRegistry.GetExactPosition(origin.ToVector3(), projectile.ExactPosition, shield.Position.ToVector3(), (fieldRadius - 1) * (fieldRadius - 1));
+ exactPosition = BlockerRegistry.GetExactPosition(from, to, shieldPosition2D, (fieldRadius - 1) * (fieldRadius - 1));
FleckMakerCE.ThrowLightningGlow(exactPosition, map, 0.5f);
- projectile.ExactPosition = exactPosition;
+ projectile.InterceptProjectile(shield, exactPosition, false);
return true;
}
}
From e60ab8bca4b299d8516670cbbdcc0e77ff9f971a Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sat, 30 Sep 2023 00:19:26 -0700
Subject: [PATCH 040/158] Also set ticksToImpact to 0
---
Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 9f76253d3f..e268527337 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -648,6 +648,7 @@ public virtual void InterceptProjectile(object interceptor, Vector3 impactPositi
{
ExactPosition = impactPosition;
landed = true;
+ ticksToImpact = 0;
if (destroyCompletely)
{
this.Destroy(DestroyMode.Vanish);
From 2c74ae3160d27f9708f76cc938e67fed964a3d0e Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sat, 30 Sep 2023 00:19:46 -0700
Subject: [PATCH 041/158] Require the users of the blocker registry to call the
projectile cleanup code
---
.../CombatExtended/Projectiles/ProjectileCE.cs | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index e268527337..9189b9bd3b 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -831,11 +831,7 @@ private bool CheckCellForCollision(IntVec3 cell)
{
if (BlockerRegistry.CheckCellForCollisionCallback(this, cell, launcher))
{
- this.ticksToImpact = 0;
- this.landed = true;
-
- this.Impact(null);
- return true;
+ return true;
}
var roofChecked = false;
@@ -1197,7 +1193,6 @@ public void ImpactSomething()
{
if (BlockerRegistry.ImpactSomethingCallback(this, launcher))
{
- this.Destroy();
return;
}
var pos = ExactPosition.ToIntVec3();
From f71fe7f847bd15f52b93a9f2304776534a193ebe Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sat, 30 Sep 2023 00:20:22 -0700
Subject: [PATCH 042/158] Use new projectile cleanup code in BlockerRegistry
users
---
.../CombatExtended/Compatibility/EDShields.cs | 1 +
.../Compatibility/Rimatomics.cs | 3 ++-
.../VanillaFurnitureExpandedSecurity.cs | 21 ++++++++++++-------
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/Source/CombatExtended/Compatibility/EDShields.cs b/Source/CombatExtended/Compatibility/EDShields.cs
index 0d99eceac1..a58810beb1 100644
--- a/Source/CombatExtended/Compatibility/EDShields.cs
+++ b/Source/CombatExtended/Compatibility/EDShields.cs
@@ -132,6 +132,7 @@ public static bool ImpactSomethingCallback(ProjectileCE projectile, Thing launch
FleckMakerCE.ThrowLightningGlow(destination, map, 0.5f);
int damage = (projectile.def.projectile.GetDamageAmount(launcher));
generator.FieldIntegrity_Current -= damage;
+ projectile.InterceptProjectile(shield, projectile.ExactPosition, true);
return true;
}
return false;
diff --git a/Source/CombatExtended/Compatibility/Rimatomics.cs b/Source/CombatExtended/Compatibility/Rimatomics.cs
index 28495c9d88..d49b887e33 100644
--- a/Source/CombatExtended/Compatibility/Rimatomics.cs
+++ b/Source/CombatExtended/Compatibility/Rimatomics.cs
@@ -103,7 +103,7 @@ public static bool CheckForCollisionCallback(ProjectileCE projectile, IntVec3 ce
}
-
+ projectile.InterceptProjectile(shield, exactPosition, true);
return true;
}
@@ -148,6 +148,7 @@ public static bool ImpactSomethingCallback(ProjectileCE projectile, Thing launch
DamageInfo dinfo = new DamageInfo(projectile.def.projectile.damageDef, (float)damage, 0f, -1f, null, null, null, 0, null, true, true);
shield.BreakShield(dinfo);
}
+ projectile.InterceptProjectile(shield, projectile.ExactPosition, true);
return true;
}
return false;
diff --git a/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs b/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs
index 700f104c40..c81f89d516 100644
--- a/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs
+++ b/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs
@@ -70,15 +70,16 @@ private static bool CheckCollision(ProjectileCE projectile, IntVec3 cell, Thing
continue;
}
- projectile.ExactPosition = BlockerRegistry.GetExactPosition(projectile.OriginIV3.ToVector3(),
- exactPosition,
- new Vector3(shield.Position.x, 0, shield.Position.z),
- shield.ShieldRadius * shield.ShieldRadius);
+ exactPosition = BlockerRegistry.GetExactPosition(projectile.OriginIV3.ToVector3(),
+ exactPosition,
+ new Vector3(shield.Position.x, 0, shield.Position.z),
+ shield.ShieldRadius * shield.ShieldRadius);
if (!(projectile is ProjectileCE_Explosive))
{
shield.AbsorbDamage(projectile.def.projectile.GetDamageAmount(launcher), projectile.def.projectile.damageDef, projectile.ExactRotation.eulerAngles.y);
}
+ projectile.InterceptProjectile(shield, exactPosition, true);
return true;
}
@@ -92,9 +93,15 @@ private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
refreshShields(map);
- var blocked = shields.Any(building => ShieldInterceptsProjectile(building as Building_Shield, projectile, launcher));
- CELogger.Message($"Blocked {projectile}? -- {blocked}");
- return blocked;
+ foreach (var building in shields)
+ {
+ if (building is Building_Shield bs && ShieldInterceptsProjectile(bs, projectile, launcher))
+ {
+ projectile.InterceptProjectile(bs, exactPosition, true);
+ return true;
+ }
+ }
+ return false;
}
private static bool ShieldInterceptsProjectile(Building building, ProjectileCE projectile, Thing launcher)
From 80394de4048dd13056655584437aa8fc4b853842 Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sun, 1 Oct 2023 14:11:49 -0700
Subject: [PATCH 043/158] Don't duplicate sqrt calls
---
.../CombatExtended/Compatibility/VanillaPsycastExpanded.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
index 70d8adfec6..2712dd1161 100644
--- a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
+++ b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
@@ -171,8 +171,9 @@ public static Vector3[] IntersectionPoint(Vector3 p1, Vector3 p2, Vector3 center
// line does not intersect
return new Vector3[] { Vector3.zero, Vector3.zero };
}
- mu1 = (-b + Mathf.Sqrt(bb4ac)) / (2 * a);
- mu2 = (-b - Mathf.Sqrt(bb4ac)) / (2 * a);
+ let sqrtbb4ac = Mathf.Sqrt(bb4ac)
+ mu1 = (-b + sqrtbb4ac) / (2 * a);
+ mu2 = (-b - sqrtbb4ac) / (2 * a);
sect = new Vector3[2];
sect[0] = new Vector3(p1.x + mu1 * (p2.x - p1.x), 0, p1.z + mu1 * (p2.z - p1.z));
sect[1] = new Vector3(p1.x + mu2 * (p2.x - p1.x), 0, p1.z + mu2 * (p2.z - p1.z));
From 9ee1354690e45235e328e8a9f5eb9d9387ac79b7 Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sun, 1 Oct 2023 14:12:01 -0700
Subject: [PATCH 044/158] Use new InterceptProjectile call
---
Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
index 2712dd1161..ed48a3ba18 100644
--- a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
+++ b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
@@ -48,6 +48,7 @@ private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
Effecter eff = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
eff.Trigger(new TargetInfo(projectile.ExactPosition.ToIntVec3(), interceptor.Map, false), TargetInfo.Invalid);
eff.Cleanup();
+ projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
return true;
}
else
@@ -80,6 +81,7 @@ public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing l
Effecter e = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
e.Trigger(new TargetInfo(newExactPos.ToIntVec3(), interceptor.pawn.Map, false), TargetInfo.Invalid);
e.Cleanup();
+ projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
}
return result;
@@ -122,6 +124,7 @@ public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing l
Effecter eff = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
eff.Trigger(new TargetInfo(newExactPos.ToIntVec3(), interceptor.pawn.Map, false), TargetInfo.Invalid);
eff.Cleanup();
+ projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
return true;
}
From 5f4b626b98e7104e22fe31917c2a020b3db668fe Mon Sep 17 00:00:00 2001
From: Logan Perkins
Date: Sun, 1 Oct 2023 14:12:17 -0700
Subject: [PATCH 045/158] Handle the simple cases quickly, then use dot
products to find high
speed intercepts
---
.../CombatExtended/CE_Utility.cs | 47 +++++++++++++++----
1 file changed, 39 insertions(+), 8 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 911ddcb1a5..6b87d08e30 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -893,14 +893,45 @@ public static float GetCollisionWidth(Thing thing)
/// True if the line segment intercepts the circle
public static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
{
- Vector3 displacement = pointB - pointA;
- double a = displacement.sqrMagnitude;
- double b = 2 * (displacement.x * (pointA.x - center.x) + displacement.y * (pointA.y - center.y) + displacement.z * (pointA.z - center.z));
- double c = (center - pointA).sqrMagnitude - radius * radius;
- double det = b * b - 4 * a * c;
- return det >= 0;
-// var radSq = radius * radius;
-// return ((center - pointA).sqrMagnitude > radSq) != ((center - pointB).sqrMagnitude > radSq);
+ var radSq = radius * radius;
+ var aInside = ((center - pointA).sqrMagnitude <= radSq);
+ var bInside = ((center - pointB).sqrMagnitude <= radSq);
+ if (aInside != bInside) // One end is inside, one is outside -- we crossed
+ {
+ return true;
+ }
+ if (aInside && bInside) // Both are inside, we did not cross
+ {
+ return false;
+ }
+
+ // Both are outside, so check if the point on the line-segment AB closest to point C is inside.
+
+ Vector3 direction = pointB - pointA;
+ Vector3 displacement = pointA - center;
+
+ // Calculate the dot product between the relative position at the start and the direction of travel.
+ float dotProduct = displacement.x * direction.x + displacement.y * direction.y + displacement.z * direction.z;
+
+ if (dotProduct < 0) // Moving *away* from the shield
+ {
+ return false;
+ }
+
+ if (dotProduct > direction.sqrMagnitude) // Still moving closer, might hit next tick but it hasn't arrived. We don't need to check if the end point is inside, because we already did that.
+ {
+ return false;
+ }
+
+ // The center lies some distance perpindicular to the line segment AB
+ // Rotate the direction vector 90 degrees, and find its dot product with the relative displacement to find that distance.
+ dotProduct = displacement.x * direction.z - displacement.z * direction.x + displacement.y * direction.y;
+
+ if (dotProduct * dotProduct <= direction.sqrMagnitude * displacement.sqrMagnitude)
+ {
+ return true;
+ }
+ return false;
}
///
From 2dd4c69d8a678d818553a36460230b6c5b37bce6 Mon Sep 17 00:00:00 2001
From: MaxDorob
Date: Tue, 3 Oct 2023 20:40:59 +0600
Subject: [PATCH 046/158] Build and style fix
---
.../CombatExtended/CE_Utility.cs | 78 +++++++++----------
.../Projectiles/ProjectileCE.cs | 14 ++--
.../CombatExtended/Compatibility/EDShields.cs | 23 +++---
.../Compatibility/Rimatomics.cs | 4 +-
.../VanillaFurnitureExpandedSecurity.cs | 28 +++----
.../Compatibility/VanillaPsycastExpanded.cs | 8 +-
6 files changed, 78 insertions(+), 77 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 6b87d08e30..77cb2a8a16 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -893,45 +893,45 @@ public static float GetCollisionWidth(Thing thing)
/// True if the line segment intercepts the circle
public static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
{
- var radSq = radius * radius;
- var aInside = ((center - pointA).sqrMagnitude <= radSq);
- var bInside = ((center - pointB).sqrMagnitude <= radSq);
- if (aInside != bInside) // One end is inside, one is outside -- we crossed
- {
- return true;
- }
- if (aInside && bInside) // Both are inside, we did not cross
- {
- return false;
- }
-
- // Both are outside, so check if the point on the line-segment AB closest to point C is inside.
-
- Vector3 direction = pointB - pointA;
- Vector3 displacement = pointA - center;
-
- // Calculate the dot product between the relative position at the start and the direction of travel.
- float dotProduct = displacement.x * direction.x + displacement.y * direction.y + displacement.z * direction.z;
-
- if (dotProduct < 0) // Moving *away* from the shield
- {
- return false;
- }
-
- if (dotProduct > direction.sqrMagnitude) // Still moving closer, might hit next tick but it hasn't arrived. We don't need to check if the end point is inside, because we already did that.
- {
- return false;
- }
-
- // The center lies some distance perpindicular to the line segment AB
- // Rotate the direction vector 90 degrees, and find its dot product with the relative displacement to find that distance.
- dotProduct = displacement.x * direction.z - displacement.z * direction.x + displacement.y * direction.y;
-
- if (dotProduct * dotProduct <= direction.sqrMagnitude * displacement.sqrMagnitude)
- {
- return true;
- }
- return false;
+ var radSq = radius * radius;
+ var aInside = ((center - pointA).sqrMagnitude <= radSq);
+ var bInside = ((center - pointB).sqrMagnitude <= radSq);
+ if (aInside != bInside) // One end is inside, one is outside -- we crossed
+ {
+ return true;
+ }
+ if (aInside && bInside) // Both are inside, we did not cross
+ {
+ return false;
+ }
+
+ // Both are outside, so check if the point on the line-segment AB closest to point C is inside.
+
+ Vector3 direction = pointB - pointA;
+ Vector3 displacement = pointA - center;
+
+ // Calculate the dot product between the relative position at the start and the direction of travel.
+ float dotProduct = displacement.x * direction.x + displacement.y * direction.y + displacement.z * direction.z;
+
+ if (dotProduct < 0) // Moving *away* from the shield
+ {
+ return false;
+ }
+
+ if (dotProduct > direction.sqrMagnitude) // Still moving closer, might hit next tick but it hasn't arrived. We don't need to check if the end point is inside, because we already did that.
+ {
+ return false;
+ }
+
+ // The center lies some distance perpindicular to the line segment AB
+ // Rotate the direction vector 90 degrees, and find its dot product with the relative displacement to find that distance.
+ dotProduct = displacement.x * direction.z - displacement.z * direction.x + displacement.y * direction.y;
+
+ if (dotProduct * dotProduct <= direction.sqrMagnitude * displacement.sqrMagnitude)
+ {
+ return true;
+ }
+ return false;
}
///
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 9189b9bd3b..649cf8588b 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -646,9 +646,9 @@ public virtual void Launch(Thing launcher, Vector2 origin, Thing equipment = nul
#region Collisions
public virtual void InterceptProjectile(object interceptor, Vector3 impactPosition, bool destroyCompletely = false)
{
- ExactPosition = impactPosition;
- landed = true;
- ticksToImpact = 0;
+ ExactPosition = impactPosition;
+ landed = true;
+ ticksToImpact = 0;
if (destroyCompletely)
{
this.Destroy(DestroyMode.Vanish);
@@ -660,15 +660,15 @@ public virtual void InterceptProjectile(object interceptor, Vector3 impactPositi
}
public virtual void InterceptProjectile(object interceptor, Vector3 shieldPosition, float shieldRadius, bool destroyCompletely = false)
{
- InterceptProjectile(interceptor, BlockerRegistry.GetExactPosition(OriginIV3.ToVector3(), ExactPosition, shieldPosition, shieldRadius * shieldRadius));
- }
+ InterceptProjectile(interceptor, BlockerRegistry.GetExactPosition(OriginIV3.ToVector3(), ExactPosition, shieldPosition, shieldRadius * shieldRadius));
+ }
private bool CheckIntercept(Thing interceptorThing, CompProjectileInterceptor interceptorComp, bool withDebug = false)
{
Vector3 shieldPosition = interceptorThing.Position.ToVector3ShiftedWithAltitude(0.5f);
float radius = interceptorComp.Props.radius;
float blockRadius = radius + def.projectile.SpeedTilesPerTick + 0.1f;
- float radiusSq = blockRadius * blockRadius;
+ float radiusSq = blockRadius * blockRadius;
var newExactPos = ExactPosition;
@@ -831,7 +831,7 @@ private bool CheckCellForCollision(IntVec3 cell)
{
if (BlockerRegistry.CheckCellForCollisionCallback(this, cell, launcher))
{
- return true;
+ return true;
}
var roofChecked = false;
diff --git a/Source/CombatExtended/Compatibility/EDShields.cs b/Source/CombatExtended/Compatibility/EDShields.cs
index a58810beb1..6bd75d1e76 100644
--- a/Source/CombatExtended/Compatibility/EDShields.cs
+++ b/Source/CombatExtended/Compatibility/EDShields.cs
@@ -46,10 +46,11 @@ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vec
{
/* Check if an active shield can block this projectile
*/
- if (projectile.def.projectile.flyOverhead) {
- return false;
- }
- Thing launcher = projectile.launcher;
+ if (projectile.def.projectile.flyOverhead)
+ {
+ return false;
+ }
+ Thing launcher = projectile.launcher;
Map map = projectile.Map;
Vector3 exactPosition = projectile.ExactPosition;
IntVec3 origin = projectile.OriginIV3;
@@ -70,12 +71,12 @@ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vec
continue;
}
int fieldRadius = (int)generator.FieldRadius_Active();
- Vector3 shieldPosition2D = new Vector3(shield.Position.x, 0, shield.Position.z);
+ Vector3 shieldPosition2D = new Vector3(shield.Position.x, 0, shield.Position.z);
- if (!CE_Utility.IntersectLineSphericalOutline(shieldPosition2D, fieldRadius, from, to))
- {
- continue;
- }
+ if (!CE_Utility.IntersectLineSphericalOutline(shieldPosition2D, fieldRadius, from, to))
+ {
+ continue;
+ }
int fieldRadiusSq = fieldRadius * fieldRadius;
@@ -91,7 +92,7 @@ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vec
exactPosition = BlockerRegistry.GetExactPosition(from, to, shieldPosition2D, (fieldRadius - 1) * (fieldRadius - 1));
FleckMakerCE.ThrowLightningGlow(exactPosition, map, 0.5f);
- projectile.InterceptProjectile(shield, exactPosition, false);
+ projectile.InterceptProjectile(shield, exactPosition, false);
return true;
}
}
@@ -132,7 +133,7 @@ public static bool ImpactSomethingCallback(ProjectileCE projectile, Thing launch
FleckMakerCE.ThrowLightningGlow(destination, map, 0.5f);
int damage = (projectile.def.projectile.GetDamageAmount(launcher));
generator.FieldIntegrity_Current -= damage;
- projectile.InterceptProjectile(shield, projectile.ExactPosition, true);
+ projectile.InterceptProjectile(shield, projectile.ExactPosition, true);
return true;
}
return false;
diff --git a/Source/CombatExtended/Compatibility/Rimatomics.cs b/Source/CombatExtended/Compatibility/Rimatomics.cs
index d49b887e33..2a9bc0d838 100644
--- a/Source/CombatExtended/Compatibility/Rimatomics.cs
+++ b/Source/CombatExtended/Compatibility/Rimatomics.cs
@@ -103,7 +103,7 @@ public static bool CheckForCollisionCallback(ProjectileCE projectile, IntVec3 ce
}
- projectile.InterceptProjectile(shield, exactPosition, true);
+ projectile.InterceptProjectile(shield, exactPosition, true);
return true;
}
@@ -148,7 +148,7 @@ public static bool ImpactSomethingCallback(ProjectileCE projectile, Thing launch
DamageInfo dinfo = new DamageInfo(projectile.def.projectile.damageDef, (float)damage, 0f, -1f, null, null, null, 0, null, true, true);
shield.BreakShield(dinfo);
}
- projectile.InterceptProjectile(shield, projectile.ExactPosition, true);
+ projectile.InterceptProjectile(shield, projectile.ExactPosition, true);
return true;
}
return false;
diff --git a/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs b/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs
index c81f89d516..3852571d47 100644
--- a/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs
+++ b/Source/CombatExtended/Compatibility/VanillaFurnitureExpandedSecurity.cs
@@ -70,16 +70,16 @@ private static bool CheckCollision(ProjectileCE projectile, IntVec3 cell, Thing
continue;
}
- exactPosition = BlockerRegistry.GetExactPosition(projectile.OriginIV3.ToVector3(),
- exactPosition,
- new Vector3(shield.Position.x, 0, shield.Position.z),
- shield.ShieldRadius * shield.ShieldRadius);
+ exactPosition = BlockerRegistry.GetExactPosition(projectile.OriginIV3.ToVector3(),
+ exactPosition,
+ new Vector3(shield.Position.x, 0, shield.Position.z),
+ shield.ShieldRadius * shield.ShieldRadius);
if (!(projectile is ProjectileCE_Explosive))
{
shield.AbsorbDamage(projectile.def.projectile.GetDamageAmount(launcher), projectile.def.projectile.damageDef, projectile.ExactRotation.eulerAngles.y);
}
- projectile.InterceptProjectile(shield, exactPosition, true);
+ projectile.InterceptProjectile(shield, exactPosition, true);
return true;
}
@@ -93,15 +93,15 @@ private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
refreshShields(map);
- foreach (var building in shields)
- {
- if (building is Building_Shield bs && ShieldInterceptsProjectile(bs, projectile, launcher))
- {
- projectile.InterceptProjectile(bs, exactPosition, true);
- return true;
- }
- }
- return false;
+ foreach (var building in shields)
+ {
+ if (building is Building_Shield bs && ShieldInterceptsProjectile(bs, projectile, launcher))
+ {
+ projectile.InterceptProjectile(bs, exactPosition, true);
+ return true;
+ }
+ }
+ return false;
}
private static bool ShieldInterceptsProjectile(Building building, ProjectileCE projectile, Thing launcher)
diff --git a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
index ed48a3ba18..b7c2ad1920 100644
--- a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
+++ b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
@@ -48,7 +48,7 @@ private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
Effecter eff = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
eff.Trigger(new TargetInfo(projectile.ExactPosition.ToIntVec3(), interceptor.Map, false), TargetInfo.Invalid);
eff.Cleanup();
- projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
+ projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
return true;
}
else
@@ -81,7 +81,7 @@ public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing l
Effecter e = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
e.Trigger(new TargetInfo(newExactPos.ToIntVec3(), interceptor.pawn.Map, false), TargetInfo.Invalid);
e.Cleanup();
- projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
+ projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
}
return result;
@@ -124,7 +124,7 @@ public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing l
Effecter eff = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
eff.Trigger(new TargetInfo(newExactPos.ToIntVec3(), interceptor.pawn.Map, false), TargetInfo.Invalid);
eff.Cleanup();
- projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
+ projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
return true;
}
@@ -174,7 +174,7 @@ public static Vector3[] IntersectionPoint(Vector3 p1, Vector3 p2, Vector3 center
// line does not intersect
return new Vector3[] { Vector3.zero, Vector3.zero };
}
- let sqrtbb4ac = Mathf.Sqrt(bb4ac)
+ var sqrtbb4ac = Mathf.Sqrt(bb4ac);
mu1 = (-b + sqrtbb4ac) / (2 * a);
mu2 = (-b - sqrtbb4ac) / (2 * a);
sect = new Vector3[2];
From 88305ba219b29eb46687476f8382bf540e849207 Mon Sep 17 00:00:00 2001
From: MaxDorob
Date: Tue, 3 Oct 2023 21:15:34 +0600
Subject: [PATCH 047/158] Moved IntersectionPoint to CE_Utility Made LastPos
getter public
---
.../CombatExtended/CE_Utility.cs | 42 ++++++++++++++++++
.../Projectiles/ProjectileCE.cs | 4 +-
.../Compatibility/VanillaExpandedFramework.cs | 5 +--
.../Compatibility/VanillaPsycastExpanded.cs | 43 ++-----------------
4 files changed, 50 insertions(+), 44 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/CE_Utility.cs b/Source/CombatExtended/CombatExtended/CE_Utility.cs
index 77cb2a8a16..113d18393e 100644
--- a/Source/CombatExtended/CombatExtended/CE_Utility.cs
+++ b/Source/CombatExtended/CombatExtended/CE_Utility.cs
@@ -934,6 +934,48 @@ public static bool IntersectLineSphericalOutline(Vector3 center, float radius, V
return false;
}
+ ///
+ /// Calculates whether a line segment intercepts a radius circle. Used to get intersection points.
+ ///
+ /// The first point of the line segment
+ /// The second point of the line segment
+ /// The center of the circle
+ /// The radius of the circle
+ /// Vector3[] { Vector3.zero, Vector3.zero }
if there's no intersection, othrewise returns two intersection points
+ public static Vector3[] IntersectionPoint(Vector3 p1, Vector3 p2, Vector3 center, float radius)
+ {
+ Vector3 dp = new Vector3();
+ Vector3[] sect;
+ float a, b, c;
+ float bb4ac;
+ float mu1;
+ float mu2;
+
+ // get the distance between X and Z on the segment
+ dp.x = p2.x - p1.x;
+ dp.z = p2.z - p1.z;
+ // I don't get the math here
+ a = dp.x * dp.x + dp.z * dp.z;
+ b = 2 * (dp.x * (p1.x - center.x) + dp.z * (p1.z - center.z));
+ c = center.x * center.x + center.z * center.z;
+ c += p1.x * p1.x + p1.z * p1.z;
+ c -= 2 * (center.x * p1.x + center.z * p1.z);
+ c -= radius * radius;
+ bb4ac = b * b - 4 * a * c;
+ if (Mathf.Abs(a) < float.Epsilon || bb4ac < 0)
+ {
+ // line does not intersect
+ return new Vector3[] { Vector3.zero, Vector3.zero };
+ }
+ var sqrtbb4ac = Mathf.Sqrt(bb4ac);
+ mu1 = (-b + sqrtbb4ac) / (2 * a);
+ mu2 = (-b - sqrtbb4ac) / (2 * a);
+ sect = new Vector3[2];
+ sect[0] = new Vector3(p1.x + mu1 * (p2.x - p1.x), 0, p1.z + mu1 * (p2.z - p1.z));
+ sect[1] = new Vector3(p1.x + mu2 * (p2.x - p1.x), 0, p1.z + mu2 * (p2.z - p1.z));
+
+ return sect;
+ }
///
/// Calculates body scale factors based on body type
///
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 649cf8588b..9a3ed83f7d 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -271,9 +271,9 @@ public override Vector3 DrawPos
}
private Vector3 lastExactPos = new Vector3(-1000, 0, 0);
- private Vector3 LastPos
+ public Vector3 LastPos
{
- set
+ private set
{
lastExactPos = value;
}
diff --git a/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs b/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs
index 66d8f7b6cb..193cc8c9f3 100644
--- a/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs
+++ b/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs
@@ -39,7 +39,7 @@ private static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing
return false;
}
var def = projectile.def;
- Vector3 lastExactPos = (Vector3)new Traverse(projectile).Field("lastExactPos").GetValue();//Why this field(or linked property) is private?
+ Vector3 lastExactPos = projectile.LastPos;
var newExactPos = projectile.ExactPosition;
foreach (var interceptor in interceptors)
{
@@ -77,8 +77,7 @@ private static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing
{
continue;
}
- var intersectionPoints = //BlockerRegistry.GetExactPosition(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptorThing.Position.ToVector3(), (radius ) * (radius));
- VanillaPsycastExpanded.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.parent.Position.ToVector3(), (radius));
+ var intersectionPoints = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.parent.Position.ToVector3(), (radius));
if (intersectionPoints == new Vector3[] { Vector3.zero, Vector3.zero })
{
continue;
diff --git a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
index b7c2ad1920..2d6e0bd46f 100644
--- a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
+++ b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
@@ -38,7 +38,7 @@ private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
if (interceptor != null)
{
var hediff = interceptor.health.hediffSet.hediffs.FirstOrDefault(x => x is Hediff_Overshield) as Hediff_Overshield;
- projectile.ExactPosition = IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.DrawPos, hediff.OverlaySize).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
+ projectile.ExactPosition = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.DrawPos, hediff.OverlaySize).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
projectile.landed = true;
new Traverse(interceptor).Field("lastInterceptAngle").SetValue(projectile.ExactPosition.AngleToFlat(interceptor.TrueCenter()));
@@ -64,14 +64,14 @@ public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing l
.Where(x => typeof(Hediff_Overshield).IsAssignableFrom(x.def.hediffClass)).Cast())
{
var def = projectile.def;
- Vector3 lastExactPos = (Vector3)new Traverse(projectile).Field("lastExactPos").GetValue();//Why this field(or linked property) is private?
+ Vector3 lastExactPos = projectile.LastPos;
var newExactPos = projectile.ExactPosition;
if (interceptor.GetType() == typeof(Hediff_Overshield))
{
var result = interceptor.pawn != launcher && (interceptor.pawn.Position == cell || PreventTryColideWithPawn(projectile, interceptor.pawn, newExactPos));
if (result)
{
- projectile.ExactPosition = IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.pawn.DrawPos, interceptor.OverlaySize).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
+ projectile.ExactPosition = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.pawn.DrawPos, interceptor.OverlaySize).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
projectile.landed = true;
new Traverse(interceptor).Field("lastInterceptAngle").SetValue(newExactPos.AngleToFlat(interceptor.pawn.TrueCenter()));
@@ -112,8 +112,7 @@ public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing l
{
return false;
}
- var exactPosition = //BlockerRegistry.GetExactPosition(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptorThing.Position.ToVector3(), (radius ) * (radius));
- IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.pawn.Position.ToVector3(), (radius)).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
+ var exactPosition = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.pawn.Position.ToVector3(), (radius)).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
projectile.ExactPosition = exactPosition;
projectile.landed = true;
@@ -149,40 +148,6 @@ private static bool PreventTryColideWithPawn(ProjectileCE projectile, Thing pawn
return true;
}
- public static Vector3[] IntersectionPoint(Vector3 p1, Vector3 p2, Vector3 center, float radius)
- {
- Vector3 dp = new Vector3();
- Vector3[] sect;
- float a, b, c;
- float bb4ac;
- float mu1;
- float mu2;
-
- // get the distance between X and Z on the segment
- dp.x = p2.x - p1.x;
- dp.z = p2.z - p1.z;
- // I don't get the math here
- a = dp.x * dp.x + dp.z * dp.z;
- b = 2 * (dp.x * (p1.x - center.x) + dp.z * (p1.z - center.z));
- c = center.x * center.x + center.z * center.z;
- c += p1.x * p1.x + p1.z * p1.z;
- c -= 2 * (center.x * p1.x + center.z * p1.z);
- c -= radius * radius;
- bb4ac = b * b - 4 * a * c;
- if (Mathf.Abs(a) < float.Epsilon || bb4ac < 0)
- {
- // line does not intersect
- return new Vector3[] { Vector3.zero, Vector3.zero };
- }
- var sqrtbb4ac = Mathf.Sqrt(bb4ac);
- mu1 = (-b + sqrtbb4ac) / (2 * a);
- mu2 = (-b - sqrtbb4ac) / (2 * a);
- sect = new Vector3[2];
- sect[0] = new Vector3(p1.x + mu1 * (p2.x - p1.x), 0, p1.z + mu1 * (p2.z - p1.z));
- sect[1] = new Vector3(p1.x + mu2 * (p2.x - p1.x), 0, p1.z + mu2 * (p2.z - p1.z));
-
- return sect;
- }
///
/// Copy of ProjectileCE method
///
From c3bd3ed414d4146a0b131f93bc3aad56eb6bc1d9 Mon Sep 17 00:00:00 2001
From: MaxDorob
Date: Tue, 3 Oct 2023 23:19:36 +0600
Subject: [PATCH 048/158] Removed local IntersectLineSphericalOutline merthod
Splited AOE interception check
---
.../Compatibility/VanillaExpandedFramework.cs | 21 +---
.../Compatibility/VanillaPsycastExpanded.cs | 101 ++++++------------
2 files changed, 36 insertions(+), 86 deletions(-)
diff --git a/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs b/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs
index 193cc8c9f3..7437a173c6 100644
--- a/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs
+++ b/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs
@@ -73,7 +73,7 @@ private static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing
{
continue;
}
- if (!IntersectLineSphericalOutline(shieldPosition, radius, lastExactPos, newExactPos))
+ if (!CE_Utility.IntersectLineSphericalOutline(shieldPosition, radius, lastExactPos, newExactPos))
{
continue;
}
@@ -89,25 +89,6 @@ private static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing
}
return false;
}
- ///
- /// Copy of ProjectileCE method
- ///
- private static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
- {
- var pointAInShield = (center - pointA).sqrMagnitude <= Mathf.Pow(radius, 2);
- var pointBInShield = (center - pointB).sqrMagnitude <= Mathf.Pow(radius, 2);
-
- if (pointAInShield && pointBInShield)
- {
- return false;
- }
- if (!pointAInShield && !pointBInShield)
- {
- return false;
- }
-
- return true;
- }
}
diff --git a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
index 2d6e0bd46f..eb76d65d98 100644
--- a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
+++ b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs
@@ -27,7 +27,7 @@ public IEnumerable GetCompatList()
public void Install()
{
BlockerRegistry.RegisterImpactSomethingCallback(ImpactSomething);
- BlockerRegistry.RegisterCheckForCollisionCallback(CheckIntercept);
+ BlockerRegistry.RegisterCheckForCollisionBetweenCallback(AOE_CheckIntercept);
}
private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
@@ -38,17 +38,7 @@ private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
if (interceptor != null)
{
var hediff = interceptor.health.hediffSet.hediffs.FirstOrDefault(x => x is Hediff_Overshield) as Hediff_Overshield;
- projectile.ExactPosition = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.DrawPos, hediff.OverlaySize).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
- projectile.landed = true;
-
- new Traverse(interceptor).Field("lastInterceptAngle").SetValue(projectile.ExactPosition.AngleToFlat(interceptor.TrueCenter()));
- new Traverse(interceptor).Field("lastInterceptTicks").SetValue(Find.TickManager.TicksGame);
- new Traverse(interceptor).Field("drawInterceptCone").SetValue(true);
-
- Effecter eff = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
- eff.Trigger(new TargetInfo(projectile.ExactPosition.ToIntVec3(), interceptor.Map, false), TargetInfo.Invalid);
- eff.Cleanup();
- projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
+ OnIntercepted(hediff, projectile);
return true;
}
else
@@ -56,41 +46,36 @@ private static bool ImpactSomething(ProjectileCE projectile, Thing launcher)
return false;
}
}
-
- public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing launcher)
+ public static bool Hediff_Overshield_InterceptCheck(ProjectileCE projectile, IntVec3 cell, Thing launcher)
{
- foreach (var interceptor in projectile.Map.listerThings.ThingsInGroup(ThingRequestGroup.Pawn).Cast()
+ foreach (var interceptor in projectile.Map.thingGrid.ThingsListAt(cell).OfType()
.SelectMany(x => x.health.hediffSet.hediffs)
- .Where(x => typeof(Hediff_Overshield).IsAssignableFrom(x.def.hediffClass)).Cast())
+ .Where(x => x.GetType() == typeof(Hediff_Overshield)).Cast())
{
+
var def = projectile.def;
Vector3 lastExactPos = projectile.LastPos;
var newExactPos = projectile.ExactPosition;
- if (interceptor.GetType() == typeof(Hediff_Overshield))
+ var result = interceptor.pawn != launcher && (interceptor.pawn.Position == cell || PreventTryColideWithPawn(projectile, interceptor.pawn, newExactPos));
+ if (result)
{
- var result = interceptor.pawn != launcher && (interceptor.pawn.Position == cell || PreventTryColideWithPawn(projectile, interceptor.pawn, newExactPos));
- if (result)
- {
- projectile.ExactPosition = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.pawn.DrawPos, interceptor.OverlaySize).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
- projectile.landed = true;
-
- new Traverse(interceptor).Field("lastInterceptAngle").SetValue(newExactPos.AngleToFlat(interceptor.pawn.TrueCenter()));
- new Traverse(interceptor).Field("lastInterceptTicks").SetValue(Find.TickManager.TicksGame);
- new Traverse(interceptor).Field("drawInterceptCone").SetValue(true);
-
- Effecter e = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
- e.Trigger(new TargetInfo(newExactPos.ToIntVec3(), interceptor.pawn.Map, false), TargetInfo.Invalid);
- e.Cleanup();
- projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
- }
-
+ OnIntercepted(interceptor, projectile);
return result;
}
- #region AOE shields
+ }
+ return false;
+ }
+ public static bool AOE_CheckIntercept(ProjectileCE projectile, Vector3 from, Vector3 newExactPos)
+ {
+ var def = projectile.def;
+ foreach (var interceptor in projectile.Map.listerThings.ThingsInGroup(ThingRequestGroup.Pawn).Cast()
+ .SelectMany(x => x.health.hediffSet.hediffs)
+ .Where(x => x is Hediff_Overshield && x.GetType() != typeof(Hediff_Overshield)).Cast())
+ {
Vector3 shieldPosition = interceptor.pawn.Position.ToVector3ShiftedWithAltitude(0.5f);
float radius = interceptor.OverlaySize;
float blockRadius = radius + def.projectile.SpeedTilesPerTick + 0.1f;
- if ((lastExactPos - shieldPosition).sqrMagnitude < radius * radius)
+ if ((from - shieldPosition).sqrMagnitude < radius * radius)
{
return false;
}
@@ -104,26 +89,16 @@ public static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing l
return false;
}
- if ((shieldPosition - lastExactPos).sqrMagnitude <= Mathf.Pow((float)radius, 2))
+ if ((shieldPosition - from).sqrMagnitude <= Mathf.Pow((float)radius, 2))
{
return false;
}
- if (!IntersectLineSphericalOutline(shieldPosition, radius, lastExactPos, newExactPos))
+ if (!CE_Utility.IntersectLineSphericalOutline(shieldPosition, radius, from, newExactPos))
{
return false;
}
- var exactPosition = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.pawn.Position.ToVector3(), (radius)).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
- projectile.ExactPosition = exactPosition;
- projectile.landed = true;
-
- new Traverse(interceptor).Field("lastInterceptAngle").SetValue(newExactPos.AngleToFlat(interceptor.pawn.TrueCenter()));
- new Traverse(interceptor).Field("lastInterceptTicks").SetValue(Find.TickManager.TicksGame);
- new Traverse(interceptor).Field("drawInterceptCone").SetValue(true);
- Effecter eff = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
- eff.Trigger(new TargetInfo(newExactPos.ToIntVec3(), interceptor.pawn.Map, false), TargetInfo.Invalid);
- eff.Cleanup();
- projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
+ OnIntercepted(interceptor, projectile);
return true;
}
@@ -148,25 +123,19 @@ private static bool PreventTryColideWithPawn(ProjectileCE projectile, Thing pawn
return true;
}
- ///
- /// Copy of ProjectileCE method
- ///
- private static bool IntersectLineSphericalOutline(Vector3 center, float radius, Vector3 pointA, Vector3 pointB)
+ private static void OnIntercepted(Hediff_Overshield interceptor, ProjectileCE projectile)
{
- var pointAInShield = (center - pointA).sqrMagnitude <= Mathf.Pow(radius, 2);
- var pointBInShield = (center - pointB).sqrMagnitude <= Mathf.Pow(radius, 2);
-
- if (pointAInShield && pointBInShield)
- {
- return false;
- }
- if (!pointAInShield && !pointBInShield)
- {
- return false;
- }
-
- return true;
+ var newExactPos = projectile.ExactPosition;
+ var exactPosition = CE_Utility.IntersectionPoint(projectile.OriginIV3.ToVector3(), projectile.ExactPosition, interceptor.pawn.Position.ToVector3(), interceptor.OverlaySize).OrderBy(x => (projectile.OriginIV3.ToVector3() - x).sqrMagnitude).First();
+ projectile.ExactPosition = exactPosition;
+ new Traverse(interceptor).Field("lastInterceptAngle").SetValue(newExactPos.AngleToFlat(interceptor.pawn.TrueCenter()));
+ new Traverse(interceptor).Field("lastInterceptTicks").SetValue(Find.TickManager.TicksGame);
+ new Traverse(interceptor).Field("drawInterceptCone").SetValue(true);
+
+ Effecter eff = new Effecter(EffecterDefOf.Interceptor_BlockedProjectile);
+ eff.Trigger(new TargetInfo(newExactPos.ToIntVec3(), interceptor.pawn.Map, false), TargetInfo.Invalid);
+ eff.Cleanup();
+ projectile.InterceptProjectile(interceptor, projectile.ExactPosition, true);
}
}
}
-#endregion
From 87559b9dcefe9a8dbe18218e20f5bf341f119043 Mon Sep 17 00:00:00 2001
From: MaxDorob
Date: Wed, 4 Oct 2023 00:13:38 +0600
Subject: [PATCH 049/158] Overshield changes
---
.vs/ProjectSettings.json | 3 ++
.vs/VSWorkspaceState.json | 12 +++++
.vs/slnx.sqlite | Bin 0 -> 2297856 bytes
.../CombatExtended/CE_DebugUtility.cs | 6 +++
.../Compatibility/VanillaPsycastExpanded.cs | 17 ++-----
...20\276\320\262\320\260\321\202\321\214.cs" | 42 ++++++++++++++++++
6 files changed, 66 insertions(+), 14 deletions(-)
create mode 100644 .vs/ProjectSettings.json
create mode 100644 .vs/VSWorkspaceState.json
create mode 100644 .vs/slnx.sqlite
create mode 100644 "Source/CombatExtended/Harmony/Harmony_Job - \320\232\320\276\320\277\320\270\321\200\320\276\320\262\320\260\321\202\321\214.cs"
diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json
new file mode 100644
index 0000000000..f8b4888565
--- /dev/null
+++ b/.vs/ProjectSettings.json
@@ -0,0 +1,3 @@
+{
+ "CurrentProjectSetting": null
+}
\ No newline at end of file
diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json
new file mode 100644
index 0000000000..d10ce9a311
--- /dev/null
+++ b/.vs/VSWorkspaceState.json
@@ -0,0 +1,12 @@
+{
+ "ExpandedNodes": [
+ "",
+ "\\Sounds",
+ "\\Source",
+ "\\Source\\CombatExtended",
+ "\\Source\\CombatExtended\\CombatExtended",
+ "\\Source\\CombatExtended\\Harmony"
+ ],
+ "SelectedNode": "\\Source\\CombatExtended.sln",
+ "PreviewInSolutionExplorer": false
+}
\ No newline at end of file
diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..2fd580a89bb7ee02fcc8e9b743ecfdc539927622
GIT binary patch
literal 2297856
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