Merge branch '导弹'
# Conflicts: # 1.6/1.6/Assemblies/ArachnaeSwarm.dll
This commit is contained in:
@@ -209,6 +209,10 @@
|
||||
<Compile Include="Building_ARANutrientDispenser.cs" />
|
||||
<Compile Include="ARAFoodDispenserProperties.cs" />
|
||||
<Compile Include="Patch_DispenserFoodSearch.cs" />
|
||||
<Compile Include="Buildings\Building_CatastropheMissileSilo.cs" />
|
||||
<Compile Include="World\WorldObject_CatastropheMissile.cs" />
|
||||
<Compile Include="HarmonyPatches\Patch_ForceTargetable.cs" />
|
||||
<Compile Include="Comps\CompForceTargetable.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Wormhole\Building_WormholePortal_A.cs" />
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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<Texture2D>.Get("UI/Commands/Attack", true);
|
||||
|
||||
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||
{
|
||||
base.SpawnSetup(map, respawningAfterLoad);
|
||||
if (!respawningAfterLoad)
|
||||
{
|
||||
this.longTarget = GlobalTargetInfo.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_TargetInfo.Look(ref this.longTarget, "longTarget");
|
||||
}
|
||||
|
||||
protected override void Tick()
|
||||
{
|
||||
// Base tick handles all local targeting, cooldowns, and fuel consumption via XML.
|
||||
base.Tick();
|
||||
|
||||
// If a local target is active, prevent remote targeting.
|
||||
if (this.forcedTarget.IsValid && this.longTarget.IsValid)
|
||||
{
|
||||
this.longTarget = GlobalTargetInfo.Invalid;
|
||||
}
|
||||
|
||||
// If a remote target is set and the turret is ready, fire.
|
||||
// The base.Tick() cooldown handling prevents this from running if a local shot was just fired.
|
||||
if (this.longTarget.IsValid && this.burstCooldownTicksLeft <= 0 && base.Active && CanFireGlobal(out _))
|
||||
{
|
||||
this.FireMission(this.longTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetInspectString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(base.GetInspectString());
|
||||
|
||||
if (burstCooldownTicksLeft > 0)
|
||||
{
|
||||
if (sb.Length > 0) sb.AppendLine();
|
||||
sb.Append("Cooldown".Translate() + ": " + this.burstCooldownTicksLeft.ToStringTicksToPeriod());
|
||||
}
|
||||
|
||||
if (this.longTarget.IsValid)
|
||||
{
|
||||
if (sb.Length > 0) sb.AppendLine();
|
||||
sb.Append("RemoteTargetSet".Translate(this.longTarget.Label));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> GetGizmos()
|
||||
{
|
||||
foreach (var g in base.GetGizmos())
|
||||
{
|
||||
yield return g;
|
||||
}
|
||||
|
||||
Command_Action fireGlobal = new Command_Action
|
||||
{
|
||||
defaultLabel = "CommandFireGlobal".Translate(),
|
||||
defaultDesc = "CommandFireGlobalDesc".Translate(),
|
||||
icon = FireMissionTex,
|
||||
action = new Action(StartChoosingDestination)
|
||||
};
|
||||
|
||||
if (!CanFireGlobal(out string reason))
|
||||
{
|
||||
fireGlobal.Disable(reason);
|
||||
}
|
||||
if (this.forcedTarget.IsValid)
|
||||
{
|
||||
fireGlobal.Disable("LocalTargetForced".Translate());
|
||||
}
|
||||
yield return fireGlobal;
|
||||
|
||||
if (this.longTarget.IsValid)
|
||||
{
|
||||
Command_Action clearRemote = new Command_Action
|
||||
{
|
||||
defaultLabel = "CommandClearRemoteTarget".Translate(),
|
||||
defaultDesc = "CommandClearRemoteTargetDesc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel"),
|
||||
action = () =>
|
||||
{
|
||||
this.longTarget = GlobalTargetInfo.Invalid;
|
||||
}
|
||||
};
|
||||
yield return clearRemote;
|
||||
}
|
||||
}
|
||||
|
||||
public void FireMission(GlobalTargetInfo target)
|
||||
{
|
||||
if (!CanFireGlobal(out _)) return;
|
||||
|
||||
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);
|
||||
|
||||
Vector3 shellDirection = Vector3.forward.RotatedBy(this.top.CurRotation);
|
||||
IntVec3 outcell = (this.DrawPos + shellDirection * 500f).ToIntVec3();
|
||||
|
||||
Projectile_CruiseMissile dummy = (Projectile_CruiseMissile)GenSpawn.Spawn(DefDatabase<ThingDef>.GetNamed("Projectile_CatastropheMissile_Fake"), this.Position, this.Map);
|
||||
dummy?.Launch(this, this.DrawPos, new LocalTargetInfo(outcell), new LocalTargetInfo(outcell), ProjectileHitFlags.None);
|
||||
|
||||
var refuelableComp = this.TryGetComp<CompRefuelable>();
|
||||
if(refuelableComp != null)
|
||||
{
|
||||
refuelableComp.ConsumeFuel(1);
|
||||
}
|
||||
SoundDef.Named("RocketLaunch").PlayOneShot(new TargetInfo(this.Position, this.Map));
|
||||
|
||||
this.BurstComplete();
|
||||
}
|
||||
|
||||
private bool CanFireGlobal(out string reason)
|
||||
{
|
||||
var refuelableComp = this.TryGetComp<CompRefuelable>();
|
||||
if (refuelableComp != null && !refuelableComp.HasFuel)
|
||||
{
|
||||
reason = "NoFuel".Translate().CapitalizeFirst();
|
||||
return false;
|
||||
}
|
||||
reason = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StartChoosingDestination()
|
||||
{
|
||||
CameraJumper.TryJump(CameraJumper.GetWorldTarget(this), CameraJumper.MovementMode.Pan);
|
||||
Find.WorldSelector.ClearSelection();
|
||||
Find.WorldTargeter.BeginTargeting(
|
||||
new Func<GlobalTargetInfo, bool>(this.ChoseWorldTarget),
|
||||
true,
|
||||
FireMissionTex,
|
||||
true,
|
||||
() => GenDraw.DrawWorldRadiusRing(this.Map.Tile, 99999),
|
||||
null, null, null, true);
|
||||
}
|
||||
|
||||
private bool ChoseWorldTarget(GlobalTargetInfo target)
|
||||
{
|
||||
if (!target.IsValid)
|
||||
{
|
||||
Messages.Message("MessageTargetInvalid".Translate(), MessageTypeDefOf.RejectInput, true);
|
||||
return false;
|
||||
}
|
||||
if (target.Tile == this.Map.Tile)
|
||||
{
|
||||
Messages.Message("Cannot target own map for global strike.", MessageTypeDefOf.RejectInput, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.WorldObject is MapParent mapParent && mapParent.HasMap)
|
||||
{
|
||||
var originalMap = this.Map;
|
||||
Action onFinished = () => {
|
||||
if (Current.Game.CurrentMap != originalMap) Current.Game.CurrentMap = originalMap;
|
||||
};
|
||||
|
||||
Current.Game.CurrentMap = mapParent.Map;
|
||||
Find.Targeter.BeginTargeting(new TargetingParameters
|
||||
{
|
||||
canTargetLocations = true,
|
||||
canTargetPawns = true,
|
||||
canTargetBuildings = true
|
||||
},
|
||||
(LocalTargetInfo localTarget) =>
|
||||
{
|
||||
if (localTarget.HasThing)
|
||||
{
|
||||
this.longTarget = new GlobalTargetInfo(localTarget.Thing);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.longTarget = new GlobalTargetInfo(localTarget.Cell, mapParent.Map);
|
||||
}
|
||||
this.forcedTarget = LocalTargetInfo.Invalid;
|
||||
},
|
||||
null, onFinished, FireMissionTex, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("MessageTargetMustBeMap".Translate(), MessageTypeDefOf.RejectInput, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Source/ArachnaeSwarm/Comps/CompForceTargetable.cs
Normal file
18
Source/ArachnaeSwarm/Comps/CompForceTargetable.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_ForceTargetable : CompProperties
|
||||
{
|
||||
public CompProperties_ForceTargetable()
|
||||
{
|
||||
this.compClass = typeof(CompForceTargetable);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompForceTargetable : ThingComp
|
||||
{
|
||||
// This component doesn't need any specific logic.
|
||||
// Its mere presence on a turret is checked by the Harmony patch.
|
||||
}
|
||||
}
|
||||
23
Source/ArachnaeSwarm/HarmonyPatches/Patch_ForceTargetable.cs
Normal file
23
Source/ArachnaeSwarm/HarmonyPatches/Patch_ForceTargetable.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using HarmonyLib;
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[HarmonyPatch(typeof(Building_TurretGun), "get_CanSetForcedTarget")]
|
||||
public static class Patch_Building_TurretGun_CanSetForcedTarget
|
||||
{
|
||||
public static void Postfix(Building_TurretGun __instance, ref bool __result)
|
||||
{
|
||||
if (__result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (__instance.GetComp<CompForceTargetable>() != null && __instance.Faction == Faction.OfPlayer)
|
||||
{
|
||||
__result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<CruiseMissileProperties>() ?? 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,
|
||||
|
||||
76
Source/ArachnaeSwarm/World/WorldObject_CatastropheMissile.cs
Normal file
76
Source/ArachnaeSwarm/World/WorldObject_CatastropheMissile.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using RimWorld.Planet;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class WorldObject_CatastropheMissile : WorldObject
|
||||
{
|
||||
public int destinationTile = -1;
|
||||
public IntVec3 destinationCell = IntVec3.Invalid;
|
||||
public ThingDef Projectile;
|
||||
|
||||
private int initialTile = -1;
|
||||
private float traveledPct;
|
||||
private const float TravelSpeed = 0.0002f;
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref destinationTile, "destinationTile", 0);
|
||||
Scribe_Values.Look(ref destinationCell, "destinationCell");
|
||||
Scribe_Defs.Look(ref Projectile, "Projectile");
|
||||
Scribe_Values.Look(ref initialTile, "initialTile", 0);
|
||||
Scribe_Values.Look(ref traveledPct, "traveledPct", 0f);
|
||||
}
|
||||
|
||||
public override void PostAdd()
|
||||
{
|
||||
base.PostAdd();
|
||||
this.initialTile = this.Tile;
|
||||
}
|
||||
|
||||
private Vector3 StartPos => Find.WorldGrid.GetTileCenter(this.initialTile);
|
||||
private Vector3 EndPos => Find.WorldGrid.GetTileCenter(this.destinationTile);
|
||||
|
||||
public override Vector3 DrawPos => Vector3.Slerp(StartPos, EndPos, traveledPct);
|
||||
|
||||
protected override void Tick()
|
||||
{
|
||||
base.Tick();
|
||||
float distance = GenMath.SphericalDistance(StartPos.normalized, EndPos.normalized);
|
||||
if(distance > 0)
|
||||
{
|
||||
traveledPct += TravelSpeed / distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
traveledPct = 1;
|
||||
}
|
||||
|
||||
if (traveledPct >= 1f)
|
||||
{
|
||||
Arrived();
|
||||
}
|
||||
}
|
||||
|
||||
private void Arrived()
|
||||
{
|
||||
Map targetMap = Current.Game.FindMap(this.destinationTile);
|
||||
if (targetMap != null)
|
||||
{
|
||||
// Find a random entry point at the north edge of the target map
|
||||
IntVec3 entryCell = CellFinder.RandomEdgeCell(Rot4.North, 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);
|
||||
|
||||
// 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