1
This commit is contained in:
@@ -359,8 +359,6 @@ namespace WulaFallenEmpire
|
||||
return Mathf.Max(0, remainingTicks / 2500f);
|
||||
}
|
||||
|
||||
// 开发模式方法:立即结束冷却
|
||||
[DebugAction("机械族回收器", "立即结束冷却", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.Playing)]
|
||||
public static void DevEndCooldown()
|
||||
{
|
||||
Building_MechanoidRecycler selectedRecycler = Find.Selector.SingleSelectedThing as Building_MechanoidRecycler;
|
||||
|
||||
@@ -2,6 +2,7 @@ using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
@@ -75,31 +76,96 @@ namespace WulaFallenEmpire
|
||||
|
||||
ConsumeMaterials();
|
||||
|
||||
Thing thing = ThingMaker.MakeThing(Props.skyfallerDef);
|
||||
if (thing is Skyfaller_PrefabSpawner skyfaller)
|
||||
{
|
||||
skyfaller.prefabDefName = PropsPrefab.prefabDefName;
|
||||
GenSpawn.Spawn(skyfaller, parent.Position, parent.Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"[PrefabSkyfallerCaller] Failed to create Skyfaller_PrefabSpawner. Created thing is of type {thing.GetType().FullName}. Def: {Props.skyfallerDef.defName}, ThingClass: {Props.skyfallerDef.thingClass.FullName}");
|
||||
// Fallback: spawn as normal skyfaller if possible, or just abort
|
||||
if (thing is Skyfaller normalSkyfaller)
|
||||
{
|
||||
GenSpawn.Spawn(normalSkyfaller, parent.Position, parent.Map);
|
||||
}
|
||||
}
|
||||
|
||||
if (PropsPrefab.destroyBuilding && !parent.Destroyed)
|
||||
{
|
||||
parent.Destroy(DestroyMode.Vanish);
|
||||
}
|
||||
SpawnPrefabSkyfaller();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ExecuteSkyfallerCall();
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:重写自动呼叫方法
|
||||
protected override void ExecuteAutoSkyfallerCall()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PropsPrefab.prefabDefName))
|
||||
{
|
||||
Log.Message($"[PrefabSkyfallerCaller] Executing auto skyfaller call for prefab at {parent.Position}");
|
||||
|
||||
// 非玩家派系自动呼叫不需要资源检查
|
||||
HandleRoofDestruction();
|
||||
|
||||
SpawnPrefabSkyfaller();
|
||||
|
||||
ResetCall();
|
||||
autoCallScheduled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ExecuteAutoSkyfallerCall();
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:统一的 Prefab Skyfaller 生成方法
|
||||
private void SpawnPrefabSkyfaller()
|
||||
{
|
||||
Thing thing = ThingMaker.MakeThing(Props.skyfallerDef);
|
||||
if (thing is Skyfaller_PrefabSpawner skyfaller)
|
||||
{
|
||||
skyfaller.prefabDefName = PropsPrefab.prefabDefName;
|
||||
Log.Message($"[PrefabSkyfallerCaller] Setting prefabDefName to: {PropsPrefab.prefabDefName}");
|
||||
GenSpawn.Spawn(skyfaller, parent.Position, parent.Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"[PrefabSkyfallerCaller] Failed to create Skyfaller_PrefabSpawner. Created thing is of type {thing.GetType().FullName}. Def: {Props.skyfallerDef.defName}, ThingClass: {Props.skyfallerDef.thingClass.FullName}");
|
||||
// Fallback: spawn as normal skyfaller if possible, or just abort
|
||||
if (thing is Skyfaller normalSkyfaller)
|
||||
{
|
||||
GenSpawn.Spawn(normalSkyfaller, parent.Position, parent.Map);
|
||||
}
|
||||
}
|
||||
|
||||
if (PropsPrefab.destroyBuilding && !parent.Destroyed)
|
||||
{
|
||||
parent.Destroy(DestroyMode.Vanish);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:重写屋顶处理方法以确保日志输出
|
||||
private void HandleRoofDestruction()
|
||||
{
|
||||
if (parent?.Map == null) return;
|
||||
|
||||
IntVec3 targetPos = parent.Position;
|
||||
RoofDef roof = targetPos.GetRoof(parent.Map);
|
||||
|
||||
if (roof != null && !roof.isThickRoof && Props.allowThinRoof)
|
||||
{
|
||||
Log.Message($"[PrefabSkyfallerCaller] Destroying thin roof at {targetPos}");
|
||||
parent.Map.roofGrid.SetRoof(targetPos, null);
|
||||
|
||||
// 生成屋顶破坏效果
|
||||
FleckMaker.ThrowDustPuffThick(targetPos.ToVector3Shifted(), parent.Map, 2f, new Color(1f, 1f, 1f, 2f));
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:调试信息
|
||||
public override string CompInspectStringExtra()
|
||||
{
|
||||
var baseString = base.CompInspectStringExtra();
|
||||
|
||||
if (!string.IsNullOrEmpty(PropsPrefab.prefabDefName))
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
if (!string.IsNullOrEmpty(baseString))
|
||||
{
|
||||
sb.AppendLine(baseString);
|
||||
}
|
||||
sb.Append($"Prefab: {PropsPrefab.prefabDefName}");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
return baseString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace WulaFallenEmpire
|
||||
public int delayTicks = 0;
|
||||
|
||||
public bool canAutoCall = true; // 默认启用自动召唤
|
||||
public int autoCallDelayTicks = 600; // 默认10秒
|
||||
public int autoCallDelayTicks = 1; // 默认10秒
|
||||
|
||||
// 新增:是否需要 FlyOver 作为前提条件
|
||||
public bool requireFlyOver = false; // 默认不需要 FlyOver
|
||||
|
||||
@@ -41,10 +41,17 @@ namespace WulaFallenEmpire
|
||||
private bool used = false;
|
||||
private int callTick = -1;
|
||||
private bool calling = false;
|
||||
private bool usedGlobalStorage = false; // 新增:标记是否使用了全局储存器
|
||||
private bool usedGlobalStorage = false;
|
||||
public bool autoCallScheduled = false; // 新增:标记是否已安排自动呼叫
|
||||
|
||||
public bool CanCall => !used && !calling;
|
||||
|
||||
// 新增:检查建筑是否属于非玩家派系
|
||||
public bool IsNonPlayerFaction => parent.Faction != null && parent.Faction != Faction.OfPlayer;
|
||||
|
||||
// 新增:检查是否应该自动呼叫
|
||||
private bool ShouldAutoCall => IsNonPlayerFaction && Props.canAutoCall && !autoCallScheduled && !used;
|
||||
|
||||
// 固定的显示标签
|
||||
public string RequiredFlyOverLabel => "建筑空投飞行器";
|
||||
|
||||
@@ -127,6 +134,22 @@ namespace WulaFallenEmpire
|
||||
|
||||
public bool CanCallSkyfaller => CanCall && HasRequiredFlyOver && CheckRoofConditions;
|
||||
|
||||
// 新增:在建筑生成时检查是否需要自动呼叫
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
|
||||
if (!respawningAfterLoad && ShouldAutoCall)
|
||||
{
|
||||
// 安排自动呼叫
|
||||
autoCallScheduled = true;
|
||||
callTick = Find.TickManager.TicksGame + Props.autoCallDelayTicks;
|
||||
calling = true;
|
||||
|
||||
Log.Message($"[SkyfallerCaller] Scheduled auto-call for non-player building {parent.Label} at tick {callTick}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
@@ -134,23 +157,95 @@ namespace WulaFallenEmpire
|
||||
Scribe_Values.Look(ref callTick, "callTick", -1);
|
||||
Scribe_Values.Look(ref calling, "calling", false);
|
||||
Scribe_Values.Look(ref usedGlobalStorage, "usedGlobalStorage", false);
|
||||
Scribe_Values.Look(ref autoCallScheduled, "autoCallScheduled", false);
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
|
||||
// 处理自动呼叫
|
||||
if (calling && callTick >= 0 && Find.TickManager.TicksGame >= callTick)
|
||||
{
|
||||
ExecuteSkyfallerCall();
|
||||
if (autoCallScheduled)
|
||||
{
|
||||
// 执行自动呼叫
|
||||
ExecuteAutoSkyfallerCall();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 执行手动呼叫
|
||||
ExecuteSkyfallerCall();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:自动呼叫方法(非玩家派系专用)
|
||||
protected virtual void ExecuteAutoSkyfallerCall()
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Message($"[SkyfallerCaller] Executing auto skyfaller call for non-player building at {parent.Position}");
|
||||
|
||||
if (Props.skyfallerDef == null)
|
||||
{
|
||||
Log.Error("[SkyfallerCaller] Skyfaller def is null!");
|
||||
ResetCall();
|
||||
return;
|
||||
}
|
||||
|
||||
// 非玩家派系自动呼叫不需要资源检查
|
||||
// 直接处理屋顶
|
||||
HandleRoofDestruction();
|
||||
|
||||
// 创建Skyfaller
|
||||
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(Props.skyfallerDef);
|
||||
if (skyfaller == null)
|
||||
{
|
||||
Log.Error("[SkyfallerCaller] Failed to create skyfaller!");
|
||||
ResetCall();
|
||||
return;
|
||||
}
|
||||
|
||||
IntVec3 spawnPos = parent.Position;
|
||||
Log.Message($"[SkyfallerCaller] Spawning auto skyfaller at {spawnPos}");
|
||||
|
||||
GenSpawn.Spawn(skyfaller, spawnPos, parent.Map);
|
||||
|
||||
if (Props.destroyBuilding)
|
||||
{
|
||||
Log.Message($"[SkyfallerCaller] Destroying non-player building {parent.Label}");
|
||||
parent.Destroy(DestroyMode.Vanish);
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
ResetCall();
|
||||
autoCallScheduled = false;
|
||||
|
||||
// 显示自动呼叫消息
|
||||
Messages.Message("WULA_AutoSkyfallerCalled".Translate(parent.Faction.Name),
|
||||
MessageTypeDefOf.NeutralEvent);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"[SkyfallerCaller] Error in ExecuteAutoSkyfallerCall: {ex}");
|
||||
ResetCall();
|
||||
}
|
||||
}
|
||||
|
||||
public void CallSkyfaller(bool isAutoCall = false)
|
||||
{
|
||||
// 新增:非玩家派系不能手动呼叫
|
||||
if (IsNonPlayerFaction && !isAutoCall)
|
||||
{
|
||||
Messages.Message("WULA_NonPlayerCannotCall".Translate(), parent, MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanCallSkyfaller)
|
||||
{
|
||||
// 显示相应的错误消息
|
||||
if (!HasRequiredFlyOver && Props.requireFlyOver) // 只在需要 FlyOver 时才显示此消息
|
||||
if (!HasRequiredFlyOver && Props.requireFlyOver)
|
||||
{
|
||||
Messages.Message("WULA_NoBuildingDropperFlyOver".Translate(), parent, MessageTypeDefOf.RejectInput);
|
||||
}
|
||||
@@ -195,6 +290,7 @@ namespace WulaFallenEmpire
|
||||
used = false;
|
||||
callTick = -1;
|
||||
usedGlobalStorage = false;
|
||||
autoCallScheduled = false;
|
||||
}
|
||||
|
||||
protected virtual void ExecuteSkyfallerCall()
|
||||
@@ -599,6 +695,7 @@ namespace WulaFallenEmpire
|
||||
used = false;
|
||||
callTick = -1;
|
||||
usedGlobalStorage = false;
|
||||
autoCallScheduled = false;
|
||||
Messages.Message("WULA_SkyfallerCallCancelled".Translate(), parent, MessageTypeDefOf.NeutralEvent);
|
||||
}
|
||||
|
||||
@@ -607,6 +704,10 @@ namespace WulaFallenEmpire
|
||||
foreach (var gizmo in base.CompGetGizmosExtra())
|
||||
yield return gizmo;
|
||||
|
||||
// 新增:非玩家派系不显示呼叫按钮
|
||||
if (IsNonPlayerFaction)
|
||||
yield break;
|
||||
|
||||
if (calling)
|
||||
{
|
||||
Command_Action cancelCommand = new Command_Action
|
||||
@@ -679,6 +780,12 @@ namespace WulaFallenEmpire
|
||||
|
||||
private string GetDisabledReason()
|
||||
{
|
||||
// 新增:非玩家派系不能手动呼叫
|
||||
if (IsNonPlayerFaction)
|
||||
{
|
||||
return "WULA_NonPlayerCannotCall".Translate();
|
||||
}
|
||||
|
||||
// 只在需要 FlyOver 时检查并显示相关原因
|
||||
if (Props.requireFlyOver && !HasRequiredFlyOver)
|
||||
{
|
||||
@@ -719,7 +826,12 @@ namespace WulaFallenEmpire
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
|
||||
if (calling)
|
||||
// 新增:显示自动呼叫状态
|
||||
if (autoCallScheduled && calling)
|
||||
{
|
||||
int ticksLeft = callTick - Find.TickManager.TicksGame;
|
||||
}
|
||||
else if (calling)
|
||||
{
|
||||
int ticksLeft = callTick - Find.TickManager.TicksGame;
|
||||
if (ticksLeft > 0)
|
||||
@@ -732,7 +844,15 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
else if (!used)
|
||||
{
|
||||
sb.Append("WULA_ReadyToCallSkyfaller".Translate());
|
||||
// 新增:显示非玩家派系自动呼叫信息
|
||||
if (IsNonPlayerFaction && Props.canAutoCall)
|
||||
{
|
||||
sb.Append("WULA_AutoSkyfallerReady".Translate());
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("WULA_ReadyToCallSkyfaller".Translate());
|
||||
}
|
||||
|
||||
if (Props.requireFlyOver && !HasRequiredFlyOver)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using LudeonTK;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class DebugActions_PrefabSkyfallerCaller
|
||||
{
|
||||
[DebugAction("Wula Fallen Empire", "Spawn Prefab Skyfaller Caller (Single)", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.Playing)]
|
||||
public static void SpawnPrefabSkyfallerCallerSingle()
|
||||
{
|
||||
var eligibleDefs = DefDatabase<ThingDef>.AllDefs
|
||||
.Where(def => def.comps != null && def.comps.Any(comp => comp is CompProperties_PrefabSkyfallerCaller))
|
||||
.ToList();
|
||||
|
||||
if (!eligibleDefs.Any())
|
||||
{
|
||||
Log.Warning("[Debug] No ThingDefs found with CompProperties_PrefabSkyfallerCaller");
|
||||
return;
|
||||
}
|
||||
|
||||
var options = new List<DebugMenuOption>();
|
||||
foreach (var thingDef in eligibleDefs)
|
||||
{
|
||||
options.Add(new DebugMenuOption(thingDef.defName, DebugMenuOptionMode.Tool, () =>
|
||||
{
|
||||
ShowFactionSelectionMenu(thingDef, false);
|
||||
}));
|
||||
}
|
||||
|
||||
Find.WindowStack.Add(new Dialog_DebugOptionListLister(options));
|
||||
}
|
||||
|
||||
[DebugAction("Wula Fallen Empire", "Spawn Prefab Skyfaller Caller (x10)", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.Playing)]
|
||||
public static void SpawnPrefabSkyfallerCallerMultiple()
|
||||
{
|
||||
var eligibleDefs = DefDatabase<ThingDef>.AllDefs
|
||||
.Where(def => def.comps != null && def.comps.Any(comp => comp is CompProperties_PrefabSkyfallerCaller))
|
||||
.ToList();
|
||||
|
||||
if (!eligibleDefs.Any())
|
||||
{
|
||||
Log.Warning("[Debug] No ThingDefs found with CompProperties_PrefabSkyfallerCaller");
|
||||
return;
|
||||
}
|
||||
|
||||
var options = new List<DebugMenuOption>();
|
||||
foreach (var thingDef in eligibleDefs)
|
||||
{
|
||||
options.Add(new DebugMenuOption(thingDef.defName, DebugMenuOptionMode.Tool, () =>
|
||||
{
|
||||
ShowFactionSelectionMenu(thingDef, true);
|
||||
}));
|
||||
}
|
||||
|
||||
Find.WindowStack.Add(new Dialog_DebugOptionListLister(options));
|
||||
}
|
||||
|
||||
private static void ShowFactionSelectionMenu(ThingDef thingDef, bool spawnMultiple)
|
||||
{
|
||||
var allFactions = Find.FactionManager.AllFactions.ToList();
|
||||
var options = new List<DebugMenuOption>();
|
||||
|
||||
options.Add(new DebugMenuOption("No Faction", DebugMenuOptionMode.Tool, () =>
|
||||
{
|
||||
SpawnThingAtValidLocation(thingDef, null, spawnMultiple);
|
||||
}));
|
||||
|
||||
foreach (var faction in allFactions)
|
||||
{
|
||||
options.Add(new DebugMenuOption(faction.Name, DebugMenuOptionMode.Tool, () =>
|
||||
{
|
||||
SpawnThingAtValidLocation(thingDef, faction, spawnMultiple);
|
||||
}));
|
||||
}
|
||||
|
||||
Find.WindowStack.Add(new Dialog_DebugOptionListLister(options));
|
||||
}
|
||||
|
||||
private static void SpawnThingAtValidLocation(ThingDef thingDef, Faction faction, bool spawnMultiple)
|
||||
{
|
||||
var currentMap = Find.CurrentMap;
|
||||
if (currentMap == null)
|
||||
{
|
||||
Log.Warning("[Debug] No current map found");
|
||||
return;
|
||||
}
|
||||
|
||||
int spawnCount = spawnMultiple ? 10 : 1;
|
||||
int successCount = 0;
|
||||
int attempts = 0;
|
||||
const int maxAttempts = 50;
|
||||
|
||||
var compProps = thingDef.comps.OfType<CompProperties_PrefabSkyfallerCaller>().FirstOrDefault();
|
||||
if (compProps == null)
|
||||
{
|
||||
Log.Warning($"[Debug] Could not find CompProperties_PrefabSkyfallerCaller for {thingDef.defName}");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Message($"[Debug] Looking for spawn positions for {thingDef.defName} (Size: {thingDef.Size})");
|
||||
|
||||
for (int i = 0; i < spawnCount && attempts < maxAttempts; i++)
|
||||
{
|
||||
attempts++;
|
||||
IntVec3 spawnPos = FindSpawnPositionForSkyfaller(currentMap, thingDef, compProps);
|
||||
|
||||
if (spawnPos.IsValid)
|
||||
{
|
||||
Thing thing = ThingMaker.MakeThing(thingDef);
|
||||
|
||||
if (faction != null)
|
||||
{
|
||||
thing.SetFaction(faction);
|
||||
}
|
||||
|
||||
GenSpawn.Spawn(thing, spawnPos, currentMap);
|
||||
|
||||
successCount++;
|
||||
Log.Message($"[Debug] Successfully spawned {thingDef.defName} at {spawnPos} for faction {faction?.Name ?? "None"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[Debug] Failed to find valid spawn position for {thingDef.defName} (attempt {attempts})");
|
||||
}
|
||||
}
|
||||
|
||||
if (successCount > 0)
|
||||
{
|
||||
Messages.Message($"[Debug] Successfully spawned {successCount} {thingDef.defName}", MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message($"[Debug] Failed to spawn any {thingDef.defName} after {attempts} attempts", MessageTypeDefOf.NegativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private static IntVec3 FindSpawnPositionForSkyfaller(Map map, ThingDef thingDef, CompProperties_SkyfallerCaller compProps)
|
||||
{
|
||||
// 基于Skyfaller实际行为的查找逻辑
|
||||
var potentialCells = new List<IntVec3>();
|
||||
|
||||
// 策略1:首先尝试玩家基地附近的开放区域
|
||||
Log.Message($"[Debug] Searching near base area...");
|
||||
var baseCells = GetOpenAreaCellsNearBase(map, thingDef.Size);
|
||||
foreach (var cell in baseCells)
|
||||
{
|
||||
if (IsValidForSkyfallerDrop(map, cell, thingDef, compProps))
|
||||
{
|
||||
potentialCells.Add(cell);
|
||||
}
|
||||
if (potentialCells.Count > 20) break; // 找到足够位置就停止
|
||||
}
|
||||
|
||||
if (potentialCells.Count > 0)
|
||||
{
|
||||
Log.Message($"[Debug] Found {potentialCells.Count} positions near base");
|
||||
return potentialCells.RandomElement();
|
||||
}
|
||||
|
||||
// 策略2:搜索整个地图的开阔区域
|
||||
Log.Message($"[Debug] Searching open areas...");
|
||||
var openAreas = FindOpenAreas(map, thingDef.Size, 1000);
|
||||
foreach (var cell in openAreas)
|
||||
{
|
||||
if (IsValidForSkyfallerDrop(map, cell, thingDef, compProps))
|
||||
{
|
||||
potentialCells.Add(cell);
|
||||
}
|
||||
if (potentialCells.Count > 10) break;
|
||||
}
|
||||
|
||||
if (potentialCells.Count > 0)
|
||||
{
|
||||
Log.Message($"[Debug] Found {potentialCells.Count} positions in open areas");
|
||||
return potentialCells.RandomElement();
|
||||
}
|
||||
|
||||
// 策略3:使用随机采样
|
||||
Log.Message($"[Debug] Trying random sampling...");
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
IntVec3 randomCell = new IntVec3(
|
||||
Rand.Range(thingDef.Size.x, map.Size.x - thingDef.Size.x),
|
||||
0,
|
||||
Rand.Range(thingDef.Size.z, map.Size.z - thingDef.Size.z)
|
||||
);
|
||||
|
||||
if (randomCell.InBounds(map) && IsValidForSkyfallerDrop(map, randomCell, thingDef, compProps))
|
||||
{
|
||||
potentialCells.Add(randomCell);
|
||||
}
|
||||
if (potentialCells.Count > 5) break;
|
||||
}
|
||||
|
||||
if (potentialCells.Count > 0)
|
||||
{
|
||||
Log.Message($"[Debug] Found {potentialCells.Count} positions via random sampling");
|
||||
return potentialCells.RandomElement();
|
||||
}
|
||||
|
||||
Log.Warning($"[Debug] No valid positions found for {thingDef.defName}");
|
||||
return IntVec3.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于Skyfaller实际行为的有效性检查
|
||||
/// </summary>
|
||||
private static bool IsValidForSkyfallerDrop(Map map, IntVec3 cell, ThingDef thingDef, CompProperties_SkyfallerCaller compProps)
|
||||
{
|
||||
// 1. 检查边界
|
||||
if (!cell.InBounds(map))
|
||||
return false;
|
||||
|
||||
// 2. 检查整个建筑区域
|
||||
CellRect occupiedRect = GenAdj.OccupiedRect(cell, Rot4.North, thingDef.Size);
|
||||
|
||||
foreach (IntVec3 occupiedCell in occupiedRect)
|
||||
{
|
||||
if (!occupiedCell.InBounds(map))
|
||||
return false;
|
||||
|
||||
// 3. 检查厚岩顶 - 绝对不允许
|
||||
RoofDef roof = occupiedCell.GetRoof(map);
|
||||
if (roof != null && roof.isThickRoof)
|
||||
{
|
||||
if (!compProps.allowThickRoof)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 检查水体 - 不允许
|
||||
TerrainDef terrain = occupiedCell.GetTerrain(map);
|
||||
if (terrain != null && terrain.IsWater)
|
||||
return false;
|
||||
|
||||
// 5. 检查建筑 - 不允许(但自然物体如树、石头是允许的)
|
||||
var things = map.thingGrid.ThingsListAtFast(occupiedCell);
|
||||
foreach (var thing in things)
|
||||
{
|
||||
// 允许自然物体(树、石头等),它们会被空投清除
|
||||
if (thing.def.category == ThingCategory.Plant ||
|
||||
thing.def.category == ThingCategory.Item ||
|
||||
thing.def.category == ThingCategory.Filth)
|
||||
{
|
||||
continue; // 这些是可以被清除的
|
||||
}
|
||||
|
||||
// 不允许建筑、蓝图、框架等
|
||||
if (thing.def.category == ThingCategory.Building ||
|
||||
thing.def.IsBlueprint ||
|
||||
thing.def.IsFrame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 不允许其他不可清除的物体
|
||||
if (thing.def.passability == Traversability.Impassable &&
|
||||
thing.def.category != ThingCategory.Plant) // 植物是可清除的
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 检查薄岩顶和普通屋顶的条件
|
||||
if (roof != null && !roof.isThickRoof)
|
||||
{
|
||||
if (!compProps.allowThinRoof)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取基地附近的开阔区域
|
||||
/// </summary>
|
||||
private static IEnumerable<IntVec3> GetOpenAreaCellsNearBase(Map map, IntVec2 size)
|
||||
{
|
||||
var homeArea = map.areaManager.Home;
|
||||
IntVec3 searchCenter;
|
||||
|
||||
if (homeArea != null && homeArea.ActiveCells.Any())
|
||||
{
|
||||
searchCenter = homeArea.ActiveCells.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
searchCenter = new IntVec3(map.Size.x / 2, 0, map.Size.z / 2);
|
||||
}
|
||||
|
||||
// 在基地周围搜索开阔区域
|
||||
int searchRadius = 50;
|
||||
var searchArea = CellRect.CenteredOn(searchCenter, searchRadius);
|
||||
|
||||
// 返回该区域内所有可能的中心点
|
||||
foreach (var cell in searchArea.Cells)
|
||||
{
|
||||
if (!cell.InBounds(map)) continue;
|
||||
|
||||
// 检查这个位置周围是否有足够的开阔空间
|
||||
if (IsAreaMostlyOpen(map, cell, size, 0.8f)) // 80%的区域需要是开阔的
|
||||
{
|
||||
yield return cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找地图上的开阔区域
|
||||
/// </summary>
|
||||
private static IEnumerable<IntVec3> FindOpenAreas(Map map, IntVec2 size, int maxCellsToCheck)
|
||||
{
|
||||
int cellsChecked = 0;
|
||||
|
||||
// 优先检查地图上已知的开阔区域
|
||||
var allCells = map.AllCells.Where(c => c.InBounds(map)).ToList();
|
||||
|
||||
foreach (var cell in allCells)
|
||||
{
|
||||
if (cellsChecked >= maxCellsToCheck) yield break;
|
||||
cellsChecked++;
|
||||
|
||||
// 快速检查:如果这个单元格本身就不适合,跳过
|
||||
if (!cell.InBounds(map) || cell.GetTerrain(map).IsWater)
|
||||
continue;
|
||||
|
||||
// 检查整个区域是否开阔
|
||||
if (IsAreaMostlyOpen(map, cell, size, 0.7f)) // 70%的区域需要是开阔的
|
||||
{
|
||||
yield return cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查区域是否大部分是开阔的(没有建筑,允许自然物体)
|
||||
/// </summary>
|
||||
private static bool IsAreaMostlyOpen(Map map, IntVec3 center, IntVec2 size, float openThreshold)
|
||||
{
|
||||
CellRect area = GenAdj.OccupiedRect(center, Rot4.North, size);
|
||||
int totalCells = area.Area;
|
||||
int openCells = 0;
|
||||
|
||||
foreach (IntVec3 cell in area)
|
||||
{
|
||||
if (!cell.InBounds(map))
|
||||
{
|
||||
continue; // 边界外的单元格不计入
|
||||
}
|
||||
|
||||
// 检查是否有不可清除的建筑
|
||||
bool hasBlockingBuilding = false;
|
||||
var things = map.thingGrid.ThingsListAtFast(cell);
|
||||
foreach (var thing in things)
|
||||
{
|
||||
if (thing.def.category == ThingCategory.Building ||
|
||||
thing.def.IsBlueprint ||
|
||||
thing.def.IsFrame ||
|
||||
(thing.def.passability == Traversability.Impassable &&
|
||||
thing.def.category != ThingCategory.Plant))
|
||||
{
|
||||
hasBlockingBuilding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查水体
|
||||
bool isWater = cell.GetTerrain(map).IsWater;
|
||||
|
||||
if (!hasBlockingBuilding && !isWater)
|
||||
{
|
||||
openCells++;
|
||||
}
|
||||
}
|
||||
|
||||
float openRatio = (float)openCells / totalCells;
|
||||
return openRatio >= openThreshold;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制清除区域(作为最后手段)
|
||||
/// </summary>
|
||||
private static bool TryForceClearAreaForSkyfaller(Map map, IntVec3 center, IntVec2 size)
|
||||
{
|
||||
try
|
||||
{
|
||||
CellRect clearRect = GenAdj.OccupiedRect(center, Rot4.North, size);
|
||||
int clearedCount = 0;
|
||||
|
||||
foreach (IntVec3 cell in clearRect)
|
||||
{
|
||||
if (!cell.InBounds(map)) continue;
|
||||
|
||||
// 清除植物和物品
|
||||
var thingsToRemove = map.thingGrid.ThingsAt(cell)
|
||||
.Where(thing => thing.def.category == ThingCategory.Plant ||
|
||||
thing.def.category == ThingCategory.Item ||
|
||||
thing.def.category == ThingCategory.Filth)
|
||||
.ToList();
|
||||
|
||||
foreach (var thing in thingsToRemove)
|
||||
{
|
||||
thing.Destroy();
|
||||
clearedCount++;
|
||||
}
|
||||
|
||||
// 确保不是水体
|
||||
if (cell.GetTerrain(map).IsWater)
|
||||
{
|
||||
map.terrainGrid.SetTerrain(cell, TerrainDefOf.Soil);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Message($"[Debug] Force cleared {clearedCount} objects for skyfaller drop");
|
||||
return clearedCount > 0;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"[Debug] Error force clearing area: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user