This commit is contained in:
Tourswen
2025-11-13 12:22:57 +08:00
4 changed files with 453 additions and 1 deletions

View File

@@ -2090,7 +2090,7 @@
</li>
</modExtensions>
</ThingDef>
<AbilityDef>
<!-- <AbilityDef>
<defName>WULA_RW_Auto_GL_Smoke_Ability</defName>
<label>发射烟雾弹</label>
<description>使用SEl-78"角砾岩"临时装填并发射一枚烟雾弹,用于遮蔽炮塔的瞄准和防止己方被流弹击中。</description>
@@ -2124,5 +2124,46 @@
<projectileDef>Bullet_Shell_Smoke</projectileDef>
</li>
</comps>
</AbilityDef> -->
<AbilityDef>
<defName>WULA_RW_Auto_GL_Smoke_Ability</defName>
<label>律令死亡</label>
<description>使得目标立刻被放逐——无视一切保护措施,将其从存档中永远擦除。</description>
<iconPath>Wula/UI/Abilities/WULA_RW_Auto_GL_Smoke_Ability</iconPath>
<writeCombatLog>True</writeCombatLog>
<showPsycastEffects>False</showPsycastEffects>
<hotKey>Misc11</hotKey>
<showOnCharacterCard>false</showOnCharacterCard>
<charges>3</charges>
<cooldownPerCharge>true</cooldownPerCharge>
<cooldownTicksRange>1</cooldownTicksRange>
<statBases>
<Ability_EffectRadius>35</Ability_EffectRadius>
</statBases>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<range>500</range>
<warmupTime>0.25</warmupTime>
<muzzleFlashScale>9</muzzleFlashScale>
<ai_IsWeapon>true</ai_IsWeapon>
<soundCast>Pawn_Mech_Apocriton_Death</soundCast>
<requireLineOfSight>false</requireLineOfSight>
<targetParams>
<canTargetLocations>true</canTargetLocations>
<canTargetSelf>false</canTargetSelf>
<canTargetPawns>true</canTargetPawns>
<canTargetBuildings>true</canTargetBuildings>
<canTargetPlants>true</canTargetPlants>
</targetParams>
</verbProperties>
<comps>
<li Class="WulaFallenEmpire.CompProperties_AbilityDeleteTarget">
<affectEverything>true</affectEverything>
<requireLineOfSight>false</requireLineOfSight>
<!-- <showEffect>true</showEffect>
<soundEffect>EnergyWeapon_Discharge</soundEffect> -->
</li>
</comps>
</AbilityDef>
</Defs>

View File

