diff --git a/Defs/ThingDefs_Items/Items_Resource_Ammo.xml b/Defs/ThingDefs_Items/Items_Resource_Ammo.xml index e191e67878..cbcbbac74e 100644 --- a/Defs/ThingDefs_Items/Items_Resource_Ammo.xml +++ b/Defs/ThingDefs_Items/Items_Resource_Ammo.xml @@ -4,7 +4,6 @@ - CombatExtended.AmmoThing Prometheum Military-grade incendiary agent, ignites on contact with oxygen. The raw resource is used to craft incendiary ammo of all kinds. @@ -46,7 +45,6 @@ - CombatExtended.AmmoThing FSX High-explosive chemical extracted from Boomalope sacks and synthesized from chemfuel, it is used in a variety of industrial and military applications. diff --git a/Source/CombatExtended.sln b/Source/CombatExtended.sln index 699530c978..185860b847 100644 --- a/Source/CombatExtended.sln +++ b/Source/CombatExtended.sln @@ -15,6 +15,8 @@ Project("{E9B3A09D-DD32-4429-ABCC-64A2EA58B0B3}") = "ArtilleryCompat", "Artiller EndProject Project("{B55F2CBF-ABFF-4E01-813B-79DAAAC20203}") = "VehiclesCompat", "VehiclesCompat\VehiclesCompat.csproj", "{06833316-A463-4044-BAA7-993683F9810B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SRTSCompat", "SRTSCompat\SRTSCompat.csproj", "{2ECDEC68-5878-41C7-B494-E189B8C5C33E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -58,7 +60,12 @@ Global {06833316-A463-4044-BAA7-993683F9810B}.Debugger|Any CPU.Build.0 = Debug|Any CPU {06833316-A463-4044-BAA7-993683F9810B}.Release|Any CPU.ActiveCfg = Release|Any CPU {06833316-A463-4044-BAA7-993683F9810B}.Release|Any CPU.Build.0 = Release|Any CPU - + {2ECDEC68-5878-41C7-B494-E189B8C5C33E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2ECDEC68-5878-41C7-B494-E189B8C5C33E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2ECDEC68-5878-41C7-B494-E189B8C5C33E}.Debugger|Any CPU.ActiveCfg = Debug|Any CPU + {2ECDEC68-5878-41C7-B494-E189B8C5C33E}.Debugger|Any CPU.Build.0 = Debug|Any CPU + {2ECDEC68-5878-41C7-B494-E189B8C5C33E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2ECDEC68-5878-41C7-B494-E189B8C5C33E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/CombatExtended/Compatibility/SRTS.cs b/Source/CombatExtended/Compatibility/SRTS.cs new file mode 100644 index 0000000000..065e2239bd --- /dev/null +++ b/Source/CombatExtended/Compatibility/SRTS.cs @@ -0,0 +1,35 @@ +using HarmonyLib; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Verse; +using Verse.Sound; +using System.Reflection.Emit; +using System; +using UnityEngine; +using RimWorld; + +namespace CombatExtended.Compatibility +{ + public class SRTS : IPatch + { + public bool CanInstall() + { + Log.Message("Combat Extended :: Checking SRTS Expanded"); + if (!ModLister.HasActiveModWithName("SRTS Expanded")) + { + return false; + } + return true; + } + public IEnumerable GetCompatList() + { + yield return "SRTSCompat"; + } + + public void Install() + { + Log.Message("Combat Extended :: Installing SRTS Expanded"); + } + } +} diff --git a/Source/SRTSCompat/SRTSCompat.csproj b/Source/SRTSCompat/SRTSCompat.csproj new file mode 100644 index 0000000000..0286358d4c --- /dev/null +++ b/Source/SRTSCompat/SRTSCompat.csproj @@ -0,0 +1,58 @@ + + + + net48 + SRTSCompat + 1.1.2.0 + 1.1.2.0 + 1.1.2 + + + CC BY-NC-SA 4.0 2022 + false + + + ..\..\AssembliesCompat\ + portable + true + true + 8.0 + true + + + ..\..\AssembliesCompat\ + true + none + false + 8.0 + true + + + + + + + + + + + False + + + + + ..\..\Assemblies\0CombatExtendedLoader.dll + False + + + + + ..\packages\SRTS-reference.dll + False + + + + + + + diff --git a/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_BomberSkyfaller.cs b/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_BomberSkyfaller.cs new file mode 100644 index 0000000000..73f5079d55 --- /dev/null +++ b/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_BomberSkyfaller.cs @@ -0,0 +1,140 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using Verse; +using CombatExtended.Loader; +using RimWorld; +using System.Collections.Generic; +using HarmonyLib; +using SRTS; +using SPExtended; +using UnityEngine; + +namespace CombatExtended.Compatibility.SRTSCompat +{ + [HarmonyPatch(typeof(BomberSkyfaller), + "DropBomb", + new Type[] { })] + [HarmonyPriority(Priority.First)] + public static class Harmony_BomberSkyfaller_DropBomb + { + private static MethodInfo GetCurrentTargetingRadiusInfo = AccessTools.DeclaredMethod( + typeof(BomberSkyfaller), "GetCurrentTargetingRadius", new Type[] { }); + + private static float shotHeight = 5f; + + // A destructive prefix so as to force SRTS bombers to use our projectile system, + // unless the bomb isn't an AmmoThing, if so, fall back to SRTS's bombing system. + public static bool Prefix(BomberSkyfaller __instance) + { + ActiveDropPod srts = (ActiveDropPod)__instance.innerContainer.First(); + if (srts == null) + { + goto FunctionEnd; + } + + for (int i = 0; i < (__instance.bombType == BombingType.precise + ? __instance.precisionBombingNumBombs : 1); ++i) + { + Thing bombStack = srts.Contents.innerContainer + .FirstOrDefault(thing => SRTSMod.mod.settings.allowedBombs.Contains(thing.def.defName)); + if (bombStack == null) + { + goto FunctionEnd; + } + + Thing bombThing = srts.Contents.innerContainer.Take(bombStack, 1); + + IntVec3 bombPos = __instance.bombCells[0]; + if (__instance.bombType == BombingType.carpet) + { + __instance.bombCells.RemoveAt(0); + } + + object targetingRadiusNullable = GetCurrentTargetingRadiusInfo.Invoke(__instance, null); + if (targetingRadiusNullable == null) + { + Log.Error("Combat Extended :: SRTSCompat BomberSkyfaller.DropBomb() - " + + "could not get SRTS dropship's targeting radius"); + throw new Exception(); + } + float targetingRadius = (float)(int)targetingRadiusNullable; + + if (bombThing is AmmoThing) + { + // Combat Extended projectile system + ThingDef bombProjectileThingDef = (bombThing.def as AmmoDef)?.AmmoSetDefs + .Find(ammoSet => ammoSet.ammoTypes?.Any() ?? false)? + .ammoTypes + .FirstOrDefault(ammoLink => ammoLink.ammo == bombThing.def)? + .projectile; + if (bombProjectileThingDef == null) + { + Log.Error("Combat Extended :: SRTSCompat Harmony_BomberSkyfaller_DropBomb Prefix - " + + $"AmmoDef {bombThing.def} doesn't have a projectile"); + throw new Exception(); + } + + ProjectilePropertiesCE bombPropsCE = bombProjectileThingDef.projectile as ProjectilePropertiesCE; + if (bombPropsCE == null) + { + Log.Error("Combat Extended :: SRTSCompat Harmony_BomberSkyfaller_DropBomb Prefix - " + + $"AmmoDef {bombThing.def} projectile doesn't have ProjectilePropsCE"); + throw new Exception(); + } + + ProjectileCE bombProjectileCE = ThingMaker.MakeThing(bombProjectileThingDef) as ProjectileCE; + + GenSpawn.Spawn(bombProjectileCE, __instance.DrawPosCell, __instance.Map); + bombProjectileCE.canTargetSelf = false; + bombProjectileCE.minCollisionDistance = 1; + bombProjectileCE.intendedTarget = null; + bombProjectileCE.AccuracyFactor = 1f; + + float freefallTime = Mathf.Sqrt(2 * shotHeight / bombPropsCE.Gravity); + float maxShotSpeed = targetingRadius / freefallTime; + + bombProjectileCE.Launch(launcher: __instance, + origin: __instance.DrawPosCell.ToIntVec2.ToVector2(), + shotAngle: 0f, + shotRotation: Rand.Range(-180f, 180f), + shotHeight: shotHeight, + shotSpeed: Rand.Range(0f, maxShotSpeed), + equipment: __instance); + // Adjust shot speed after because of + // this.shotSpeed = Math.Max(shotSpeed, def.projectile.speed); + bombProjectileCE.shotSpeed = Rand.Range(0f, maxShotSpeed); + } + else + { + // Original SRTS bomb dropping system + int timerTickExplode = 20 + Rand.Range(0, 5); + + FallingBomb fallingBombThing = new FallingBomb(bombThing, + bombThing.TryGetComp(), + __instance.Map, + __instance.def.skyfaller.shadow); + fallingBombThing.HitPoints = int.MaxValue; + fallingBombThing.ticksRemaining = timerTickExplode; + + IntVec3 targetCell = GenRadial.RadialCellsAround(bombPos, targetingRadius, true) + .Where(cell => cell.InBounds(__instance.Map)) + .RandomElementByWeight(cell => 1f - Mathf.Min(cell.DistanceTo(__instance.Position) / targetingRadius, 1f) + 0.05f); + + fallingBombThing.angle = __instance.angle + (SPTrig.LeftRightOfLine(__instance.DrawPosCell, __instance.Position, targetCell) * -10); + fallingBombThing.speed = (float)SPExtra.Distance(__instance.DrawPosCell, targetCell) / fallingBombThing.ticksRemaining; + Thing t = GenSpawn.Spawn(fallingBombThing, targetCell, __instance.Map); + GenExplosion.NotifyNearbyPawnsOfDangerousExplosive(t, bombThing.TryGetComp().Props.explosiveDamageType, null); + } + } + + FunctionEnd: + if (__instance.bombType == BombingType.precise && __instance.bombCells.Any()) + { + __instance.bombCells.Clear(); + } + return false; + } + } +} diff --git a/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_DialogSettings.cs b/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_DialogSettings.cs new file mode 100644 index 0000000000..465a8be226 --- /dev/null +++ b/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_DialogSettings.cs @@ -0,0 +1,134 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using Verse; +using CombatExtended.Loader; +using RimWorld; +using System.Collections.Generic; +using HarmonyLib; +using SRTS; +using UnityEngine; + +namespace CombatExtended.Compatibility.SRTSCompat +{ + [HarmonyPatch(typeof(Dialog_AllowedBombs), + nameof(Dialog_AllowedBombs.DoWindowContents), + new Type[] { typeof(Rect) })] + public static class Harmony_Dialog_AllowedBombs_DoWindowContents_Rect + { + static FieldInfo explosivesStringInfo = AccessTools.DeclaredField(typeof(Dialog_AllowedBombs), + "explosivesString"); + public static List SearchForExplosives(Dialog_AllowedBombs __instance) + { + // You think this is bad? Imagine how it'd be as a single line + return DefDatabase.AllDefs + .Where(thingDef => !SRTSMod.mod.settings.allowedBombs.Contains(thingDef.defName) + && thingDef.building is null + && !thingDef.IsWeapon + && !thingDef.IsApparel + && thingDef.projectile == null + && 0 <= CultureInfo.CurrentCulture.CompareInfo.IndexOf(thingDef.defName, + explosivesStringInfo.GetValue(__instance) as string, + CompareOptions.IgnoreCase) + && (thingDef.GetCompProperties() != null + || ((thingDef as AmmoDef)?.AmmoSetDefs? + .Any(ammoSetDef => ammoSetDef.ammoTypes? + .Any(ammoLink => ammoLink.projectile.GetCompProperties() != null + || ammoLink.projectile.projectile?.explosionRadius > 0.5f) + ?? false) + ?? false) + ) + ) + .ToList(); + } + + /// Replaces + // if (SRTSHelper.CEModLoaded) + // { + // explosivesSearched = DefDatabase.AllDefs + // .Where(x => x.HasComp(Type.GetType("CombatExtended.CompExplosiveCE,CombatExtended")) + // && !SRTSMod.mod.settings.allowedBombs.Contains(x.defName) + // && CultureInfo.CurrentCulture.CompareInfo.IndexOf(x.defName, explosivesString, CompareOptions.IgnoreCase) >= 0) + // .ToList(); + // } + // else + // { + // explosivesSearched = DefDatabase.AllDefs + // .Where(x => x.GetCompProperties() != null + // && x.building is null + // && !SRTSMod.mod.settings.allowedBombs.Contains(x.defName) + // && CultureInfo.CurrentCulture.CompareInfo.IndexOf(x.defName, explosivesString, CompareOptions.IgnoreCase) >= 0) + // .ToList(); + // } + /// With + // explosivesSearched = Harmony_Dialog_AllowedBombs_DoWindowContents_Rect.SearchForExplosives(this); + static IEnumerable Transpiler(IEnumerable instructions, + ILGenerator il) + { + bool found = false; + + FieldInfo explosivesChangedField = AccessTools.DeclaredField(typeof(Dialog_AllowedBombs), + "explosivesChanged"); + CodeInstruction callIns = CodeInstruction.Call( + typeof(Harmony_Dialog_AllowedBombs_DoWindowContents_Rect), + "SearchForExplosives", + new Type[] { typeof(Dialog_AllowedBombs) }); + + List codes = instructions.ToList(); + for (int i = 0; i < codes.Count(); ++i) + { + if (!found && i > 1 + && codes[i - 2].opcode == OpCodes.Ldc_I4_0 + && codes[i - 1].StoresField(explosivesChangedField)) + { + found = true; + + if (callIns == null) + { + Log.Error("Combat Extended :: SRTSCompat Dialog_AllowedBombs.DoWindowContents(Rect) - " + + "tried patching, but no function to patch with"); + goto SkipTranspile; + } + + Label? skipToLabel = null; + for (int j = i; j >= 0; --j) + { + if (codes[j].Branches(out skipToLabel)) + { + break; + } + } + if (skipToLabel == null) + { + Log.Error("Combat Extended :: SRTSCompat Dialog_AllowedBombs.DoWindowContents(Rect) - " + + "didn't find any branch to labels before the target; did we even find the target?"); + goto SkipTranspile; + } + + yield return new CodeInstruction(OpCodes.Ldarg_0); + yield return new CodeInstruction(OpCodes.Ldarg_0); + yield return callIns; + yield return CodeInstruction.StoreField(typeof(Dialog_AllowedBombs), "explosivesSearched"); + + while (i < codes.Count() && !codes[i].labels.Any(label => label == skipToLabel)) + { + ++i; + } + if (codes.Count() == i) + { + Log.Error("Combat Extended :: SRTSCompat Dialog_AllowedBombs.DoWindowContents(Rect) - " + + "we skipped over the entire code without finding the skip label. " + + "If you're reading this, know that your bombs selection menu is now screwed :)"); + } + } + + SkipTranspile: + yield return codes[i]; + } + } + } + +} diff --git a/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_FallingBomb.cs b/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_FallingBomb.cs new file mode 100644 index 0000000000..997c4838cf --- /dev/null +++ b/Source/SRTSCompat/SRTSCompat/Harmony/Harmony_FallingBomb.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using Verse; +using CombatExtended.Loader; +using RimWorld; +using System.Collections.Generic; +using HarmonyLib; +using SRTS; +using UnityEngine; + +namespace CombatExtended.Compatibility.SRTSCompat +{ + [HarmonyPatch(typeof(FallingBomb), + MethodType.Constructor, + new Type[] { typeof(Thing), typeof(CompExplosive), typeof(Map), typeof(string) })] + public static class Harmony_FallingBomb_Thing_CompExplosive_Map_string + { + // Adds an additional null check for def.projectileWhenLoaded of + // this.projectile = def.projectileWhenLoaded.projectile; + static IEnumerable Transpiler(IEnumerable instructions, + ILGenerator il) + { + bool found = false; + Label projectileDoesntExist = il.DefineLabel(); + + FieldInfo thingDefProjectileField = AccessTools.DeclaredField(typeof(ThingDef), + "projectile"); + + List codes = instructions.ToList(); + for (int i = 0; i < codes.Count(); ++i) + { + if (!found && codes[i].LoadsField(thingDefProjectileField)) + { + found = true; + + yield return new CodeInstruction(OpCodes.Dup); + yield return new CodeInstruction(OpCodes.Brfalse_S, projectileDoesntExist); + yield return codes[i]; + + ++i; + codes[i].labels.Add(projectileDoesntExist); + } + + yield return codes[i]; + } + } + } +} diff --git a/Source/SRTSCompat/SRTSCompat/SRTSCompat.cs b/Source/SRTSCompat/SRTSCompat/SRTSCompat.cs new file mode 100644 index 0000000000..e509b77528 --- /dev/null +++ b/Source/SRTSCompat/SRTSCompat/SRTSCompat.cs @@ -0,0 +1,37 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using Verse; +using CombatExtended.Loader; +using RimWorld; +using System.Collections.Generic; +using HarmonyLib; +using SRTS; +using UnityEngine; + + +namespace CombatExtended.Compatibility.SRTSCompat +{ + [StaticConstructorOnStartup] + public class SRTSCompat : IModPart + { + private static Harmony harmony; + + public Type GetSettingsType() + { + return null; + } + + public IEnumerable GetCompatList() + { + yield break; + } + + public void PostLoad(ModContentPack content, ISettingsCE _) + { + harmony = new Harmony("CombatExtended.Compatibility.SRTSCompat"); + harmony.PatchAll(Assembly.GetExecutingAssembly()); + } + } +} diff --git a/Source/packages/SRTS-reference.dll b/Source/packages/SRTS-reference.dll new file mode 100644 index 0000000000..7d0b364775 Binary files /dev/null and b/Source/packages/SRTS-reference.dll differ diff --git a/SupportedThirdPartyMods.md b/SupportedThirdPartyMods.md index 601d7e5654..425c17a72f 100644 --- a/SupportedThirdPartyMods.md +++ b/SupportedThirdPartyMods.md @@ -412,6 +412,7 @@ Soviet Armory | Space Worms (Continued) | Spartan Foundry | Spidercamp's Horses | +SRTS Expanded | Stalingrad – Uniforms | Steamworld Uniforms | Star Crafters Armory | @@ -510,4 +511,4 @@ Xenohumans - Anthromorphs | Xenohumans Expanded | Xenoorca Race | Yet Another Prosthetic Expansion Mod - Core | -Zombieland | \ No newline at end of file +Zombieland |