feat(Flyover): 天巫集群掠食 — 空投拦截系统
新增天巫种对敌方空投袭击的自动拦截功能: - GameComponent_DropPodInterceptor: 全局拦截状态管理与核心逻辑 - Harmony Prefix 挂钩 EdgeDrop/CenterDrop Arrive(),拦截 1-3 个运输仓 - 被拦截 Pawn 击杀(Bite)后尸体以空投仓形式落地 - 拦截时生成天巫种 FlyOver 视觉飞越 + PositiveEvent 信件通知 - 引航种新增 ARA_ToggleDropPodIntercept 自释放能力切换开关 - 前置检查:开关启用 + 天巫升空 + 敌对派系,至少保留 1 名袭击者 新增文件: - Source/.../GameComponent_DropPodInterceptor.cs - Source/.../Patch_DropPodIntercept.cs - Source/.../CompAbilityEffect_ToggleDropPodIntercept.cs - Defs/AbilityDefs/Ability_DropPodIntercept.xml 修改文件: - ARA_PawnKinds.xml (Skyraider abilities) - AirStrike_Keys.xml (10 localization keys) - ArachnaeSwarm.csproj (3 Compile entries)
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_ToggleDropPodIntercept : CompProperties_AbilityEffect
|
||||
{
|
||||
public string enabledMessage = "ARA_InterceptDropPod_Enabled";
|
||||
public string disabledMessage = "ARA_InterceptDropPod_Disabled";
|
||||
public string noAircraftMessage = "ARA_InterceptDropPod_NoAircraft";
|
||||
public ThingDef requiredAircraftType;
|
||||
|
||||
public CompProperties_ToggleDropPodIntercept()
|
||||
{
|
||||
compClass = typeof(CompAbilityEffect_ToggleDropPodIntercept);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompAbilityEffect_ToggleDropPodIntercept : CompAbilityEffect
|
||||
{
|
||||
public new CompProperties_ToggleDropPodIntercept Props => (CompProperties_ToggleDropPodIntercept)props;
|
||||
|
||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||
{
|
||||
base.Apply(target, dest);
|
||||
|
||||
GameComponent_DropPodInterceptor interceptor = Current.Game?.GetComponent<GameComponent_DropPodInterceptor>();
|
||||
if (interceptor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool enabled = interceptor.ToggleIntercept();
|
||||
string messageKey = enabled ? Props.enabledMessage : Props.disabledMessage;
|
||||
Messages.Message(messageKey.Translate(), parent.pawn, MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
|
||||
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
|
||||
{
|
||||
if (!base.Valid(target, throwMessages))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GameComponent_DropPodInterceptor interceptor = Current.Game?.GetComponent<GameComponent_DropPodInterceptor>();
|
||||
if (interceptor == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!interceptor.HasAirborneTianwu(Props.requiredAircraftType))
|
||||
{
|
||||
if (throwMessages)
|
||||
{
|
||||
Messages.Message(Props.noAircraftMessage.Translate(), parent.pawn, MessageTypeDefOf.RejectInput);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ExtraLabelMouseAttachment(LocalTargetInfo target)
|
||||
{
|
||||
GameComponent_DropPodInterceptor interceptor = Current.Game?.GetComponent<GameComponent_DropPodInterceptor>();
|
||||
if (interceptor == null)
|
||||
{
|
||||
return base.ExtraLabelMouseAttachment(target);
|
||||
}
|
||||
|
||||
string status = interceptor.IsInterceptEnabled
|
||||
? "ARA_InterceptDropPod_StatusOn".Translate()
|
||||
: "ARA_InterceptDropPod_StatusOff".Translate();
|
||||
|
||||
return "ARA_InterceptDropPod_Status".Translate(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,7 @@
|
||||
<Compile Include="Abilities\ARA_TerrainRestriction\CompProperties_TerrainRestriction.cs" />
|
||||
<Compile Include="Abilities\CompAbilityEffect_DRM_Deaddustpop.cs" />
|
||||
<Compile Include="Abilities\CompAbilityEffect_RandomHediff.cs" />
|
||||
<Compile Include="Abilities\CompAbilityEffect_ToggleDropPodIntercept.cs" />
|
||||
<Compile Include="Abilities\CompAbilityEffect_TransformCorpse.cs" />
|
||||
<Compile Include="Abilities\ARA_PsychicBrainburn\CompAbilityEffect_PsychicBrainburn.cs" />
|
||||
<Compile Include="Abilities\ARA_PsychicBrainburn\CompProperties_PsychicBrainburn.cs" />
|
||||
@@ -269,10 +270,12 @@
|
||||
<Compile Include="Flyover\ARA_ShipArtillery\CompShipArtillery.cs" />
|
||||
<Compile Include="Flyover\ARA_SpawnFlyOver\CompAbilityEffect_SpawnFlyOver.cs" />
|
||||
<Compile Include="Flyover\ARA_SpawnFlyOver\CompProperties_AbilitySpawnFlyOver.cs" />
|
||||
<Compile Include="Flyover\GameComponent_DropPodInterceptor.cs" />
|
||||
<Compile Include="Flyover\ThingclassFlyOver.cs" />
|
||||
<Compile Include="HarmonyPatches\DestroyRemovesResearch\CompDestroyRemovesResearch.cs" />
|
||||
<Compile Include="HarmonyPatches\DestroyRemovesResearch\CompProperties_DestroyRemovesResearch.cs" />
|
||||
<Compile Include="HarmonyPatches\Patch_DraftableAnimals.cs" />
|
||||
<Compile Include="HarmonyPatches\Patch_DropPodIntercept.cs" />
|
||||
<Compile Include="HarmonyPatches\Patch_ForceTargetable.cs" />
|
||||
<Compile Include="HarmonyPatches\Patch_PlantPollutionNullCheck.cs" />
|
||||
<Compile Include="HarmonyPatches\Patch_ResearchManager_AddRemoveMethod.cs" />
|
||||
@@ -467,4 +470,4 @@
|
||||
<RemoveDir Directories="$(ProjectDir)obj\Debug" />
|
||||
<RemoveDir Directories="$(ProjectDir)obj\Release" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
177
Source/ArachnaeSwarm/Flyover/GameComponent_DropPodInterceptor.cs
Normal file
177
Source/ArachnaeSwarm/Flyover/GameComponent_DropPodInterceptor.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class GameComponent_DropPodInterceptor : GameComponent
|
||||
{
|
||||
private const string DefaultAircraftDefName = "ARA_HiveCorvette_Entity";
|
||||
private const string DefaultInterceptFlyOverDefName = "ARA_HiveCorvette_Fake";
|
||||
|
||||
private bool interceptEnabled;
|
||||
|
||||
public bool IsInterceptEnabled => interceptEnabled;
|
||||
|
||||
public GameComponent_DropPodInterceptor(Game game)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ToggleIntercept()
|
||||
{
|
||||
interceptEnabled = !interceptEnabled;
|
||||
ArachnaeLog.Debug($"DropPodInterceptor toggled: {interceptEnabled}");
|
||||
return interceptEnabled;
|
||||
}
|
||||
|
||||
public bool HasAirborneTianwu(ThingDef requiredAircraftDef = null)
|
||||
{
|
||||
WorldComponent_AircraftManager manager = Find.World?.GetComponent<WorldComponent_AircraftManager>();
|
||||
if (manager == null || Faction.OfPlayer == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ThingDef aircraftDef = requiredAircraftDef ?? DefDatabase<ThingDef>.GetNamedSilentFail(DefaultAircraftDefName);
|
||||
if (aircraftDef == null)
|
||||
{
|
||||
ArachnaeLog.Debug($"DropPodInterceptor: missing aircraft def {DefaultAircraftDefName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return manager.GetAvailableAircraftCount(aircraftDef, Faction.OfPlayer) > 0;
|
||||
}
|
||||
|
||||
public bool TryInterceptDropPods(List<Pawn> pawns, IncidentParms parms, out List<Pawn> interceptedPawns)
|
||||
{
|
||||
interceptedPawns = new List<Pawn>();
|
||||
|
||||
if (pawns == null || pawns.Count <= 1 || !interceptEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parms == null || parms.faction == null || Faction.OfPlayer == null || !parms.faction.HostileTo(Faction.OfPlayer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HasAirborneTianwu())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Map map = parms.target as Map;
|
||||
if (map == null)
|
||||
{
|
||||
ArachnaeLog.Debug("DropPodInterceptor: target map missing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int validPawnCount = pawns.Count(p => p != null);
|
||||
if (validPawnCount <= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxInterceptCount = Mathf.Min(3, validPawnCount - 1);
|
||||
int interceptCount = Rand.RangeInclusive(1, maxInterceptCount);
|
||||
|
||||
List<Pawn> selected = pawns.Where(p => p != null).InRandomOrder().Take(interceptCount).ToList();
|
||||
if (selected.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Thing> corpses = new List<Thing>();
|
||||
foreach (Pawn pawn in selected)
|
||||
{
|
||||
if (!pawns.Remove(pawn))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
interceptedPawns.Add(pawn);
|
||||
|
||||
if (!pawn.Dead)
|
||||
{
|
||||
pawn.Kill(new DamageInfo(DamageDefOf.Bite, 9999f));
|
||||
}
|
||||
|
||||
Corpse corpse = pawn.Corpse;
|
||||
if (corpse != null && !corpse.Spawned && !corpse.Destroyed)
|
||||
{
|
||||
corpses.Add(corpse);
|
||||
}
|
||||
}
|
||||
|
||||
if (interceptedPawns.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IntVec3 dropCenter = parms.spawnCenter.IsValid ? parms.spawnCenter : map.Center;
|
||||
if (corpses.Count > 0)
|
||||
{
|
||||
DropPodUtility.DropThingsNear(dropCenter, map, corpses, leaveSlag: true);
|
||||
}
|
||||
|
||||
SpawnInterceptionFlyOver(map, dropCenter);
|
||||
SendInterceptionLetter(map, interceptedPawns.Count, dropCenter);
|
||||
|
||||
ArachnaeLog.Debug($"DropPodInterceptor: intercepted {interceptedPawns.Count} raid pawns.");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SpawnInterceptionFlyOver(Map map, IntVec3 dropCenter)
|
||||
{
|
||||
ThingDef flyOverDef = DefDatabase<ThingDef>.GetNamedSilentFail(DefaultInterceptFlyOverDefName);
|
||||
if (flyOverDef == null)
|
||||
{
|
||||
ArachnaeLog.Debug($"DropPodInterceptor: missing fly over def {DefaultInterceptFlyOverDefName}.");
|
||||
return;
|
||||
}
|
||||
|
||||
IntVec3 start = GetRandomMapEdgeCell(map);
|
||||
IntVec3 end = dropCenter.IsValid && dropCenter.InBounds(map) ? dropCenter : map.Center;
|
||||
|
||||
FlyOver.MakeFlyOver(flyOverDef, start, end, map, speed: 5f, height: 12f);
|
||||
}
|
||||
|
||||
private static IntVec3 GetRandomMapEdgeCell(Map map)
|
||||
{
|
||||
int edge = Rand.RangeInclusive(0, 3);
|
||||
switch (edge)
|
||||
{
|
||||
case 0:
|
||||
return new IntVec3(Rand.RangeInclusive(0, map.Size.x - 1), 0, 0);
|
||||
case 1:
|
||||
return new IntVec3(map.Size.x - 1, 0, Rand.RangeInclusive(0, map.Size.z - 1));
|
||||
case 2:
|
||||
return new IntVec3(Rand.RangeInclusive(0, map.Size.x - 1), 0, map.Size.z - 1);
|
||||
default:
|
||||
return new IntVec3(0, 0, Rand.RangeInclusive(0, map.Size.z - 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void SendInterceptionLetter(Map map, int interceptedCount, IntVec3 dropCenter)
|
||||
{
|
||||
string label = "ARA_InterceptDropPod_LetterLabel".Translate();
|
||||
string text = "ARA_InterceptDropPod_LetterText".Translate(interceptedCount);
|
||||
|
||||
Find.LetterStack.ReceiveLetter(
|
||||
label,
|
||||
text,
|
||||
LetterDefOf.PositiveEvent,
|
||||
new TargetInfo(dropCenter, map));
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref interceptEnabled, "interceptEnabled", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[HarmonyPatch(typeof(PawnsArrivalModeWorker_EdgeDrop), nameof(PawnsArrivalModeWorker_EdgeDrop.Arrive))]
|
||||
public static class Patch_DropPodIntercept_EdgeDrop
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(List<Pawn> pawns, IncidentParms parms)
|
||||
{
|
||||
return Patch_DropPodIntercept.InterceptPrefix(pawns, parms);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(PawnsArrivalModeWorker_CenterDrop), nameof(PawnsArrivalModeWorker_CenterDrop.Arrive))]
|
||||
public static class Patch_DropPodIntercept_CenterDrop
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(List<Pawn> pawns, IncidentParms parms)
|
||||
{
|
||||
return Patch_DropPodIntercept.InterceptPrefix(pawns, parms);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Patch_DropPodIntercept
|
||||
{
|
||||
public static bool InterceptPrefix(List<Pawn> pawns, IncidentParms parms)
|
||||
{
|
||||
try
|
||||
{
|
||||
GameComponent_DropPodInterceptor interceptor = Current.Game?.GetComponent<GameComponent_DropPodInterceptor>();
|
||||
interceptor?.TryInterceptDropPods(pawns, parms, out _);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ArachnaeLog.Debug($"DropPodInterceptor prefix exception: {ex}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user