diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index f5d0010..183e09f 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_Laser.xml b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon_Laser.xml new file mode 100644 index 0000000..b13fbe2 --- /dev/null +++ b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon_Laser.xml @@ -0,0 +1,86 @@ + + + + + + WULA_RW_DM_AR_Arc + + 乌拉帝国一线部队所使用的由暗物质驱动的常规步枪的改造版。现在它发射的能量束会在命中后寻找并跳跃到附近的其他敌人身上,形成致命的能量链。 + Ultra + + Wula/Weapon/WULA_RW_DM_AR + Graphic_Single + 1.2 + + +
  • Wula_Ranged_Weapon_T4
  • +
    + 0.9 + Interact_ChargeRifle + + +
  • WULA_Cube_Productor_Energy
  • +
    + WULA_Synth_Weapon_4_DM_Base_Technology + UnfinishedWeapon +
    + + 400 + 200 + 4 + + + 40000 + 4.5 + 1 + 1 + 1 + 1 + 1.25 + + +
  • + ArachnaeSwarm.Verb_ShootBeamArc + + + true + 1 + 36 + 6 + 10 + Wula_Dark_Matter_Beam + 90 + + + 0 + BeamGraser_Shooting + Fleck_BeamBurn + 0.32 + Mote_Wula_Dark_Matter_Beam + GraserBeam_End + 0.35 + + + 0.6 + 0.6 + 0.25 + + + + true + + + + 4 + 30 + 0.7 + Mote_Wula_Dark_Matter_Beam +
  • +
    + None + +
  • RewardStandardQualitySuper
  • +
    +
    + +
    \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 06637fc..a0be073 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -233,6 +233,7 @@ + diff --git a/Source/ArachnaeSwarm/Verbs/Verb_ShootBeamArc.cs b/Source/ArachnaeSwarm/Verbs/Verb_ShootBeamArc.cs new file mode 100644 index 0000000..a0f5bc1 --- /dev/null +++ b/Source/ArachnaeSwarm/Verbs/Verb_ShootBeamArc.cs @@ -0,0 +1,193 @@ +using System.Collections.Generic; +using RimWorld; +using UnityEngine; +using Verse; +using Verse.AI; +using Verse.Sound; +using System.Linq; + +namespace ArachnaeSwarm +{ + public class VerbProperties_BeamArc : VerbProperties + { + public int conductNum; + public float conductRange; + public float secondaryDamageFactor = 0.5f; + public ThingDef chainMoteDef; + + public VerbProperties_BeamArc() + { + this.verbClass = typeof(Verb_ShootBeamArc); + } + } + + // This class is a modified copy of Verb_ShootBeam to implement chain-lightning functionality. + public class Verb_ShootBeamArc : Verb + { + // --- Fields from original Verb_ShootBeam --- + private int ticksToNextPathStep; + private MoteDualAttached mote; // This will be the main beam + private Effecter endEffecter; + private Sustainer sustainer; + + // --- Custom fields for chain logic --- + protected List chainedTargets = new List(); + protected List chainMotes = new List(); + private VerbProperties_BeamArc Props => this.verbProps as VerbProperties_BeamArc; + + protected override int ShotsPerBurst => base.BurstShotCount; + + public override void WarmupComplete() + { + // --- Chain Target Finding Logic --- + chainedTargets.Clear(); + foreach (MoteDualAttached m in chainMotes) { m.Destroy(); } + chainMotes.Clear(); + + if (this.Props != null && this.Props.conductNum > 0 && this.currentTarget.HasThing) + { + Thing currentTargetThing = this.currentTarget.Thing; + chainedTargets.Add(currentTargetThing); + + Thing lastTarget = currentTargetThing; + for (int i = 0; i < this.Props.conductNum; i++) + { + Thing nextTarget = AttackTargetFinder.BestAttackTarget(lastTarget as Pawn, TargetScanFlags.NeedLOSToAll, (Thing t) => + t is Pawn p && !p.Downed && !chainedTargets.Contains(t) && t.Position.InHorDistOf(lastTarget.Position, this.Props.conductRange) && this.Caster.HostileTo(t), + 0f, 9999f, default(IntVec3), this.Props.conductRange) as Thing; + + if (nextTarget != null) + { + chainedTargets.Add(nextTarget); + lastTarget = nextTarget; + } + else { break; } + } + } + + // --- Original Verb_ShootBeam Logic (simplified) --- + burstShotsLeft = ShotsPerBurst; + state = VerbState.Bursting; + + // Create main beam mote + if (verbProps.beamMoteDef != null && this.currentTarget.Thing != null) + { + mote = MoteMaker.MakeInteractionOverlay(verbProps.beamMoteDef, caster, this.currentTarget.Thing); + } + + // Create chain motes + if (chainedTargets.Count > 1) + { + for (int i = 0; i < chainedTargets.Count - 1; i++) + { + ThingDef moteDef = this.Props.chainMoteDef ?? this.verbProps.beamMoteDef; + if (moteDef != null) + { + MoteDualAttached chainLinkMote = MoteMaker.MakeInteractionOverlay(moteDef, chainedTargets[i], chainedTargets[i + 1]); + chainMotes.Add(chainLinkMote); + } + } + } + + TryCastNextBurstShot(); + ticksToNextPathStep = verbProps.ticksBetweenBurstShots; + endEffecter?.Cleanup(); + if (verbProps.soundCastBeam != null) + { + sustainer = verbProps.soundCastBeam.TrySpawnSustainer(SoundInfo.InMap(caster, MaintenanceType.PerTick)); + } + } + + public override void BurstingTick() + { + // --- Update Visuals --- + mote?.Maintain(); + foreach (MoteDualAttached m in chainMotes) { m.Maintain(); } + + // --- Original ground/end effect logic (simplified to target) --- + if (this.currentTarget.Thing != null) + { + Vector3 endPoint = this.currentTarget.Thing.DrawPos; + IntVec3 endCell = this.currentTarget.Cell; + + if (verbProps.beamGroundFleckDef != null && Rand.Chance(verbProps.beamFleckChancePerTick)) + { + FleckMaker.Static(endPoint, caster.Map, verbProps.beamGroundFleckDef); + } + if (endEffecter == null && verbProps.beamEndEffecterDef != null) + { + endEffecter = verbProps.beamEndEffecterDef.Spawn(endCell, caster.Map, Vector3.zero); + } + if (endEffecter != null) + { + endEffecter.EffectTick(new TargetInfo(endCell, caster.Map), TargetInfo.Invalid); + endEffecter.ticksLeft--; + } + } + sustainer?.Maintain(); + } + + protected override bool TryCastShot() + { + if (this.currentTarget.HasThing && this.currentTarget.Thing.Map != this.caster.Map) { return false; } + + if (base.EquipmentSource != null) + { + base.EquipmentSource.GetComp()?.Notify_ProjectileLaunched(); + base.EquipmentSource.GetComp()?.UsedOnce(); + } + + // --- Apply Damage to Chain --- + if (this.chainedTargets.Any()) + { + this.ApplyChainDamage(this.chainedTargets[0], 1.0f); + for (int i = 1; i < this.chainedTargets.Count; i++) + { + this.ApplyChainDamage(this.chainedTargets[i], this.Props.secondaryDamageFactor); + } + } + else if(this.currentTarget.Thing != null) + { + this.ApplyChainDamage(this.currentTarget.Thing, 1.0f); + } + + return true; + } + + private void ApplyChainDamage(Thing thing, float damageFactor) + { + Map map = this.caster.Map; + if (thing == null || this.verbProps.beamDamageDef == null) { return; } + + float angleFlat = (this.currentTarget.Cell - this.caster.Position).AngleFlat; + BattleLogEntry_RangedImpact log = new BattleLogEntry_RangedImpact(this.caster, thing, this.currentTarget.Thing, base.EquipmentSource.def, null, null); + + DamageInfo dinfo; + if (this.verbProps.beamTotalDamage > 0f) + { + float damagePerShot = this.verbProps.beamTotalDamage / (float)this.ShotsPerBurst; + dinfo = new DamageInfo(this.verbProps.beamDamageDef, damagePerShot * damageFactor, this.verbProps.beamDamageDef.defaultArmorPenetration, angleFlat, this.caster, null, base.EquipmentSource.def, DamageInfo.SourceCategory.ThingOrUnknown, this.currentTarget.Thing); + } + else + { + float amount = (float)this.verbProps.beamDamageDef.defaultDamage * damageFactor; + dinfo = new DamageInfo(this.verbProps.beamDamageDef, amount, this.verbProps.beamDamageDef.defaultArmorPenetration, angleFlat, this.caster, null, base.EquipmentSource.def, DamageInfo.SourceCategory.ThingOrUnknown, this.currentTarget.Thing); + } + + thing.TakeDamage(dinfo).AssociateWithLog(log); + + if (thing.CanEverAttachFire()) + { + float chance = this.verbProps.flammabilityAttachFireChanceCurve?.Evaluate(thing.GetStatValue(StatDefOf.Flammability)) ?? this.verbProps.beamChanceToAttachFire; + if (Rand.Chance(chance)) + { + thing.TryAttachFire(this.verbProps.beamFireSizeRange.RandomInRange, this.caster); + } + } + else if (Rand.Chance(this.verbProps.beamChanceToStartFire)) + { + FireUtility.TryStartFireIn(thing.Position, map, this.verbProps.beamFireSizeRange.RandomInRange, this.caster, this.verbProps.flammabilityAttachFireChanceCurve); + } + } + } +} \ No newline at end of file