diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index dd6f7824..5993a73f 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/1.6/1.6/Defs/Effects/Mote_Wula.xml b/1.6/1.6/Defs/Effects/Mote_Wula.xml index 34d67e77..b6b77e23 100644 --- a/1.6/1.6/Defs/Effects/Mote_Wula.xml +++ b/1.6/1.6/Defs/Effects/Mote_Wula.xml @@ -28,4 +28,28 @@ + + + Mote_WULA_RW_Penetrating_Beam + MoteDualAttached + MoteOverhead + + 0.1 + 0.1 + 0.1 + True + True + + true + + Things/Projectile/ChargeLanceShot + Graphic_MoteWithAgeSecs + MoteBeam + + <_ScrollSpeedA>0 + <_ScrollSpeedB>0 + <_Intensity>2 + + + \ No newline at end of file diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_CruiseMissile.xml b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_CruiseMissile.xml deleted file mode 100644 index 1a6ef9d4..00000000 --- a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_CruiseMissile.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - Weapon_ExampleCruiseMissileLauncher - - A heavy launcher designed for cruise missiles, capable of long-range precision strikes and area bombardment. - Spacer - - Things/Item/Weapon/Launcher - Graphic_Single - - 1 - Interact_ChargeRifle - -
  • LongShots
  • -
  • RangedHeavy
  • -
    - - -
  • WULA_Cube_Productor_BIO
  • -
  • WULA_Cube_Productor_Energy
  • -
    - WULA_Synth_Weapon_Technology - UnfinishedWeapon -
    - - 2500 - 6.0 - 0.3 - 0.5 - 0.6 - 0.7 - 4.0 - - - 180 - 10 - 40 - 15 - - -
  • - Verb_LaunchProjectile - true - Bullet_ExampleCruiseMissile - 3.0 - 60 - 1 - Shot_ChargeLance - GunTail_Heavy - 15 - - true - - false -
  • -
    - -
  • Wula_Weapon_Init
  • -
  • CruiseMissileLauncher
  • -
    - -
  • RewardStandardQualitySuper
  • -
    -
    - - - Bullet_ExampleCruiseMissile - - - Things/Projectile/Bullet_Big - Graphic_Single - - WulaFallenEmpire.Projectile_CruiseMissile - - 30 - Bomb - 20 - 2.5 - 0.8 - Explosion_Bomb - Impact_Metal - true - 1.0 - 4.0 - Filth_Rubble - 1.0 - 5 - 120 - 1.5 - true - 1.0 - 8 - 0.7 - true - true - -
  • - Bomb - 25 - 3.5 - Explosion_Bomb - true - 5 - 2.0 - 15 - 7.0 - Bomb - Explosion_Bomb - WULA_GunTail_Smoke - 0.05 - 15 - - 180 - 300 - - 0.001 - - 10 - 20 - - 1.5 -
  • -
    -
    -
    -
    \ No newline at end of file diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_Homing.xml b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_Homing.xml index 60360de1..0fdfeb62 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_Homing.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_Homing.xml @@ -6,8 +6,9 @@ A rifle designed to fire homing projectiles. It can track targets and adjust its trajectory in real-time. Spacer - Things/Item/Weapon/Rifle + Wula/Weapon/WULA_RW_Plasm_AR Graphic_Single + 1.4 1 Interact_Rifle @@ -45,7 +46,7 @@ 1.0 35 1 - Shot_ChargeRifle + Shot_ChargeRifle GunTail_Medium 9 @@ -63,7 +64,7 @@ Bullet_ExampleHoming - Things/Projectile/Bullet_Big + Things/Projectile/Bullet_Big Graphic_Single WulaFallenEmpire.Projectile_Homing @@ -78,9 +79,6 @@ false 0.5 0.5 - Filth_Rubble - 0.5 - 1 0 0.5 false @@ -89,22 +87,22 @@ 0.1 true true - -
  • - 10 - 0.05 - 60 - 1.5 - 0.8 - Bullet_ExampleHoming - 0.05 - - 20 - 40 - - WULA_GunTail_Smoke -
  • -
    + +
  • + 10 + 0.05 + 60 + 1.5 + 0.8 + Bullet_ExampleHoming + 0.05 + + 20 + 40 + + WULA_GunTail_Smoke +
  • +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_HomingExplosive.xml b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_HomingExplosive.xml index b9314b46..153f8455 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_HomingExplosive.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon_Homing_Examples_Bullet_HomingExplosive.xml @@ -6,8 +6,9 @@ A powerful launcher that fires homing projectiles with explosive payloads. Ideal for area denial and heavy damage. Spacer - Things/Item/Weapon/Launcher + Wula/Weapon/WULA_RW_Plasm_AR Graphic_Single + 1.4 0.8 Interact_ChargeRifle @@ -79,10 +80,6 @@ true 0.8 3.0 - Filth_Rubble - 1.0 - 3 - 60 1.0 true 1.0 @@ -90,34 +87,22 @@ 0.5 true true - -
  • - 5 - 0.03 - 90 - 2.0 - 0.7 - Bullet_ExampleHomingExplosive - 0.03 - - 20 - 30 - - WULA_GunTail_Smoke -
  • -
  • - 30 - 5 - 3 - Bullet_ShotgunPellet - 5 - - 1 - 3 - - true -
  • -
    + +
  • + 5 + 0.03 + 90 + 2.0 + 0.7 + Bullet_ExampleHomingExplosive + 0.03 + + 20 + 30 + + WULA_GunTail_Smoke +
  • +
    \ No newline at end of file diff --git a/1.6/Anomaly/Defs/EventDefs/EventDef_WULA_FE_Spiritualist.xml b/1.6/Anomaly/Defs/EventDefs/EventDef_WULA_FE_Spiritualist.xml index 4392bb17..a3f0a9e8 100644 --- a/1.6/Anomaly/Defs/EventDefs/EventDef_WULA_FE_Spiritualist.xml +++ b/1.6/Anomaly/Defs/EventDefs/EventDef_WULA_FE_Spiritualist.xml @@ -302,7 +302,7 @@ - Wula_UI_FE_Spiritualist_10 + Wula_UI_FE_Spiritualist_11 Wula/Events/Portraits/WULA_FE_Spiritualist_1 帝国修女 diff --git a/Source/WulaFallenEmpire/HomingProjectileDef.cs b/Source/WulaFallenEmpire/HomingProjectileDef.cs index 65ba2ff1..69a279d1 100644 --- a/Source/WulaFallenEmpire/HomingProjectileDef.cs +++ b/Source/WulaFallenEmpire/HomingProjectileDef.cs @@ -5,21 +5,8 @@ namespace WulaFallenEmpire { public class HomingProjectileDef : DefModExtension { - public float SpeedChangeTilesPerTickOverride - { - get - { - return this.speedChangePerTick / 100f; - } - } - - public FloatRange SpeedRangeTilesPerTickOverride - { - get - { - return this.speedRangeOverride.Value * 0.01f; - } - } + public float SpeedChangeTilesPerTickOverride; + public FloatRange SpeedRangeTilesPerTickOverride; public float hitChance = 0.5f; @@ -27,7 +14,7 @@ namespace WulaFallenEmpire public float initRotateAngle = 30f; - public float proximityFuseRange = 0f; + public float proximityFuseRange = 0.5f; // 调整默认值,使其在接近目标时能正确触发引信 public IntRange destroyTicksAfterLosingTrack = new IntRange(60, 120); @@ -37,5 +24,12 @@ namespace WulaFallenEmpire public FloatRange? speedRangeOverride; public FleckDef tailFleckDef; + // 拖尾特效的详细配置参数 + public int fleckMakeFleckTickMax = 1; + public IntRange fleckMakeFleckNum = new IntRange(1, 1); + public FloatRange fleckAngle = new FloatRange(-180f, 180f); + public FloatRange fleckScale = new FloatRange(1f, 1f); + public FloatRange fleckSpeed = new FloatRange(0f, 0f); + public FloatRange fleckRotation = new FloatRange(-180f, 180f); } } \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Projectile_Homing.cs b/Source/WulaFallenEmpire/Projectile_Homing.cs index 93f38c22..9243a528 100644 --- a/Source/WulaFallenEmpire/Projectile_Homing.cs +++ b/Source/WulaFallenEmpire/Projectile_Homing.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Reflection; using RimWorld; @@ -9,317 +8,319 @@ using Verse.Sound; namespace WulaFallenEmpire { - public class Projectile_Homing : Bullet - { - public HomingProjectileDef HomingDef - { - get - { - bool flag = this.homingDefInt == null; - if (flag) - { - this.homingDefInt = this.def.GetModExtension(); - } - return this.homingDefInt; - } - } + public class Projectile_Homing : Bullet + { + private HomingProjectileDef homingDefInt; - public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null) - { - bool flag = false; - bool flag2 = usedTarget.HasThing && usedTarget.Thing is IAttackTarget; - if (flag2) - { - bool flag3 = Rand.Chance(this.GetHitChance(usedTarget.Thing)); - if (flag3) - { - hitFlags |= ProjectileHitFlags.IntendedTarget; - intendedTarget = usedTarget; - flag = true; - } - } - else - { - bool flag4 = Rand.Chance(this.GetHitChance(intendedTarget.Thing)); - if (flag4) - { - hitFlags |= ProjectileHitFlags.IntendedTarget; - usedTarget = intendedTarget; - flag = true; - } - } - bool flag5 = flag; - if (flag5) - { - hitFlags &= ~ProjectileHitFlags.IntendedTarget; - } - base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef); - this.exactPositionInt = origin.Yto0() + Vector3.up * this.def.Altitude; - Vector3 normalized = (this.destination - origin).Yto0().normalized; - float degrees = Rand.Range(-this.HomingDef.initRotateAngle, this.HomingDef.initRotateAngle); - Vector2 vector = new Vector2(normalized.x, normalized.z); - vector = vector.RotatedBy(degrees); - Vector3 a = new Vector3(vector.x, 0f, vector.y); - bool flag6 = this.HomingDef.speedRangeOverride == null; - if (flag6) - { - this.curSpeed = a * this.def.projectile.SpeedTilesPerTick; - } - else - { - this.curSpeed = a * this.HomingDef.SpeedRangeTilesPerTickOverride.RandomInRange; - } - this.ticksToImpact = int.MaxValue; - this.lifetime = int.MaxValue; - this.ReflectInit(); - } + private Sustainer ambientSustainer; - protected void ReflectInit() - { - bool flag = !this.def.projectile.soundAmbient.NullOrUndefined(); - if (flag) - { - this.ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this); - } - this.comps = (List)NonPublicFields.ThingWithComps_comps.GetValue(this); - } + private List comps; - public float GetHitChance(Thing thing) - { - float num = this.HomingDef.hitChance; - bool flag = thing == null; - float result; - if (flag) - { - result = num; - } - else - { - Pawn pawn = thing as Pawn; - bool flag2 = pawn != null; - if (flag2) - { - num *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f); - bool flag3 = pawn.GetPosture() > PawnPosture.Standing; - if (flag3) - { - num *= 0.5f; - } - float num2 = 1f; - switch (this.equipmentQuality) - { - case QualityCategory.Awful: - num2 = 0.5f; - goto IL_DD; - case QualityCategory.Poor: - num2 = 0.75f; - goto IL_DD; - case QualityCategory.Normal: - num2 = 1f; - goto IL_DD; - case QualityCategory.Excellent: - num2 = 1.1f; - goto IL_DD; - case QualityCategory.Masterwork: - num2 = 1.2f; - goto IL_DD; - case QualityCategory.Legendary: - num2 = 1.3f; - goto IL_DD; - } - Log.Message("Unknown QualityCategory, returning default qualityFactor = 1"); - IL_DD: - num *= num2; - } - else - { - num *= 1.5f * thing.def.fillPercent; - } - result = Mathf.Clamp(num, 0f, 1f); - } - return result; - } + protected Vector3 exactPositionInt; - public override Vector3 ExactPosition - { - get - { - return this.exactPositionInt; - } - } + public Vector3 curSpeed; - public override Quaternion ExactRotation - { - get - { - return Quaternion.LookRotation(this.curSpeed); - } - } + public bool homing = true; + private int Fleck_MakeFleckTick; // 拖尾特效的计时器 + private Vector3 lastTickPosition; // 记录上一帧的位置,用于计算移动方向 - public virtual void MovementTick() - { - Vector3 vect = this.ExactPosition + this.curSpeed; - ShootLine shootLine = new ShootLine(this.ExactPosition.ToIntVec3(), vect.ToIntVec3()); - Vector3 vector = (this.intendedTarget.Cell.ToVector3() - this.ExactPosition).Yto0(); - bool flag = this.homing; - if (flag) - { - Vector3 a = vector.normalized - this.curSpeed.normalized; - bool flag2 = a.sqrMagnitude >= 1.414f; - if (flag2) - { - this.homing = false; - this.lifetime = this.HomingDef.destroyTicksAfterLosingTrack.RandomInRange; - this.ticksToImpact = this.lifetime; - base.HitFlags &= ~ProjectileHitFlags.IntendedTarget; - base.HitFlags |= ProjectileHitFlags.NonTargetPawns; - base.HitFlags |= ProjectileHitFlags.NonTargetWorld; - } - else - { - this.curSpeed += a * this.HomingDef.homingSpeed * this.curSpeed.magnitude; - } - } - foreach (IntVec3 b in shootLine.Points()) - { - bool flag3 = (this.intendedTarget.Cell - b).SqrMagnitude <= this.HomingDef.proximityFuseRange * this.HomingDef.proximityFuseRange; - if (flag3) - { - this.homing = false; - this.lifetime = this.HomingDef.destroyTicksAfterLosingTrack.RandomInRange; - bool flag4 = (base.HitFlags & ProjectileHitFlags.IntendedTarget) == ProjectileHitFlags.IntendedTarget || this.HomingDef.proximityFuseRange > 0f; - if (flag4) - { - this.lifetime = 0; - this.ticksToImpact = 0; - vect = b.ToVector3(); - bool flag5 = Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null; - if (flag5) - { - this.def.projectile.soundImpactAnticipate.PlayOneShot(this); - } - } - } - } - this.exactPositionInt = vect; - this.curSpeed *= (this.curSpeed.magnitude + this.HomingDef.SpeedChangeTilesPerTickOverride) / this.curSpeed.magnitude; - } + private static class NonPublicFields + { + public static FieldInfo Projectile_AmbientSustainer = typeof(Projectile).GetField("ambientSustainer", BindingFlags.Instance | BindingFlags.NonPublic); + public static FieldInfo ThingWithComps_comps = typeof(ThingWithComps).GetField("comps", BindingFlags.Instance | BindingFlags.NonPublic); + public static MethodInfo ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic); + } - protected override void Tick() - { - this.ThingWithCompsTick(); - this.lifetime--; - if (this.HomingDef.tailFleckDef != null) - { - FleckMaker.Static(this.ExactPosition, base.Map, this.HomingDef.tailFleckDef, 1f); - } - bool landed = this.landed; - if (!landed) - { - Vector3 exactPosition = this.ExactPosition; - this.ticksToImpact--; - this.MovementTick(); - bool flag = !this.ExactPosition.InBounds(base.Map); - if (flag) - { - base.Position = exactPosition.ToIntVec3(); - this.Destroy(DestroyMode.Vanish); - } - else - { - Vector3 exactPosition2 = this.ExactPosition; - object[] parameters = new object[] - { - exactPosition, - exactPosition2 - }; - bool flag2 = (bool)Projectile_Homing.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters); - if (!flag2) - { - base.Position = this.ExactPosition.ToIntVec3(); - bool flag3 = this.ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null; - if (flag3) - { - this.def.projectile.soundImpactAnticipate.PlayOneShot(this); - } - bool flag4 = this.ticksToImpact <= 0; - if (flag4) - { - this.ImpactSomething(); - } - else - { - bool flag5 = this.ambientSustainer != null; - if (flag5) - { - this.ambientSustainer.Maintain(); - } - } - } - } - } - } + public HomingProjectileDef HomingDef + { + get + { + if (homingDefInt == null) + { + homingDefInt = def.GetModExtension(); + if (homingDefInt == null) + { + Log.ErrorOnce($"HomingProjectileDef for {this.def.defName} is null. Creating a default instance.", this.thingIDNumber ^ 0x12345678); + this.homingDefInt = new HomingProjectileDef(); + } + } + return homingDefInt; + } + } - private void ThingWithCompsTick() - { - bool flag = this.comps != null; - if (flag) - { - int i = 0; - int count = this.comps.Count; - while (i < count) - { - this.comps[i].CompTick(); - i++; - } - } - } + public override Vector3 ExactPosition => exactPositionInt; - protected override void Impact(Thing hitThing, bool blockedByShield = false) - { - Map map = base.Map; - IntVec3 position = base.Position; - base.Impact(hitThing, blockedByShield); - bool flag = this.HomingDef.extraProjectile != null; - if (flag) - { - bool flag2 = hitThing != null && hitThing.Spawned; - if (flag2) - { - ((Projectile)GenSpawn.Spawn(this.HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(this.launcher, this.ExactPosition, hitThing, hitThing, ProjectileHitFlags.All, false, null, null); - } - else - { - ((Projectile)GenSpawn.Spawn(this.HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(this.launcher, this.ExactPosition, position, position, ProjectileHitFlags.All, false, null, null); - } - } - } + public override Quaternion ExactRotation => Quaternion.LookRotation(curSpeed); - public override void ExposeData() - { - base.ExposeData(); - Scribe_Values.Look(ref this.exactPositionInt, "exactPosition", default(Vector3), false); - Scribe_Values.Look(ref this.curSpeed, "curSpeed", default(Vector3), false); - Scribe_Values.Look(ref this.homing, "homing", false, false); - bool flag = Scribe.mode == LoadSaveMode.PostLoadInit; - if (flag) - { - this.ReflectInit(); - } - } + public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null) + { + bool flag = false; + if (usedTarget.HasThing && usedTarget.Thing is IAttackTarget) + { + if (Rand.Chance(GetHitChance(usedTarget.Thing))) + { + hitFlags |= ProjectileHitFlags.IntendedTarget; + intendedTarget = usedTarget; + flag = true; + } + } + else if (Rand.Chance(GetHitChance(intendedTarget.Thing))) + { + hitFlags |= ProjectileHitFlags.IntendedTarget; + usedTarget = intendedTarget; + flag = true; + } + if (flag) + { + hitFlags &= ~ProjectileHitFlags.IntendedTarget; + } + base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef); + exactPositionInt = origin.Yto0() + Vector3.up * def.Altitude; + lastTickPosition = origin; // 初始化 lastTickPosition + Vector3 normalized = (destination - origin).Yto0().normalized; + float degrees = Rand.Range(0f - HomingDef.initRotateAngle, HomingDef.initRotateAngle); + Vector2 v = new Vector2(normalized.x, normalized.z); + v = v.RotatedBy(degrees); + Vector3 vector = new Vector3(v.x, 0f, v.y); + // 检查 HomingDef.speedRangeOverride 是否有值 + if (!HomingDef.speedRangeOverride.HasValue) + { + curSpeed = vector * def.projectile.SpeedTilesPerTick; + } + else + { + curSpeed = vector * HomingDef.SpeedRangeTilesPerTickOverride.RandomInRange; + } + ticksToImpact = int.MaxValue; + lifetime = int.MaxValue; + ReflectInit(); + } - private HomingProjectileDef homingDefInt; + protected void ReflectInit() + { + if (!def.projectile.soundAmbient.NullOrUndefined()) + { + ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this); + } + comps = (List)NonPublicFields.ThingWithComps_comps.GetValue(this); + } - private Sustainer ambientSustainer; + public float GetHitChance(Thing thing) + { + if (this.HomingDef == null) + { + Log.ErrorOnce("HomingDef is null for projectile " + this.def.defName + ". Returning default hitChance.", this.thingIDNumber ^ 0x12345678); + return 0.7f; + } - private List comps; + float hitChance = HomingDef.hitChance; + if (thing == null) + { + return hitChance; + } + if (thing is Pawn pawn) + { + hitChance *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f); + if (pawn.GetPosture() != 0) + { + hitChance *= 0.5f; + } + float num = 1f; + switch (equipmentQuality) + { + case QualityCategory.Awful: + num = 0.5f; + break; + case QualityCategory.Poor: + num = 0.75f; + break; + case QualityCategory.Normal: + num = 1f; + break; + case QualityCategory.Excellent: + num = 1.1f; + break; + case QualityCategory.Masterwork: + num = 1.2f; + break; + case QualityCategory.Legendary: + num = 1.3f; + break; + default: + Log.Message("Unknown QualityCategory, returning default qualityFactor = 1"); + break; + } + hitChance *= num; + } + else + { + hitChance *= 1.5f * thing.def.fillPercent; + } + return Mathf.Clamp(hitChance, 0f, 1f); + } - protected Vector3 exactPositionInt; + public virtual void MovementTick() + { + Vector3 vect = ExactPosition + curSpeed; + ShootLine shootLine = new ShootLine(ExactPosition.ToIntVec3(), vect.ToIntVec3()); + Vector3 vector = (intendedTarget.Cell.ToVector3() - ExactPosition).Yto0(); + if (homing) + { + Vector3 vector2 = vector.normalized - curSpeed.normalized; + if (vector2.sqrMagnitude >= 1.414f) + { + homing = false; + lifetime = HomingDef.destroyTicksAfterLosingTrack.RandomInRange; + ticksToImpact = lifetime; + base.HitFlags &= ~ProjectileHitFlags.IntendedTarget; + base.HitFlags |= ProjectileHitFlags.NonTargetPawns; + base.HitFlags |= ProjectileHitFlags.NonTargetWorld; + } + else + { + curSpeed += vector2 * HomingDef.homingSpeed * curSpeed.magnitude; + } + } + foreach (IntVec3 item in shootLine.Points()) + { + if (!((intendedTarget.Cell - item).SqrMagnitude <= HomingDef.proximityFuseRange * HomingDef.proximityFuseRange)) + { + continue; + } + homing = false; + lifetime = HomingDef.destroyTicksAfterLosingTrack.RandomInRange; + if ((base.HitFlags & ProjectileHitFlags.IntendedTarget) == ProjectileHitFlags.IntendedTarget || HomingDef.proximityFuseRange > 0f) + { + lifetime = 0; + ticksToImpact = 0; + vect = item.ToVector3(); + if (Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && def.projectile.soundImpactAnticipate != null) + { + def.projectile.soundImpactAnticipate.PlayOneShot(this); + } + } + } + exactPositionInt = vect; + curSpeed *= (curSpeed.magnitude + HomingDef.SpeedChangeTilesPerTickOverride) / curSpeed.magnitude; + } - public Vector3 curSpeed; + protected override void Tick() + { + ThingWithCompsTick(); + lifetime--; + + // 处理拖尾特效 + if (HomingDef != null && HomingDef.tailFleckDef != null) + { + Fleck_MakeFleckTick++; + if (Fleck_MakeFleckTick >= HomingDef.fleckMakeFleckTickMax) + { + Fleck_MakeFleckTick = 0; + Map map = base.Map; + int randomInRange = HomingDef.fleckMakeFleckNum.RandomInRange; + Vector3 currentPosition = ExactPosition; + Vector3 previousPosition = lastTickPosition; - public bool homing = true; + for (int i = 0; i < randomInRange; i++) + { + float num = (currentPosition - previousPosition).AngleFlat(); + float velocityAngle = HomingDef.fleckAngle.RandomInRange + num; + float randomInRange2 = HomingDef.fleckScale.RandomInRange; + float randomInRange3 = HomingDef.fleckSpeed.RandomInRange; + + FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, HomingDef.tailFleckDef, randomInRange2); + dataStatic.rotation = (currentPosition - previousPosition).AngleFlat(); + dataStatic.rotationRate = HomingDef.fleckRotation.RandomInRange; + dataStatic.velocityAngle = velocityAngle; + dataStatic.velocitySpeed = randomInRange3; + map.flecks.CreateFleck(dataStatic); + } + } + } + lastTickPosition = ExactPosition; // 更新上一帧位置 - private static MethodInfo ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic); - } + if (landed) + { + return; + } + Vector3 exactPosition = ExactPosition; + ticksToImpact--; + MovementTick(); + if (!ExactPosition.InBounds(base.Map)) + { + base.Position = exactPosition.ToIntVec3(); + Destroy(); + return; + } + Vector3 exactPosition2 = ExactPosition; + object[] parameters = new object[2] { exactPosition, exactPosition2 }; + if (!(bool)NonPublicFields.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters)) + { + base.Position = ExactPosition.ToIntVec3(); + if (ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && def.projectile.soundImpactAnticipate != null) + { + def.projectile.soundImpactAnticipate.PlayOneShot(this); + } + if (ticksToImpact <= 0) + { + ImpactSomething(); + } + else if (ambientSustainer != null) + { + ambientSustainer.Maintain(); + } + } + } + + private void ThingWithCompsTick() + { + if (comps != null) + { + int i = 0; + for (int count = comps.Count; i < count; i++) + { + comps[i].CompTick(); + } + } + } + + protected override void Impact(Thing hitThing, bool blockedByShield = false) + { + Map map = base.Map; + IntVec3 position = base.Position; + base.Impact(hitThing, blockedByShield); + if (HomingDef.extraProjectile != null) + { + if (hitThing != null && hitThing.Spawned) + { + ((Projectile)GenSpawn.Spawn(HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(launcher, ExactPosition, hitThing, hitThing, ProjectileHitFlags.All, false, null, null); + } + else + { + ((Projectile)GenSpawn.Spawn(HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(launcher, ExactPosition, position, position, ProjectileHitFlags.All, false, null, null); + } + } + } + + public override void ExposeData() + { + base.ExposeData(); + Scribe_Values.Look(ref exactPositionInt, "exactPosition"); + Scribe_Values.Look(ref curSpeed, "curSpeed"); + Scribe_Values.Look(ref homing, "homing", defaultValue: false); + if (Scribe.mode == LoadSaveMode.PostLoadInit) + { + ReflectInit(); + if (this.homingDefInt == null) + { + this.homingDefInt = this.def.GetModExtension(); + if (this.homingDefInt == null) + { + Log.ErrorOnce($"HomingProjectileDef is null for projectile {this.def.defName} after PostLoadInit. Creating a default instance.", this.thingIDNumber ^ 0x12345678); + this.homingDefInt = new HomingProjectileDef(); + } + } + } + } + } } \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Projectile_WulaBeam.cs b/Source/WulaFallenEmpire/Projectile_WulaBeam.cs index ff6d1fd8..b8d22d11 100644 --- a/Source/WulaFallenEmpire/Projectile_WulaBeam.cs +++ b/Source/WulaFallenEmpire/Projectile_WulaBeam.cs @@ -84,19 +84,32 @@ namespace WulaFallenEmpire { if (!infinitePenetration && hitCounter >= maxHits) break; - if (thing is Pawn pawn && pawn != launcher && !alreadyDamaged.Contains(pawn)) + // 统一处理 Pawn 和 Building 的伤害逻辑 + // 确保 Thing 未被伤害过且不是发射者 + if (thing != launcher && !alreadyDamaged.Contains(thing)) { bool shouldDamage = false; - if (intendedTarget.Thing == pawn) shouldDamage = true; - else if (pawn.HostileTo(launcher)) shouldDamage = true; - else if (!shouldPreventFriendlyFire) shouldDamage = true; + Pawn pawn = thing as Pawn; + Building building = thing as Building; + + if (pawn != null) // 如果是 Pawn + { + if (intendedTarget.Thing == pawn) shouldDamage = true; + else if (pawn.HostileTo(launcher)) shouldDamage = true; + else if (!shouldPreventFriendlyFire) shouldDamage = true; + } + else if (building != null) // 如果是 Building + { + shouldDamage = true; // 默认对 Building 造成伤害 + } if (shouldDamage) { - ApplyPathDamage(pawn, props); + ApplyPathDamage(thing, props); // 传递 Thing } } - else if (thing.def.Fillage == FillCategory.Full && thing.def.blockLight) + // 只有当遇到完全阻挡的 Thing 且不是 Pawn 或 Building 时才停止穿透 + else if (thing.def.Fillage == FillCategory.Full && thing.def.blockLight && !(thing is Pawn) && !(thing is Building)) { break; } @@ -105,9 +118,15 @@ namespace WulaFallenEmpire this.Destroy(DestroyMode.Vanish); } - private void ApplyPathDamage(Pawn pawn, Wula_BeamPierce_Extension props) + private void ApplyPathDamage(Thing targetThing, Wula_BeamPierce_Extension props) // 接受 Thing 参数 { - float damageMultiplier = Mathf.Pow(1f - props.damageFalloff, hitCounter); + float damageMultiplier = 1f; + if (targetThing is Pawn) // 只有 Pawn 才计算穿透衰减 + { + damageMultiplier = Mathf.Pow(1f - props.damageFalloff, hitCounter); + } + // Building 不受穿透衰减影响,或者 Building 的穿透衰减始终为 1 (不衰减) + int damageAmount = (int)(this.DamageAmount * damageMultiplier); if (damageAmount <= 0) return; @@ -123,9 +142,13 @@ namespace WulaFallenEmpire DamageInfo.SourceCategory.ThingOrUnknown, this.intendedTarget.Thing); - pawn.TakeDamage(dinfo); - alreadyDamaged.Add(pawn); - hitCounter++; + targetThing.TakeDamage(dinfo); // 对 targetThing 造成伤害 + alreadyDamaged.Add(targetThing); + + if (targetThing is Pawn) // 只有 Pawn 才增加 hitCounter + { + hitCounter++; + } } protected override void Tick() { } diff --git a/Source/WulaFallenEmpire/Projectile_WulaPenetrating.cs b/Source/WulaFallenEmpire/Projectile_WulaPenetrating.cs index 2f3b1df4..ed182f5e 100644 --- a/Source/WulaFallenEmpire/Projectile_WulaPenetrating.cs +++ b/Source/WulaFallenEmpire/Projectile_WulaPenetrating.cs @@ -17,7 +17,7 @@ namespace WulaFallenEmpire public FleckDef tailFleckDef; // 用于配置拖尾特效的 FleckDef } - public class Projectile_WulaLineAttack : Projectile + public class Projectile_WulaLineAttack : Bullet { private int hitCounter = 0; private List alreadyDamaged = new List(); diff --git a/Source/WulaFallenEmpire/Verb/Trackingbullet.cs b/Source/WulaFallenEmpire/Verb/Trackingbullet.cs index f49445ca..a74d6794 100644 --- a/Source/WulaFallenEmpire/Verb/Trackingbullet.cs +++ b/Source/WulaFallenEmpire/Verb/Trackingbullet.cs @@ -1,8 +1,6 @@ using RimWorld; using System.Collections.Generic; -using Verse.Sound; using System.Linq; -using System.Reflection; using UnityEngine; using Verse; @@ -22,150 +20,74 @@ namespace WulaFallenEmpire public float subExplosionSpread = 6f; public DamageDef subDamageDef; public SoundDef subSoundExplode; - public FleckDef tailFleckDef; // 用于配置拖尾特效的 FleckDef - public float homingSpeed = 0.1f; - public float initRotateAngle = 30f; - public IntRange destroyTicksAfterLosingTrack = new IntRange(60, 120); - public float speedChangePerTick; - public FloatRange? speedRangeOverride; - public float proximityFuseRange = 0f; + + // 新增的弹道配置参数 + public float bezierArcHeightFactor = 0.05f; // 贝塞尔曲线高度因子 + public float bezierMinArcHeight = 2f; // 贝塞尔曲线最小高度 + public float bezierMaxArcHeight = 6f; // 贝塞尔曲线最大高度 + public float bezierHorizontalOffsetFactor = 0.1f; // 贝塞尔曲线水平偏移因子 + public float bezierSideOffsetFactor = 0.2f; // 贝塞尔曲线侧向偏移因子 + public float bezierRandomOffsetScale = 0.5f; // 贝塞尔曲线随机偏移缩放 + } public class Projectile_CruiseMissile : Projectile_Explosive { private CruiseMissileProperties settings; - protected Vector3 exactPositionInt; - public Vector3 curSpeed; - public bool homing = true; - private Sustainer ambientSustainer; - private List comps; - private int ticksToDestroy = -1; - - // Launch 方法的参数作为字段 - - // 拖尾特效相关字段 - private int Fleck_MakeFleckTick; - public int Fleck_MakeFleckTickMax = 1; - public IntRange Fleck_MakeFleckNum = new IntRange(1, 1); - public FloatRange Fleck_Angle = new FloatRange(-180f, 180f); - public FloatRange Fleck_Scale = new FloatRange(1f, 1f); - public FloatRange Fleck_Speed = new FloatRange(0f, 0f); - public FloatRange Fleck_Rotation = new FloatRange(-180f, 180f); + private bool flag2; + private Vector3 Randdd; + private Vector3 position2; + public Vector3 ExPos; public override void SpawnSetup(Map map, bool respawningAfterLoad) { base.SpawnSetup(map, respawningAfterLoad); settings = def.GetModExtension() ?? new CruiseMissileProperties(); - this.ReflectInit(); } - public override void Launch(Thing launcherParam, Vector3 originParam, LocalTargetInfo usedTargetParam, LocalTargetInfo intendedTargetParam, ProjectileHitFlags hitFlagsParam, bool preventFriendlyFireParam = false, Thing equipmentParam = null, ThingDef targetCoverDefParam = null) + private void RandFactor() { - this.launcher = launcherParam; - this.origin = originParam; - this.usedTarget = usedTargetParam; - this.intendedTarget = intendedTargetParam; - this.HitFlags = hitFlagsParam; - this.preventFriendlyFire = preventFriendlyFireParam; - this.equipment = equipmentParam; - this.targetCoverDef = targetCoverDefParam; - - this.exactPositionInt = origin.Yto0() + Vector3.up * this.def.Altitude; - Vector3 normalized = (this.destination - origin).Yto0().normalized; - float degrees = Rand.Range(-this.settings.initRotateAngle, this.settings.initRotateAngle); - Vector2 vector = new Vector2(normalized.x, normalized.z); - vector = vector.RotatedBy(degrees); - Vector3 a = new Vector3(vector.x, 0f, vector.y); - bool flag6 = this.settings.speedRangeOverride == null; - if (flag6) - { - this.curSpeed = a * this.def.projectile.SpeedTilesPerTick; - } - else - { - this.curSpeed = a * this.settings.speedRangeOverride.Value.RandomInRange; - } - this.ticksToImpact = int.MaxValue; - this.lifetime = int.MaxValue; + // 调整随机范围,用于控制C形弹道的随机偏移 + Randdd = new Vector3( + Rand.Range(-settings.bezierRandomOffsetScale, settings.bezierRandomOffsetScale), // X轴的随机偏移 + Rand.Range(0f, 0f), // Y轴(高度)不进行随机,保持平稳 + Rand.Range(-settings.bezierRandomOffsetScale, settings.bezierRandomOffsetScale) // Z轴的随机偏移 + ); + flag2 = true; } - protected void ReflectInit() + public Vector3 BPos(float t) { - if (NonPublicFields.Projectile_AmbientSustainer == null) - { - NonPublicFields.Projectile_AmbientSustainer = typeof(Projectile).GetField("ambientSustainer", BindingFlags.Instance | BindingFlags.NonPublic); - } - if (NonPublicFields.ThingWithComps_comps == null) - { - NonPublicFields.ThingWithComps_comps = typeof(ThingWithComps).GetField("comps", BindingFlags.Instance | BindingFlags.NonPublic); - } - if (NonPublicFields.ProjectileCheckForFreeInterceptBetween == null) - { - NonPublicFields.ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic); - } + if (!flag2) RandFactor(); - bool flag = !this.def.projectile.soundAmbient.NullOrUndefined(); - if (flag) - { - this.ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this); - } - this.comps = (List)NonPublicFields.ThingWithComps_comps.GetValue(this); + // 计算水平距离 + float horizontalDistance = Vector3.Distance(new Vector3(origin.x, 0, origin.z), + new Vector3(destination.x, 0, destination.z)); + + // 动态调整控制点高度,使其更扁平,使用XML配置的高度因子 + float arcHeight = Mathf.Clamp(horizontalDistance * settings.bezierArcHeightFactor, settings.bezierMinArcHeight, settings.bezierMaxArcHeight); + + // 计算从起点到终点的方向向量 + Vector3 direction = (destination - origin).normalized; + // 计算垂直于方向向量的水平向量(用于侧向偏移),确保C形弯曲方向一致 + Vector3 perpendicularDirection = Vector3.Cross(direction, Vector3.up).normalized; + + // 调整控制点以形成扁平 C 形,使用XML配置的偏移因子 + // P1: 在起点附近,向前偏移,向上偏移,并向一侧偏移 + Vector3 p1 = origin + direction * horizontalDistance * settings.bezierHorizontalOffsetFactor + Vector3.up * arcHeight + perpendicularDirection * horizontalDistance * settings.bezierSideOffsetFactor + Randdd; + // P2: 在终点附近,向后偏移,向上偏移,并向同一侧偏移 + Vector3 p2 = destination - direction * horizontalDistance * settings.bezierHorizontalOffsetFactor + Vector3.up * arcHeight + perpendicularDirection * horizontalDistance * settings.bezierSideOffsetFactor + Randdd; + + return BezierCurve(origin, p1, p2, destination, t); } - public float GetHitChance(Thing thing) + private Vector3 BezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { - float num = this.settings.homingSpeed; - bool flag = thing == null; - float result; - if (flag) - { - result = num; - } - else - { - Pawn pawn = thing as Pawn; - bool flag2 = pawn != null; - if (flag2) - { - num *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f); - bool flag3 = pawn.GetPosture() > PawnPosture.Standing; - if (flag3) - { - num *= 0.5f; - } - float num2 = 1f; - switch (this.equipmentQuality) - { - case QualityCategory.Awful: - num2 = 0.5f; - goto IL_DD; - case QualityCategory.Poor: - num2 = 0.75f; - goto IL_DD; - case QualityCategory.Normal: - num2 = 1f; - goto IL_DD; - case QualityCategory.Excellent: - num2 = 1.1f; - goto IL_DD; - case QualityCategory.Masterwork: - num2 = 1.2f; - goto IL_DD; - case QualityCategory.Legendary: - num2 = 1.3f; - goto IL_DD; - } - Log.Message("Unknown QualityCategory, returning default qualityFactor = 1"); - IL_DD: - num *= num2; - } - else - { - num *= 1.5f * thing.def.fillPercent; - } - result = Mathf.Clamp(num, 0f, 1f); - } - return result; + float u = 1 - t; + return u * u * u * p0 + + 3 * u * u * t * p1 + + 3 * u * t * t * p2 + + t * t * t * p3; } private IEnumerable GetValidCells(Map map) @@ -230,149 +152,43 @@ namespace WulaFallenEmpire ); } - public override Quaternion ExactRotation + protected override void DrawAt(Vector3 position, bool flip = false) { - get - { - return Quaternion.LookRotation(this.curSpeed); - } - } - public override Vector3 ExactPosition - { - get - { - return this.exactPositionInt; - } + position2 = BPos(DistanceCoveredFraction - 0.01f); + ExPos = position = BPos(DistanceCoveredFraction); + base.DrawAt(position, flip); } protected override void Tick() { - this.ThingWithCompsTick(); - this.lifetime--; - if (this.settings.tailFleckDef != null) + if (intendedTarget.Thing is Pawn pawn && pawn.Spawned && !pawn.Destroyed) { - this.Fleck_MakeFleckTick++; - if (this.Fleck_MakeFleckTick >= this.Fleck_MakeFleckTickMax) + if ((pawn.Dead || pawn.Downed) && DistanceCoveredFraction < 0.6f) { - this.Fleck_MakeFleckTick = 0; - for (int i = 0; i < this.Fleck_MakeFleckNum.RandomInRange; i++) - { - FleckMaker.Static(this.ExactPosition + Gen.RandomHorizontalVector(this.Fleck_Scale.RandomInRange / 2f), base.Map, this.settings.tailFleckDef, this.Fleck_Scale.RandomInRange); - } - } - } - - bool landed = this.landed; - if (!landed) - { - Vector3 exactPosition = this.ExactPosition; - this.ticksToImpact--; - this.MovementTick(); - bool flag = !this.ExactPosition.InBounds(base.Map); - if (flag) - { - base.Position = exactPosition.ToIntVec3(); - this.Destroy(DestroyMode.Vanish); - } - else - { - Vector3 exactPosition2 = this.ExactPosition; - object[] parameters = new object[] - { - exactPosition, - exactPosition2 - }; - bool flag2 = (bool)NonPublicFields.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters); - if (!flag2) - { - base.Position = this.ExactPosition.ToIntVec3(); - bool flag3 = this.ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null; - if (flag3) - { - this.def.projectile.soundImpactAnticipate.PlayOneShot(this); - } - bool flag4 = this.ticksToImpact <= 0; - if (flag4) - { - this.Impact(null); - } - else - { - bool flag5 = this.ambientSustainer != null; - if (flag5) - { - this.ambientSustainer.Maintain(); - } - } - } + FindNextTarget(pawn.DrawPos); } + destination = pawn.DrawPos; } + base.Tick(); } - private void MovementTick() + private void FindNextTarget(Vector3 center) { - if (this.homing) + var map = base.Map; + if (map == null) return; + + foreach (IntVec3 cell in GenRadial.RadialCellsAround(IntVec3.FromVector3(center), 7f, true)) { - if (this.intendedTarget != null && this.intendedTarget.Thing != null) + if (!cell.InBounds(map)) continue; + + Pawn target = cell.GetFirstPawn(map); + if (target != null && target.Faction.HostileTo(launcher?.Faction)) { - Vector3 vector = (this.intendedTarget.Thing.DrawPos - this.exactPositionInt).normalized; - this.curSpeed = Vector3.RotateTowards(this.curSpeed, vector * this.curSpeed.magnitude, this.settings.homingSpeed, 0f); - } - else if (this.ticksToDestroy == -1) - { - this.ticksToDestroy = this.settings.destroyTicksAfterLosingTrack.RandomInRange; - } - } - if (this.ticksToDestroy > 0) - { - this.ticksToDestroy--; - if (this.ticksToDestroy == 0) - { - this.Destroy(DestroyMode.Vanish); + intendedTarget = target; return; } } - if (this.settings.speedChangePerTick != 0f) - { - this.curSpeed = this.curSpeed.normalized * (this.curSpeed.magnitude + this.settings.speedChangePerTick); - } - if (this.settings.proximityFuseRange > 0f) - { - if (this.intendedTarget != null && this.intendedTarget.Thing != null && (this.intendedTarget.Thing.DrawPos - this.exactPositionInt).magnitude < this.settings.proximityFuseRange) - { - this.Impact(null); - return; - } - } - - this.exactPositionInt += this.curSpeed; + intendedTarget = CellRect.CenteredOn(IntVec3.FromVector3(center), 7).RandomCell; } - - protected void ThingWithCompsTick() - { - if (this.comps != null) - { - for (int i = 0; i < this.comps.Count; i++) - { - this.comps[i].CompTick(); - } - } - } - - public override void ExposeData() - { - base.ExposeData(); - Scribe_Values.Look(ref this.exactPositionInt, "exactPosition", default(Vector3), false); - Scribe_Values.Look(ref this.curSpeed, "curSpeed", default(Vector3), false); - Scribe_Values.Look(ref this.homing, "homing", true, false); - Scribe_Values.Look(ref this.ticksToDestroy, "ticksToDestroy", -1, false); - } - } - - public static class NonPublicFields - { - public static FieldInfo Projectile_AmbientSustainer; - public static FieldInfo ThingWithComps_comps; - public static MethodInfo ProjectileCheckForFreeInterceptBetween; } } \ No newline at end of file