diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 04f2729..62e7030 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml index 6eb3a16..2c15269 100644 --- a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml +++ b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml @@ -415,7 +415,7 @@
  • - Verb_Shoot + ArachnaeSwarm.Verb_ShootWithOffset true false 0.8 @@ -457,6 +457,13 @@
  • + +
  • + +
  • (0, -1)
  • + + +
    ARA_RW_Toxic_Needle_SG @@ -493,7 +500,7 @@
  • - ArachnaeSwarm.Verb_ShootShotgun + ArachnaeSwarm.Verb_ShootShotgunWithOffset true false 0.8 @@ -535,6 +542,13 @@
  • + +
  • + +
  • (0, -1)
  • + + +
    ARA_Bullet_SniperCannon @@ -587,7 +601,7 @@
  • - Verb_Shoot + ArachnaeSwarm.Verb_ShootWithOffset true ARA_Bullet_SniperCannon 2.5 @@ -632,6 +646,13 @@
  • + +
  • + +
  • (0, -1)
  • + + +
    ARA_Bullet_Rail @@ -693,7 +714,7 @@
  • - Verb_Shoot + ArachnaeSwarm.Verb_ShootWithOffset true ARA_Bullet_Rail 2.5 @@ -734,6 +755,13 @@
  • + +
  • + +
  • (0, -1)
  • + + +
    @@ -1150,7 +1178,7 @@
  • - ArachnaeSwarm.Verb_ShootShotgun + ArachnaeSwarm.Verb_ShootShotgunWithOffset true false 3 @@ -1193,6 +1221,13 @@
  • + +
  • + +
  • (0, -1)
  • + + +
    Bullet_RW_Missile_HG_Gun @@ -1275,13 +1310,13 @@
  • - Verb_Shoot + ArachnaeSwarm.Verb_ShootWithOffset true false - 4.5 + 2.8 Bullet_RW_Missile_AR_Gun 38 - 12 + 10 4 SpitterSpit @@ -1319,6 +1354,13 @@
  • + +
  • + +
  • (0, -1.4)
  • + + +
    Bullet_RW_Missile_AR_Gun diff --git a/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml b/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml index c5e2336..f7c4662 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_SwarmTurret.xml @@ -141,7 +141,7 @@
  • 600000 - true + false 寿命 这种半植物生命的寿命转瞬即逝。
  • @@ -357,6 +357,9 @@ 0.9 -20 + + 50 +
  • Flame @@ -506,6 +509,9 @@ 0.9 -20 + + 50 +
  • Flame diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 61d33ac..1409dca 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -233,6 +233,7 @@ + diff --git a/Source/ArachnaeSwarm/Thing_Comps/CompAndPatch_GiveHediffOnShot.cs b/Source/ArachnaeSwarm/Thing_Comps/CompAndPatch_GiveHediffOnShot.cs index a824251..8761afe 100644 --- a/Source/ArachnaeSwarm/Thing_Comps/CompAndPatch_GiveHediffOnShot.cs +++ b/Source/ArachnaeSwarm/Thing_Comps/CompAndPatch_GiveHediffOnShot.cs @@ -4,7 +4,6 @@ using Verse; namespace ArachnaeSwarm { - // 1. 定义CompProperties来存储我们的数据 public class CompProperties_GiveHediffOnShot : CompProperties { public HediffDef hediffDef; @@ -16,60 +15,68 @@ namespace ArachnaeSwarm } } - // 2. 创建一个简单的Comp来挂载到武器上,它只负责持有数据 public class CompGiveHediffOnShot : ThingComp { public CompProperties_GiveHediffOnShot Props => (CompProperties_GiveHediffOnShot)props; } - // 3. 创建Harmony补丁 - // 补丁目标为 Verb_LaunchProjectile.TryCastShot。这是所有发射弹丸动作的通用入口。 + // Patch 1: For all standard projectile verbs. [HarmonyPatch(typeof(Verb_LaunchProjectile), "TryCastShot")] - public static class Patch_Verb_Shoot_TryCastShot + public static class Patch_Verb_LaunchProjectile_TryCastShot { - // 使用[HarmonyPostfix]特性来创建一个在原方法执行后运行的补丁 - // 添加一个bool类型的返回值`__result`,它代表了原方法的返回值 - public static void Postfix(Verb_Shoot __instance, bool __result) + public static void Postfix(Verb_LaunchProjectile __instance, bool __result) { - // __result 是原方法 TryCastShot 的返回值。 - // 如果 __result 为 false,意味着射击动作因某些原因(如目标无效、没有弹药)失败了,我们就不应该添加Hediff。 - if (!__result) - { - return; - } + if (!__result) return; + if (__instance.CasterPawn == null || __instance.EquipmentSource == null) return; - // __instance是原方法的实例对象,也就是那个Verb_Shoot - // 检查这个Verb是否来源于一个装备(武器) - if (__instance.EquipmentSource == null || __instance.CasterPawn == null) - { - return; - } - - // 尝试从武器上获取我们自定义的Comp CompGiveHediffOnShot comp = __instance.EquipmentSource.GetComp(); - if (comp == null) - { - return; - } - - // 检查XML中是否配置了hediffDef - if (comp.Props.hediffDef == null) - { - Log.ErrorOnce($"[ArachnaeSwarm] CompGiveHediffOnShot on {__instance.EquipmentSource.def.defName} has null hediffDef.", __instance.EquipmentSource.def.GetHashCode()); - return; - } + if (comp == null || comp.Props.hediffDef == null) return; - // 为射击者(CasterPawn)添加或增加Hediff的严重性 Hediff hediff = __instance.CasterPawn.health.GetOrAddHediff(comp.Props.hediffDef); hediff.Severity += comp.Props.severityToAdd; - // 检查Hediff是否带有HediffComp_Disappears组件 - HediffComp_Disappears disappearsComp = hediff.TryGetComp(); - if (disappearsComp != null) - { - // 如果有,则调用正确的方法重置它的消失计时器 - disappearsComp.ResetElapsedTicks(); - } + var disappearsComp = hediff.TryGetComp(); + disappearsComp?.ResetElapsedTicks(); + } + } + + // Patch 2: Specifically for Verb_ShootWithOffset. + [HarmonyPatch(typeof(Verb_ShootWithOffset), "TryCastShot")] + public static class Patch_Verb_ShootWithOffset_TryCastShot + { + public static void Postfix(Verb_ShootWithOffset __instance, bool __result) + { + if (!__result) return; + if (__instance.CasterPawn == null || __instance.EquipmentSource == null) return; + + CompGiveHediffOnShot comp = __instance.EquipmentSource.GetComp(); + if (comp == null || comp.Props.hediffDef == null) return; + + Hediff hediff = __instance.CasterPawn.health.GetOrAddHediff(comp.Props.hediffDef); + hediff.Severity += comp.Props.severityToAdd; + + var disappearsComp = hediff.TryGetComp(); + disappearsComp?.ResetElapsedTicks(); + } + } + + // Patch 3: Specifically for our new Verb_ShootShotgunWithOffset. + [HarmonyPatch(typeof(Verb_ShootShotgunWithOffset), "TryCastShot")] + public static class Patch_Verb_ShootShotgunWithOffset_TryCastShot + { + public static void Postfix(Verb_ShootShotgunWithOffset __instance, bool __result) + { + if (!__result) return; + if (__instance.CasterPawn == null || __instance.EquipmentSource == null) return; + + CompGiveHediffOnShot comp = __instance.EquipmentSource.GetComp(); + if (comp == null || comp.Props.hediffDef == null) return; + + Hediff hediff = __instance.CasterPawn.health.GetOrAddHediff(comp.Props.hediffDef); + hediff.Severity += comp.Props.severityToAdd; + + var disappearsComp = hediff.TryGetComp(); + disappearsComp?.ResetElapsedTicks(); } } } \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Verbs/Verb_ShootShotgunWithOffset.cs b/Source/ArachnaeSwarm/Verbs/Verb_ShootShotgunWithOffset.cs new file mode 100644 index 0000000..8e77edb --- /dev/null +++ b/Source/ArachnaeSwarm/Verbs/Verb_ShootShotgunWithOffset.cs @@ -0,0 +1,158 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Verse; + +namespace ArachnaeSwarm +{ + public class Verb_ShootShotgunWithOffset : Verb_Shoot + { + protected override bool TryCastShot() + { + // Fire the first shot + bool initialShotSuccess = this.BaseTryCastShot(0); + if (initialShotSuccess && CasterIsPawn) + { + CasterPawn.records.Increment(RecordDefOf.ShotsFired); + } + + // Get shotgun extension + ShotgunExtension shotgunExtension = ShotgunExtension.Get(this.verbProps.defaultProjectile); + if (initialShotSuccess && shotgunExtension != null && shotgunExtension.pelletCount > 1) + { + // Fire the rest of the pellets in a loop + for (int i = 1; i < shotgunExtension.pelletCount; i++) + { + this.BaseTryCastShot(i); + } + } + return initialShotSuccess; + } + + protected bool BaseTryCastShot(int pelletIndex) + { + if (currentTarget.HasThing && currentTarget.Thing.Map != caster.Map) + { + return false; + } + + ThingDef projectile = Projectile; + if (projectile == null) + { + return false; + } + + ShootLine resultingLine; + bool flag = TryFindShootLineFromTo(caster.Position, currentTarget, out resultingLine); + if (verbProps.stopBurstWithoutLos && !flag) + { + return false; + } + + if (base.EquipmentSource != null) + { + base.EquipmentSource.GetComp()?.Notify_ProjectileLaunched(); + base.EquipmentSource.GetComp()?.UsedOnce(); + } + + lastShotTick = Find.TickManager.TicksGame; + Thing manningPawn = caster; + Thing equipmentSource = base.EquipmentSource; + CompMannable compMannable = caster.TryGetComp(); + if (compMannable?.ManningPawn != null) + { + manningPawn = compMannable.ManningPawn; + equipmentSource = caster; + } + + Vector3 drawPos = caster.DrawPos; + drawPos = ApplyProjectileOffset(drawPos, equipmentSource, pelletIndex); + Projectile projectile2 = (Projectile)GenSpawn.Spawn(projectile, resultingLine.Source, caster.Map); + if (equipmentSource.TryGetComp(out CompUniqueWeapon comp)) + { + foreach (WeaponTraitDef item in comp.TraitsListForReading) + { + if (item.damageDefOverride != null) + { + projectile2.damageDefOverride = item.damageDefOverride; + } + + if (!item.extraDamages.NullOrEmpty()) + { + if (projectile2.extraDamages == null) + { + projectile2.extraDamages = new List(); + } + projectile2.extraDamages.AddRange(item.extraDamages); + } + } + } + + if (verbProps.ForcedMissRadius > 0.5f) + { + float num = verbProps.ForcedMissRadius; + if (manningPawn is Pawn pawn) + { + num *= verbProps.GetForceMissFactorFor(equipmentSource, pawn); + } + + float num2 = VerbUtility.CalculateAdjustedForcedMiss(num, currentTarget.Cell - caster.Position); + if (num2 > 0.5f) + { + IntVec3 forcedMissTarget = GetForcedMissTarget(num2); + if (forcedMissTarget != currentTarget.Cell) + { + projectile2.Launch(manningPawn, drawPos, forcedMissTarget, currentTarget, ProjectileHitFlags.All, preventFriendlyFire, equipmentSource); + return true; + } + } + } + + ShotReport shotReport = ShotReport.HitReportFor(caster, this, currentTarget); + if (verbProps.canGoWild && !Rand.Chance(shotReport.AimOnTargetChance_IgnoringPosture)) + { + bool flyOverhead = projectile2?.def?.projectile != null && projectile2.def.projectile.flyOverhead; + resultingLine.ChangeDestToMissWild(shotReport.AimOnTargetChance_StandardTarget, flyOverhead, caster.Map); + projectile2.Launch(manningPawn, drawPos, resultingLine.Dest, currentTarget, ProjectileHitFlags.NonTargetWorld, preventFriendlyFire, equipmentSource, shotReport.GetRandomCoverToMissInto()?.def); + return true; + } + + if (currentTarget.Thing != null && currentTarget.Thing.def.CanBenefitFromCover && !Rand.Chance(shotReport.PassCoverChance)) + { + projectile2.Launch(manningPawn, drawPos, shotReport.GetRandomCoverToMissInto(), currentTarget, ProjectileHitFlags.NonTargetWorld, preventFriendlyFire, equipmentSource, shotReport.GetRandomCoverToMissInto()?.def); + return true; + } + + projectile2.Launch(manningPawn, drawPos, currentTarget, currentTarget, ProjectileHitFlags.IntendedTarget, preventFriendlyFire, equipmentSource, shotReport.GetRandomCoverToMissInto()?.def); + return true; + } + + private Vector3 ApplyProjectileOffset(Vector3 originalDrawPos, Thing equipmentSource, int pelletIndex) + { + if (equipmentSource != null) + { + ModExtension_ShootWithOffset offsetExtension = (base.EquipmentSource?.def)?.GetModExtension(); + + if (offsetExtension != null && offsetExtension.offsets != null && offsetExtension.offsets.Count > 0) + { + Vector2 offset = offsetExtension.GetOffsetFor(pelletIndex); + + Vector3 targetPos = currentTarget.CenterVector3; + Vector3 casterPos = caster.DrawPos; + float rimworldAngle = targetPos.AngleToFlat(casterPos); + + float correctedAngle = -rimworldAngle - 90f; + + Vector2 rotatedOffset = offset.RotatedBy(correctedAngle); + + originalDrawPos += new Vector3(rotatedOffset.x, 0f, rotatedOffset.y); + } + } + return originalDrawPos; + } + } +} \ No newline at end of file