@@ -0,0 +1,381 @@
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using Verse.Sound;
using System.Reflection;
namespace WulaFallenEmpire
{
public class CompAbilityEffect_DeleteTarget : CompAbilityEffect
{
public new CompProperties_AbilityDeleteTarget Props => (CompProperties_AbilityDeleteTarget)props;
// 使用反射访问私有字段
private static FieldInfo thingIDField = typeof(Thing).GetField("thingIDNumber", BindingFlags.Instance | BindingFlags.NonPublic);
private static FieldInfo mapIndexField = typeof(Thing).GetField("mapIndexOrState", BindingFlags.Instance | BindingFlags.NonPublic);
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
if (parent.pawn == null || !target.IsValid)
return;
// 尝试删除任何东西,包括地形以外的所有对象
TryDeleteEverythingAt(target.Cell);
}
private void TryDeleteEverythingAt(IntVec3 cell)
{
Map map = parent.pawn.Map;
if (map == null)
return;
bool deletedSomething = false;
int deletionCount = 0;
try
{
// 获取该位置的所有物体(创建副本,因为我们要修改集合)
List<Thing> thingsAtCell = new List<Thing>(map.thingGrid.ThingsAt(cell));
foreach (Thing thing in thingsAtCell)
{
if (thing != null && CanAffectTarget(new LocalTargetInfo(thing)))
{
if (ForceRemoveThing(thing, map))
{
deletedSomething = true;
deletionCount++;
}
}
}
// 显示效果
if (deletedSomething && Props.showEffect)
{
ShowDeleteEffect(cell);
}
// 播放音效
if (deletedSomething && Props.soundEffect != null)
{
Props.soundEffect.PlayOneShot(new TargetInfo(cell, map));
}
Log.Message($"[DeleteTarget] Processed cell {cell}, deleted {deletionCount} objects");
}
catch (System.Exception ex)
{
Log.Error($"[DeleteTarget] Error deleting objects at {cell}: {ex}");
}
}
private bool CanAffectTarget(LocalTargetInfo target)
{
Thing thing = target.Thing;
if (thing == null)
return false;
// 调试模式如果设置了affectEverything忽略所有过滤
if (Props.affectEverything)
return true;
// 根据类型过滤
if (thing is Building && !Props.affectBuildings)
return false;
if (thing is Pawn && !Props.affectPawns)
return false;
if (thing is Plant && !Props.affectPlants)
return false;
if (thing.def.EverHaulable && !Props.affectItems)
return false;
if (thing is Filth && !Props.affectFilth)
return false;
if (thing is Blueprint && !Props.affectBlueprints)
return false;
if (thing is Frame && !Props.affectFrames)
return false;
if (thing is Corpse && !Props.affectCorpses)
return false;
if (thing is Building_Trap && !Props.affectMines)
return false;
return true;
}
private bool ForceRemoveThing(Thing thing, Map map)
{
string thingInfo = $"{thing.Label} ({thing.def.defName}) at {thing.Position}";
try
{
// 方法1: 尝试使用 DeSpawn
if (thing.Spawned)
{
thing.DeSpawn(DestroyMode.Vanish);
Log.Message($"[DeleteTarget] Method1 - DeSpawn: {thingInfo}");
return true;
}
}
catch (System.Exception ex1)
{
Log.Warning($"[DeleteTarget] Method1 failed for {thingInfo}: {ex1}");
}
try
{
// 方法2: 直接操作 thingGrid使用反射
if (thing.Spawned)
{
ForceRemoveFromThingGrid(thing, map);
Log.Message($"[DeleteTarget] Method2 - ForceRemoveFromThingGrid: {thingInfo}");
return true;
}
}
catch (System.Exception ex2)
{
Log.Warning($"[DeleteTarget] Method2 failed for {thingInfo}: {ex2}");
}
try
{
// 方法3: 使用反射设置内部状态
if (thing.Spawned)
{
ForceDespawnViaReflection(thing, map);
Log.Message($"[DeleteTarget] Method3 - ForceDespawnViaReflection: {thingInfo}");
return true;
}
}
catch (System.Exception ex3)
{
Log.Warning($"[DeleteTarget] Method3 failed for {thingInfo}: {ex3}");
}
try
{
// 方法4: 最后的尝试 - 直接调用内部清理方法
if (thing.Spawned)
{
CallInternalCleanup(thing, map);
Log.Message($"[DeleteTarget] Method4 - CallInternalCleanup: {thingInfo}");
return true;
}
}
catch (System.Exception ex4)
{
Log.Warning($"[DeleteTarget] Method4 failed for {thingInfo}: {ex4}");
}
Log.Error($"[DeleteTarget] All methods failed for: {thingInfo}");
return false;
}
private void ForceRemoveFromThingGrid(Thing thing, Map map)
{
try
{
// 使用反射调用 thingGrid 的内部移除方法
MethodInfo deregisterMethod = typeof(ThingGrid).GetMethod("Deregister", BindingFlags.Instance | BindingFlags.NonPublic);
if (deregisterMethod != null)
{
deregisterMethod.Invoke(map.thingGrid, new object[] { thing });
}
else
{
// 备用方法:手动从所有相关格子中移除
ManualRemoveFromGrid(thing, map);
}
}
catch (System.Exception ex)
{
Log.Warning($"[DeleteTarget] ForceRemoveFromThingGrid failed: {ex}");
throw;
}
}
private void ManualRemoveFromGrid(Thing thing, Map map)
{
// 对于多格物体
if (thing.def.size != IntVec2.One)
{
foreach (IntVec3 cell in thing.OccupiedRect())
{
RemoveThingFromCell(thing, cell, map);
}
}
else
{
// 对于单格物体
RemoveThingFromCell(thing, thing.Position, map);
}
}
private void RemoveThingFromCell(Thing thing, IntVec3 cell, Map map)
{
try
{
// 使用反射访问 thingGrid 的 grid 字段
FieldInfo gridField = typeof(ThingGrid).GetField("grid", BindingFlags.Instance | BindingFlags.NonPublic);
if (gridField != null)
{
var grid = gridField.GetValue(map.thingGrid) as List<Thing>[];
if (grid != null && cell.InBounds(map))
{
int index = map.cellIndices.CellToIndex(cell);
if (index >= 0 && index < grid.Length)
{
grid[index]?.Remove(thing);
}
}
}
}
catch (System.Exception ex)
{
Log.Warning($"[DeleteTarget] RemoveThingFromCell failed: {ex}");
}
}
private void ForceDespawnViaReflection(Thing thing, Map map)
{
try
{
// 设置 mapIndexOrState 为 -1未生成状态
if (mapIndexField != null)
{
mapIndexField.SetValue(thing, -1);
}
// 设置 spawned 为 false
FieldInfo spawnedField = typeof(Thing).GetField("spawned", BindingFlags.Instance | BindingFlags.NonPublic);
if (spawnedField != null)
{
spawnedField.SetValue(thing, false);
}
// 设置 destroyed 为 true
FieldInfo destroyedField = typeof(Thing).GetField("destroyed", BindingFlags.Instance | BindingFlags.NonPublic);
if (destroyedField != null)
{
destroyedField.SetValue(thing, true);
}
// 从地图列表中移除
MethodInfo notifyMethod = typeof(Map).GetMethod("Notify_ThingDespawned", BindingFlags.Instance | BindingFlags.NonPublic);
if (notifyMethod != null)
{
notifyMethod.Invoke(map, new object[] { thing });
}
}
catch (System.Exception ex)
{
Log.Warning($"[DeleteTarget] ForceDespawnViaReflection failed: {ex}");
throw;
}
}
private void CallInternalCleanup(Thing thing, Map map)
{
try
{
// 调用 DeSpawn 方法的不同重载
MethodInfo despawnMethod = typeof(Thing).GetMethod("DeSpawn", new System.Type[] { });
if (despawnMethod != null)
{
despawnMethod.Invoke(thing, null);
}
// 确保从所有管理器中移除 - 修复版本
if (thing is Pawn pawn)
{
map.mapPawns.DeRegisterPawn(pawn); // 使用 DeRegisterPawn 而不是 RemovePawn
}
// 从动态绘制列表中移除
MethodInfo notifyDespawnedMethod = typeof(Map).GetMethod("Notify_ThingDespawned", BindingFlags.Instance | BindingFlags.NonPublic);
if (notifyDespawnedMethod != null)
{
notifyDespawnedMethod.Invoke(map, new object[] { thing });
}
}
catch (System.Exception ex)
{
Log.Warning($"[DeleteTarget] CallInternalCleanup failed: {ex}");
throw;
}
}
private void ShowDeleteEffect(IntVec3 cell)
{
Map map = parent.pawn.Map;
// 使用自定义效果或默认效果
if (Props.effectFleck != null)
{
FleckMaker.Static(cell, map, Props.effectFleck);
}
else
{
// 默认效果:一个明显的闪光效果
FleckMaker.Static(cell, map, FleckDefOf.PsycastAreaEffect);
FleckMaker.ThrowLightningGlow(cell.ToVector3Shifted(), map, 2f);
// 添加更多效果使其更明显
for (int i = 0; i < 3; i++)
{
FleckMaker.ThrowSmoke(cell.ToVector3Shifted() + new Vector3(Rand.Range(-0.5f, 0.5f), 0, Rand.Range(-0.5f, 0.5f)), map, 1.5f);
}
}
}
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
if (!base.Valid(target, throwMessages))
return false;
// 调试模式:任何位置都有效
return true;
}
public override string ExtraLabelMouseAttachment(LocalTargetInfo target)
{
return "强制擦除模式: 删除所有对象(包括不可销毁的)";
}
// 绘制预览效果 - 显示大范围区域
public override void DrawEffectPreview(LocalTargetInfo target)
{
base.DrawEffectPreview(target);
if (parent.pawn == null || parent.pawn.Map == null || !target.IsValid)
return;
try
{
// 绘制3x3的预览区域
CellRect previewRect = CellRect.CenteredOn(target.Cell, 1);
foreach (IntVec3 cell in previewRect)
{
if (cell.InBounds(parent.pawn.Map))
{
GenDraw.DrawFieldEdges(new List<IntVec3> { cell }, Color.red, 0.8f);
}
}
}
catch (System.Exception)
{
// 忽略预览绘制错误
}
}
}
}

