diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index a190acc..87f0f72 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 f58d8de..e09d0ef 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
@@ -50,8 +50,7 @@
true
-
-
+
CatastropheMissile_Weapon
@@ -60,7 +59,7 @@
Artillery
- Security
+ ARA_Buildings
8
ShipbuildingBasics
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 957024e..56e0dc2 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
@@ -6,16 +6,26 @@
ArachnaeSwarm.Projectile_CruiseMissile
- Wula/Projectile/WULA_Loitering_Munition
- Graphic_Single
+ Graphic_Single_AgeSecs
+ Things/Projectile/FleshmassSpitterProjectileSheet
+ (3,3)
+ MoteGlow
+ True
+ 1
ARA_AcidBurn
200
- 50
+ 80
+ 15
+ true0
+ Filth_SpentAcid
+ 4
+ Shell_AcidSpitImpact
+ 60
+ false
5.9
MortarBomb_Explode
- true
@@ -38,6 +48,49 @@
0.5
+
+
+ Shell_AcidSpitStream
+
+
+ Shell_AcidSpitLaunched
+
+
+
+
+
+ Projectile_CatastropheMissile_Fake
+
+ ArachnaeSwarm.Projectile_CruiseMissile
+
+ Graphic_Single_AgeSecs
+ Things/Projectile/FleshmassSpitterProjectileSheet
+ (3,3)
+ MoteGlow
+
+
+ True
+ 1
+ ARA_AcidBurn
+ 0
+ 50
+ 15
+ true
+
+
+
+ true
+ false
+
+
+
+
+ Shell_AcidSpitStream
+
+
+ Shell_AcidSpitLaunched
+
+
@@ -77,7 +130,7 @@
ArachnaeSwarm.Verb_LaunchCatastropheMissile
true
- Projectile_CatastropheMissile
+ Projectile_CatastropheMissile
3.0
1
true
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_MissileSilo.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_MissileSilo.xml
new file mode 100644
index 0000000..b65457c
--- /dev/null
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ArachnaeSwarm_MissileSilo.xml
@@ -0,0 +1,17 @@
+
+
+
+ 发射远程打击
+ 选择一个世界地图上的目标进行远程打击。
+
+ 发射本地打击
+ 手动命令炮塔攻击一个本地地图上的目标。
+
+ 远程目标已设定:{0}
+ 远程打击的目标必须是一个已探索的地点。
+ 没有可用的导弹。
+
+ 取消所有目标
+ 取消当前设定的所有本地和远程目标。
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Wormhole_Keys.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Wormhole_Keys.xml
deleted file mode 100644
index 12fc1e7..0000000
--- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Wormhole_Keys.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
- 部署虫洞传送门
- 选择一名驾驶员来启动一个B端传送门。
- 没有可用的驾驶员
-
-
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs b/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs
index a7c0ed3..591e1a5 100644
--- a/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs
+++ b/Source/ArachnaeSwarm/Buildings/Building_CatastropheMissileSilo.cs
@@ -1,58 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using RimWorld;
-using RimWorld.Planet;
+using System.Text;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.Sound;
+using RimWorld;
+using RimWorld.Planet;
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 SpawnSetup(Map map, bool respawningAfterLoad)
+ {
+ base.SpawnSetup(map, respawningAfterLoad);
+ // This is the definitive fix for the default target issue on spawn.
+ // It runs only once when the building is first created, not on game load.
+ if (!respawningAfterLoad)
+ {
+ this.longTarget = GlobalTargetInfo.Invalid;
+ }
+ }
+
public override void ExposeData()
{
base.ExposeData();
+ // We no longer save/load longTarget here. It is only set by player action.
+ // Scribe_TargetInfo.Look(ref this.longTarget, "longTarget"); // REMOVED
}
protected override void Tick()
{
- base.Tick();
+ // --- Attack Priority Logic ---
+ // Prio 1: Forced Local Target. Let the base class handle it.
+ if (this.forcedTarget.IsValid)
+ {
+ base.Tick();
+ return;
+ }
+ // Prio 2: Remote Target
+ if (this.longTarget.IsValid)
+ {
+ if (base.Active && this.burstCooldownTicksLeft <= 0 && CanFireGlobal(out _))
+ {
+ this.FireMission(this.longTarget);
+ }
+ else
+ {
+ // Manually tick cooldown and turret top rotation when in remote mode to prevent auto-targeting
+ if(this.burstCooldownTicksLeft > 0) this.burstCooldownTicksLeft--;
+ this.top.TurretTopTick();
+ }
+ }
+ // Prio 3: No manual target, fall back to base auto-targeting.
+ else
+ {
+ base.Tick();
+ }
+ }
+
+ public override string GetInspectString()
+ {
+ StringBuilder sb = new StringBuilder(base.GetInspectString());
+ if (this.longTarget.IsValid)
+ {
+ sb.AppendLine();
+ sb.Append("RemoteTargetSet".Translate(this.longTarget.Label));
+ }
+ return sb.ToString();
}
public override IEnumerable GetGizmos()
{
- // Add the default turret gizmos (like "Set forced target")
- foreach (Gizmo c in base.GetGizmos())
+ // Yield base gizmos first, which includes "Set forced target"
+ foreach (var g in base.GetGizmos())
{
- yield return c;
+ yield return g;
}
- // Add our custom global strike gizmo
- Command_Action launch = new Command_Action
+ // Then add our custom "Global Strike" gizmo
+ Command_Action fireGlobal = new Command_Action
{
defaultLabel = "CommandFireGlobal".Translate(),
defaultDesc = "CommandFireGlobalDesc".Translate(),
icon = FireMissionTex,
- action = new Action(this.StartChoosingDestination)
+ action = new Action(StartChoosingDestination)
};
-
+
if (!CanFireGlobal(out string reason))
{
- launch.Disable(reason);
+ fireGlobal.Disable(reason);
+ }
+ // Disable if a local forced target is already set
+ if (this.forcedTarget.IsValid)
+ {
+ fireGlobal.Disable("LocalTargetForced".Translate());
+ }
+ yield return fireGlobal;
+
+ // Add a specific button to clear the remote target
+ if (this.longTarget.IsValid)
+ {
+ Command_Action clearRemote = new Command_Action
+ {
+ defaultLabel = "CommandClearRemoteTarget".Translate(),
+ defaultDesc = "CommandClearRemoteTargetDesc".Translate(),
+ icon = ContentFinder.Get("UI/Designators/Cancel"),
+ action = () =>
+ {
+ this.longTarget = GlobalTargetInfo.Invalid;
+ }
+ };
+ yield return clearRemote;
}
- yield return launch;
}
+
+ public void FireMission(GlobalTargetInfo target)
+ {
+ if (!CanFireGlobal(out _)) return;
+ 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);
+
+ 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_CruiseMissile dummy = (Projectile_CruiseMissile)GenSpawn.Spawn(DefDatabase.GetNamed("Projectile_CatastropheMissile_Fake"), this.Position, this.Map);
+ dummy?.Launch(this, this.DrawPos, new LocalTargetInfo(edgeCell), new LocalTargetInfo(edgeCell), ProjectileHitFlags.None);
+ }
+
+ var refuelableComp = this.TryGetComp();
+ if(refuelableComp != null)
+ {
+ refuelableComp.ConsumeFuel(1);
+ }
+ SoundDef.Named("RocketLaunch").PlayOneShot(new TargetInfo(this.Position, this.Map));
+
+ // Manually reset cooldown.
+ this.BurstComplete();
+ }
+
private bool CanFireGlobal(out string reason)
{
var refuelableComp = this.TryGetComp();
- if (refuelableComp != null && !refuelableComp.HasFuel)
+ if (refuelableComp != null && !refuelableComp.HasFuel)
{
reason = "NoFuel".Translate().CapitalizeFirst();
return false;
@@ -65,13 +167,12 @@ namespace ArachnaeSwarm
{
CameraJumper.TryJump(CameraJumper.GetWorldTarget(this), CameraJumper.MovementMode.Pan);
Find.WorldSelector.ClearSelection();
-
Find.WorldTargeter.BeginTargeting(
- new Func(this.ChoseWorldTarget),
+ new Func(this.ChoseWorldTarget),
true,
- FireMissionTex,
- true,
- () => GenDraw.DrawWorldRadiusRing(this.Map.Tile, 99999),
+ FireMissionTex,
+ true,
+ () => GenDraw.DrawWorldRadiusRing(this.Map.Tile, 99999),
null, null, null, true);
}
@@ -82,12 +183,13 @@ namespace ArachnaeSwarm
Messages.Message("MessageTargetInvalid".Translate(), MessageTypeDefOf.RejectInput, true);
return false;
}
- if (target.Map == this.Map)
+ if (target.Tile == this.Map.Tile)
{
Messages.Message("Cannot target own map for global strike.", MessageTypeDefOf.RejectInput, true);
return false;
}
+ // Allow targeting Pawns and Buildings on the world map
if (target.WorldObject is MapParent mapParent && mapParent.HasMap)
{
var originalMap = this.Map;
@@ -96,53 +198,33 @@ namespace ArachnaeSwarm
};
Current.Game.CurrentMap = mapParent.Map;
- Find.Targeter.BeginTargeting(new TargetingParameters { canTargetLocations = true },
+ Find.Targeter.BeginTargeting(new TargetingParameters
+ {
+ canTargetLocations = true,
+ canTargetPawns = true, // Allow targeting pawns
+ canTargetBuildings = true // Allow targeting buildings
+ },
(LocalTargetInfo localTarget) =>
{
- this.FireMission(new GlobalTargetInfo(localTarget.Cell, mapParent.Map));
- },
+ // Convert LocalTargetInfo to GlobalTargetInfo, handling Thing targets.
+ if (localTarget.HasThing)
+ {
+ this.longTarget = new GlobalTargetInfo(localTarget.Thing);
+ }
+ else
+ {
+ this.longTarget = new GlobalTargetInfo(localTarget.Cell, mapParent.Map);
+ }
+ },
null, onFinished, FireMissionTex, true);
-
+
return true;
}
- else
+ else
{
Messages.Message("MessageTargetMustBeMap".Translate(), MessageTypeDefOf.RejectInput, true);
return false;
}
}
-
- public void FireMission(GlobalTargetInfo target)
- {
- 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/Projectiles/Projectile_CruiseMissile.cs b/Source/ArachnaeSwarm/Verbs/Projectiles/Projectile_CruiseMissile.cs
index ee8fdac..5ef3aa4 100644
--- a/Source/ArachnaeSwarm/Verbs/Projectiles/Projectile_CruiseMissile.cs
+++ b/Source/ArachnaeSwarm/Verbs/Projectiles/Projectile_CruiseMissile.cs
@@ -8,6 +8,7 @@ namespace ArachnaeSwarm
{
public class CruiseMissileProperties : DefModExtension
{
+ public bool isDummy = false;
public DamageDef customDamageDef;
public int customDamageAmount = 5;
public float customExplosionRadius = 1.1f;
@@ -38,11 +39,19 @@ namespace ArachnaeSwarm
private Vector3 Randdd;
private Vector3 position2;
public Vector3 ExPos;
+ public bool isDummy = false;
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Values.Look(ref isDummy, "isDummy", false);
+ }
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
base.SpawnSetup(map, respawningAfterLoad);
settings = def.GetModExtension() ?? new CruiseMissileProperties();
+ this.isDummy = settings.isDummy;
}
private void RandFactor()
@@ -113,6 +122,11 @@ namespace ArachnaeSwarm
var map = base.Map;
base.Impact(hitThing, blockedByShield);
+ if (isDummy)
+ {
+ return;
+ }
+
DoExplosion(
base.Position,
map,