diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index fa152df..d72881c 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/ThingDefs_Buildings/ThingDef_Building_CatastropheMissileSilo.xml b/1.6/1.6/Defs/ThingDefs_Buildings/ThingDef_Building_CatastropheMissileSilo.xml index 7425847..e601863 100644 --- a/1.6/1.6/Defs/ThingDefs_Buildings/ThingDef_Building_CatastropheMissileSilo.xml +++ b/1.6/1.6/Defs/ThingDefs_Buildings/ThingDef_Building_CatastropheMissileSilo.xml @@ -6,10 +6,19 @@ 一个多功能导弹发射平台。它装备的武器系统既可以作为自动炮塔进行本地防御,也可以在操作员的指引下,将“天灾”级巡航导弹发射到全球任何一个角落。 ArachnaeSwarm.Building_CatastropheMissileSilo + MapMeshAndRealTime - Things/Building/Security/TurretMortar_Base + Things/Building/Security/TurretHeavy_Base Graphic_Single - (6,6) + (3, 3) + (0,0,-0.1) + + (0.2,0.2,0.6,0.6) + + + (1.5,0.35,1.4) + (0,0,-0.05) + (2,2) Building @@ -38,7 +47,7 @@
  • ComponentSpacer
  • - 1 + 10 0 1 true diff --git a/1.6/1.6/Defs/ThingDefs_Misc/ThingDef_Projectile_CatastropheMissile.xml b/1.6/1.6/Defs/ThingDefs_Misc/ThingDef_Projectile_CatastropheMissile.xml index 5a7b84a..92b1f4b 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/ThingDef_Projectile_CatastropheMissile.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/ThingDef_Projectile_CatastropheMissile.xml @@ -10,15 +10,15 @@ Graphic_Single - Bomb + ARA_AcidBurn 200 - 30 + 50 5.9 MortarBomb_Explode
  • - Bomb + ARA_AcidBurn 150 5.9 MortarBomb_Explode @@ -27,7 +27,7 @@ 2.9 50 15 - Bomb + ARA_AcidBurn Mortar_Explode 0.05 5 diff --git a/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs b/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs index 1ce483e..bfe7480 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs @@ -5,6 +5,7 @@ using RimWorld; using RimWorld.Planet; using UnityEngine; using Verse; +using Verse.AI; using Verse.Sound; namespace ArachnaeSwarm @@ -12,49 +13,57 @@ namespace ArachnaeSwarm [StaticConstructorOnStartup] public class Building_CatastropheMissileSilo : Building_TurretGun { - public GlobalTargetInfo longTarget; - public static readonly Texture2D FireMissionTex = ContentFinder.Get("UI/Commands/Attack", true); public override void ExposeData() { base.ExposeData(); - Scribe_TargetInfo.Look(ref this.longTarget, "longTarget"); + } + + protected override void Tick() + { + base.Tick(); } public override IEnumerable GetGizmos() { + // Add the default turret gizmos (like "Set forced target") foreach (Gizmo c in base.GetGizmos()) { yield return c; } - // Gizmo to set the long range target - Command_Action setTarget = new Command_Action + // Add our custom global strike gizmo + Command_Action launch = new Command_Action { - defaultLabel = "CommandSetGlobalTarget".Translate(), - defaultDesc = "CommandSetGlobalTargetDesc".Translate(), + defaultLabel = "CommandFireGlobal".Translate(), + defaultDesc = "CommandFireGlobalDesc".Translate(), icon = FireMissionTex, action = new Action(this.StartChoosingDestination) }; - if (!this.powerComp.PowerOn) - { - setTarget.Disable("NoPower".Translate().CapitalizeFirst()); - } - yield return setTarget; - // Gizmo to clear the long range target - if (this.longTarget.IsValid) + if (!CanFireGlobal(out string reason)) { - Command_Action clearTarget = new Command_Action - { - defaultLabel = "CommandClearGlobalTarget".Translate(), - defaultDesc = "CommandClearGlobalTargetDesc".Translate(), - icon = ContentFinder.Get("UI/Commands/Cancel"), - action = () => { this.longTarget = GlobalTargetInfo.Invalid; } - }; - yield return clearTarget; + launch.Disable(reason); } + yield return launch; + } + + private bool CanFireGlobal(out string reason) + { + if (!this.powerComp.PowerOn) + { + reason = "NoPower".Translate().CapitalizeFirst(); + return false; + } + var refuelableComp = this.TryGetComp(); + if (refuelableComp != null && !refuelableComp.HasFuel) + { + reason = "NoFuel".Translate().CapitalizeFirst(); + return false; + } + reason = ""; + return true; } private void StartChoosingDestination() @@ -84,7 +93,6 @@ namespace ArachnaeSwarm return false; } - // The target must be a map parent that has a loaded map. if (target.WorldObject is MapParent mapParent && mapParent.HasMap) { var originalMap = this.Map; @@ -93,16 +101,16 @@ namespace ArachnaeSwarm }; Current.Game.CurrentMap = mapParent.Map; - Find.Targeter.BeginTargeting(new TargetingParameters { canTargetLocations = true }, - (LocalTargetInfo localTarget) => // This is called when the user clicks a cell in the target map + Find.Targeter.BeginTargeting(new TargetingParameters { canTargetLocations = true }, + (LocalTargetInfo localTarget) => { this.FireMission(new GlobalTargetInfo(localTarget.Cell, mapParent.Map)); - }, + }, null, onFinished, FireMissionTex, true); return true; } - else + else { Messages.Message("MessageTargetMustBeMap".Translate(), MessageTypeDefOf.RejectInput, true); return false; @@ -111,9 +119,35 @@ namespace ArachnaeSwarm public void FireMission(GlobalTargetInfo target) { - this.longTarget = target; - this.OrderAttack(new LocalTargetInfo(this)); - Messages.Message("Global target acquired. Firing sequence initiated.", MessageTypeDefOf.PositiveEvent); + if (!CanFireGlobal(out _)) return; + + var refuelableComp = this.TryGetComp(); + + // --- Launch Logic starts here --- + + // 1. Create the world-traveling object immediately + WorldObject_CatastropheMissile missile = (WorldObject_CatastropheMissile)WorldObjectMaker.MakeWorldObject( + DefDatabase.GetNamed("CatastropheMissile_Flying") + ); + missile.Tile = this.Map.Tile; + missile.destinationTile = target.Tile; + missile.destinationCell = target.Cell; + missile.Projectile = DefDatabase.GetNamed("Projectile_CatastropheMissile"); + Find.WorldObjects.Add(missile); + + // 2. Launch a local dummy projectile for visual effect, that will never impact. + if (CellFinder.TryFindRandomEdgeCellWith(c => this.Map.reachability.CanReach(this.Position, c, PathEndMode.OnCell, TraverseParms.For(TraverseMode.NoPassClosedDoors, Danger.Deadly)), this.Map, 0f, out IntVec3 edgeCell)) + { + Projectile dummy = (Projectile)GenSpawn.Spawn(DefDatabase.GetNamed("Projectile_CatastropheMissile"), this.Position, this.Map); + dummy.Launch(this, this.DrawPos, new LocalTargetInfo(edgeCell), new LocalTargetInfo(edgeCell), ProjectileHitFlags.None); + } + + // 3. Consume resources and start cooldown + if(refuelableComp != null) + { + refuelableComp.ConsumeFuel(1); + } + SoundDef.Named("RocketLaunch").PlayOneShot(new TargetInfo(this.Position, this.Map)); } } } \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Verbs/Verb_LaunchCatastropheMissile.cs b/Source/ArachnaeSwarm/Verbs/Verb_LaunchCatastropheMissile.cs index e963a9a..c451d9e 100644 --- a/Source/ArachnaeSwarm/Verbs/Verb_LaunchCatastropheMissile.cs +++ b/Source/ArachnaeSwarm/Verbs/Verb_LaunchCatastropheMissile.cs @@ -2,77 +2,17 @@ using RimWorld; using RimWorld.Planet; using UnityEngine; using Verse; +using Verse.AI; using Verse.Sound; namespace ArachnaeSwarm { public class Verb_LaunchCatastropheMissile : Verb_Shoot { - public override bool CanHitTargetFrom(IntVec3 root, LocalTargetInfo targ) - { - var silo = this.Caster as Building_CatastropheMissileSilo; - if (silo != null && silo.longTarget.IsValid) - { - return true; - } - return base.CanHitTargetFrom(root, targ); - } - + // This verb is now only for local defense. The global launch is handled by the Building. protected override bool TryCastShot() { - var silo = this.Caster as Building_CatastropheMissileSilo; - - if (silo != null && silo.longTarget.IsValid) - { - return this.TryCastGlobalShot(silo); - } - - // If no long target, perform a normal local shot return base.TryCastShot(); } - - private bool TryCastGlobalShot(Building_CatastropheMissileSilo silo) - { - var refuelableComp = silo.TryGetComp(); - if (refuelableComp != null && !refuelableComp.HasFuel) - { - Messages.Message("NoMissileToLaunch".Translate(), silo, MessageTypeDefOf.RejectInput); - return false; - } - - WorldObject_CatastropheMissile missile = (WorldObject_CatastropheMissile)WorldObjectMaker.MakeWorldObject( - DefDatabase.GetNamed("CatastropheMissile_Flying") - ); - - missile.Tile = silo.Map.Tile; - missile.destinationTile = silo.longTarget.Tile; - missile.destinationCell = silo.longTarget.Cell; - missile.Projectile = DefDatabase.GetNamed("Projectile_CatastropheMissile"); - - Find.WorldObjects.Add(missile); - - if(refuelableComp != null) - { - refuelableComp.ConsumeFuel(1); - } - - SoundDef.Named("RocketLaunch").PlayOneShot(new TargetInfo(silo.Position, silo.Map)); - - // Reset target after launch - silo.longTarget = GlobalTargetInfo.Invalid; - - // Manually reset cooldown - if (this.burstShotsLeft < this.verbProps.burstShotCount) - { - this.burstShotsLeft = 0; - } - if (this.verbProps.burstShotCount > 0) - { - this.ticksToNextBurstShot = this.verbProps.ticksBetweenBurstShots; - } - this.state = VerbState.Idle; - - return true; - } } } \ No newline at end of file diff --git a/Source/ArachnaeSwarm/World/WorldObject_CatastropheMissile.cs b/Source/ArachnaeSwarm/World/WorldObject_CatastropheMissile.cs index 7c99942..4d5a3d0 100644 --- a/Source/ArachnaeSwarm/World/WorldObject_CatastropheMissile.cs +++ b/Source/ArachnaeSwarm/World/WorldObject_CatastropheMissile.cs @@ -13,7 +13,7 @@ namespace ArachnaeSwarm private int initialTile = -1; private float traveledPct; - private const float TravelSpeed = 0.0002f; // Faster than sabot + private const float TravelSpeed = 0.0002f; public override void ExposeData() { @@ -60,11 +60,14 @@ namespace ArachnaeSwarm Map targetMap = Current.Game.FindMap(this.destinationTile); if (targetMap != null) { - // Target is a loaded map, spawn the projectile to hit it + // Find a random entry point at the edge of the target map IntVec3 entryCell = CellFinder.RandomEdgeCell(targetMap); - + + // Spawn the final projectile (the cruise missile) at the entry point Projectile_CruiseMissile missile = (Projectile_CruiseMissile)GenSpawn.Spawn(this.Projectile, entryCell, targetMap, WipeMode.Vanish); - missile.Launch(null, this.destinationCell, this.destinationCell, ProjectileHitFlags.IntendedTarget); + + // Launch it from the entry point towards the final destination cell + missile.Launch(null, entryCell.ToVector3Shifted(), new LocalTargetInfo(this.destinationCell), new LocalTargetInfo(this.destinationCell), ProjectileHitFlags.IntendedTarget); } Find.WorldObjects.Remove(this);