暂存
This commit is contained in:
Binary file not shown.
@@ -6,10 +6,19 @@
|
||||
<label>天灾导弹发射井</label>
|
||||
<description>一个多功能导弹发射平台。它装备的武器系统既可以作为自动炮塔进行本地防御,也可以在操作员的指引下,将“天灾”级巡航导弹发射到全球任何一个角落。</description>
|
||||
<thingClass>ArachnaeSwarm.Building_CatastropheMissileSilo</thingClass>
|
||||
<drawerType>MapMeshAndRealTime</drawerType>
|
||||
<graphicData>
|
||||
<texPath>Things/Building/Security/TurretMortar_Base</texPath>
|
||||
<texPath>Things/Building/Security/TurretHeavy_Base</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
<drawSize>(6,6)</drawSize>
|
||||
<drawSize>(3, 3)</drawSize>
|
||||
<drawOffset>(0,0,-0.1)</drawOffset>
|
||||
<damageData>
|
||||
<rect>(0.2,0.2,0.6,0.6)</rect>
|
||||
</damageData>
|
||||
<shadowData>
|
||||
<volume>(1.5,0.35,1.4)</volume>
|
||||
<offset>(0,0,-0.05)</offset>
|
||||
</shadowData>
|
||||
</graphicData>
|
||||
<size>(2,2)</size>
|
||||
<altitudeLayer>Building</altitudeLayer>
|
||||
@@ -38,7 +47,7 @@
|
||||
<li>ComponentSpacer</li>
|
||||
</thingDefs>
|
||||
</fuelFilter>
|
||||
<fuelCapacity>1</fuelCapacity>
|
||||
<fuelCapacity>10</fuelCapacity>
|
||||
<initialFuelPercent>0</initialFuelPercent>
|
||||
<autoRefuelPercent>1</autoRefuelPercent>
|
||||
<showFuelGizmo>true</showFuelGizmo>
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</graphicData>
|
||||
<projectile>
|
||||
<damageDef>Bomb</damageDef>
|
||||
<damageDef>ARA_AcidBurn</damageDef>
|
||||
<damageAmountBase>200</damageAmountBase>
|
||||
<speed>30</speed>
|
||||
<speed>50</speed>
|
||||
<explosionRadius>5.9</explosionRadius>
|
||||
<soundExplode>MortarBomb_Explode</soundExplode>
|
||||
</projectile>
|
||||
<modExtensions>
|
||||
<li Class="ArachnaeSwarm.CruiseMissileProperties">
|
||||
<customDamageDef>Bomb</customDamageDef>
|
||||
<customDamageDef>ARA_AcidBurn</customDamageDef>
|
||||
<customDamageAmount>150</customDamageAmount>
|
||||
<customExplosionRadius>5.9</customExplosionRadius>
|
||||
<customSoundExplode>MortarBomb_Explode</customSoundExplode>
|
||||
@@ -27,7 +27,7 @@
|
||||
<subExplosionRadius>2.9</subExplosionRadius>
|
||||
<subExplosionDamage>50</subExplosionDamage>
|
||||
<subExplosionSpread>15</subExplosionSpread>
|
||||
<subDamageDef>Bomb</subDamageDef>
|
||||
<subDamageDef>ARA_AcidBurn</subDamageDef>
|
||||
<subSoundExplode>Mortar_Explode</subSoundExplode>
|
||||
<bezierArcHeightFactor>0.05</bezierArcHeightFactor>
|
||||
<bezierMinArcHeight>5</bezierMinArcHeight>
|
||||
|
||||
@@ -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<Texture2D>.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<Gizmo> 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<Texture2D>.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<CompRefuelable>();
|
||||
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<CompRefuelable>();
|
||||
|
||||
// --- Launch Logic starts here ---
|
||||
|
||||
// 1. Create the world-traveling object immediately
|
||||
WorldObject_CatastropheMissile missile = (WorldObject_CatastropheMissile)WorldObjectMaker.MakeWorldObject(
|
||||
DefDatabase<WorldObjectDef>.GetNamed("CatastropheMissile_Flying")
|
||||
);
|
||||
missile.Tile = this.Map.Tile;
|
||||
missile.destinationTile = target.Tile;
|
||||
missile.destinationCell = target.Cell;
|
||||
missile.Projectile = DefDatabase<ThingDef>.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<ThingDef>.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<CompRefuelable>();
|
||||
if (refuelableComp != null && !refuelableComp.HasFuel)
|
||||
{
|
||||
Messages.Message("NoMissileToLaunch".Translate(), silo, MessageTypeDefOf.RejectInput);
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldObject_CatastropheMissile missile = (WorldObject_CatastropheMissile)WorldObjectMaker.MakeWorldObject(
|
||||
DefDatabase<WorldObjectDef>.GetNamed("CatastropheMissile_Flying")
|
||||
);
|
||||
|
||||
missile.Tile = silo.Map.Tile;
|
||||
missile.destinationTile = silo.longTarget.Tile;
|
||||
missile.destinationCell = silo.longTarget.Cell;
|
||||
missile.Projectile = DefDatabase<ThingDef>.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user