View File

@@ -0,0 +1,28 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_AbilityDeleteTarget : CompProperties_AbilityEffect
{
public bool affectBuildings = true;
public bool affectPawns = true;
public bool affectItems = true;
public bool affectPlants = true;
public bool affectFilth = true;
public bool affectBlueprints = true;
public bool affectFrames = true;
public bool affectCorpses = true;
public bool affectMines = true;
public bool affectEverything = true; // 覆盖所有其他设置
public bool requireLineOfSight = false; // 调试模式不需要视线
public bool showEffect = true;
public FleckDef effectFleck;
public SoundDef soundEffect;
public CompProperties_AbilityDeleteTarget()
{
this.compClass = typeof(CompAbilityEffect_DeleteTarget);
}
}
}

View File

@@ -78,6 +78,8 @@
<Compile Include="Ability\WULA_AbilityBombardment\CompProperties_AbilityBombardment.cs" />
<Compile Include="Ability\WULA_AbilityCircularBombardment\CompAbilityEffect_CircularBombardment.cs" />
<Compile Include="Ability\WULA_AbilityCircularBombardment\CompProperties_AbilityCircularBombardment.cs" />
<Compile Include="Ability\WULA_AbilityDeleteTarget\CompAbilityEffect_DeleteTarget.cs" />
<Compile Include="Ability\WULA_AbilityDeleteTarget\CompProperties_AbilityDeleteTarget.cs" />
<Compile Include="Ability\WULA_AbilityEnergyLance\AbilityWeaponDefExtension.cs" />
<Compile Include="Ability\WULA_AbilityEnergyLance\CompAbilityEffect_EnergyLance.cs" />
<Compile Include="Ability\WULA_AbilityEnergyLance\CompProperties_AbilityEnergyLance.cs" />