Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/BuildingComp/WULA_TransformAtFullCapacity/TransformValidationUtility.cs
2025-11-27 17:14:53 +08:00

219 lines
7.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Verse;
using System.Linq;
namespace WulaFallenEmpire
{
public static class TransformValidationUtility
{
/// <summary>
/// 检查位置是否可以放置目标建筑(通用版本)
/// </summary>
public static bool CanPlaceBuildingAt(ThingDef buildingDef, IntVec3 center, Map map, Faction faction, out string failReason)
{
return CanPlaceBuildingAt(buildingDef, center, map, faction, null, out failReason);
}
/// <summary>
/// 检查位置是否可以放置目标建筑(带忽略物体版本)
/// </summary>
public static bool CanPlaceBuildingAt(ThingDef buildingDef, IntVec3 center, Map map, Faction faction, Thing thingToIgnore, out string failReason)
{
failReason = null;
// 1. 检查建筑定义
if (buildingDef == null)
{
failReason = "目标建筑定义为空";
return false;
}
// 2. 检查建筑尺寸是否为奇数(根据您的需求)
if (!IsOddSizedBuilding(buildingDef))
{
failReason = $"建筑 {buildingDef.LabelCap} 的尺寸不是奇数,不符合放置要求";
return false;
}
// 3. 检查地图边界
if (!IsWithinMapBounds(buildingDef, center, map, out failReason))
return false;
// 4. 检查地形affordance
if (!HasValidTerrainAffordance(buildingDef, center, map, out failReason))
return false;
// 5. 检查其他建筑挤占(排除要忽略的物体)
if (!IsAreaClearOfBlockingBuildings(buildingDef, center, map, thingToIgnore, out failReason))
return false;
// 6. 检查特殊放置条件(如不可放置在水上等)
if (!MeetsSpecialPlacementConditions(buildingDef, center, map, out failReason))
return false;
// 7. 使用RimWorld原生的放置检查最终验证
if (!GenConstruct.CanPlaceBlueprintAt(buildingDef, center, Rot4.North, map, false, null, null, null))
{
failReason = "该位置不符合建筑放置要求";
return false;
}
return true;
}
/// <summary>
/// 检查建筑尺寸是否为奇数
/// </summary>
public static bool IsOddSizedBuilding(ThingDef buildingDef)
{
return buildingDef.Size.x % 2 == 1 && buildingDef.Size.z % 2 == 1;
}
/// <summary>
/// 检查地图边界
/// </summary>
private static bool IsWithinMapBounds(ThingDef buildingDef, IntVec3 center, Map map, out string failReason)
{
CellRect occupiedRect = GenAdj.OccupiedRect(center, Rot4.North, buildingDef.Size);
if (!occupiedRect.InBounds(map))
{
failReason = "建筑超出地图边界";
return false;
}
failReason = null;
return true;
}
/// <summary>
/// 检查地形affordance
/// </summary>
private static bool HasValidTerrainAffordance(ThingDef buildingDef, IntVec3 center, Map map, out string failReason)
{
TerrainAffordanceDef requiredAffordance = buildingDef.terrainAffordanceNeeded;
if (requiredAffordance == null)
{
// 如果没有指定affordance要求则跳过检查
failReason = null;
return true;
}
CellRect occupiedRect = GenAdj.OccupiedRect(center, Rot4.North, buildingDef.Size);
foreach (IntVec3 cell in occupiedRect)
{
if (!cell.InBounds(map))
continue;
TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
if (terrain == null || !terrain.affordances.Contains(requiredAffordance))
{
failReason = $"地形 {terrain?.LabelCap ?? ""} 不支持放置 {buildingDef.LabelCap}";
return false;
}
}
failReason = null;
return true;
}
/// <summary>
/// 检查区域是否被其他建筑阻挡(排除指定物体)
/// </summary>
private static bool IsAreaClearOfBlockingBuildings(ThingDef buildingDef, IntVec3 center, Map map, Thing thingToIgnore, out string failReason)
{
CellRect occupiedRect = GenAdj.OccupiedRect(center, Rot4.North, buildingDef.Size);
List<Thing> blockingThings = new List<Thing>();
foreach (IntVec3 cell in occupiedRect)
{
if (!cell.InBounds(map))
continue;
// 检查该单元格上的所有建筑
List<Thing> thingList = map.thingGrid.ThingsListAt(cell);
foreach (Thing thing in thingList)
{
// 跳过要忽略的物体如被转换的Pawn本身
if (thing == thingToIgnore)
continue;
if (thing.def.category == ThingCategory.Building || thing.def.category == ThingCategory.Pawn)
{
// 忽略蓝图和框架
if (thing.def.IsBlueprint || thing.def.IsFrame)
continue;
// 忽略被转换的Pawn本身
if (thing == thingToIgnore)
continue;
blockingThings.Add(thing);
}
}
}
if (blockingThings.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("以下物体阻挡了建筑放置:");
foreach (Thing thing in blockingThings)
{
sb.AppendLine($" - {thing.LabelCap}");
}
failReason = sb.ToString().TrimEnd();
return false;
}
failReason = null;
return true;
}
/// <summary>
/// 检查特殊放置条件
/// </summary>
private static bool MeetsSpecialPlacementConditions(ThingDef buildingDef, IntVec3 center, Map map, out string failReason)
{
CellRect occupiedRect = GenAdj.OccupiedRect(center, Rot4.North, buildingDef.Size);
// 检查是否在水体上
foreach (IntVec3 cell in occupiedRect)
{
if (!cell.InBounds(map))
continue;
TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
if (terrain != null && (terrain.IsWater || terrain.defName.Contains("Water")))
{
failReason = "无法在水体上放置建筑";
return false;
}
// 检查是否在不可建造的地形上
if (terrain != null && !terrain.BuildableByPlayer)
{
failReason = $"地形 {terrain.LabelCap} 不可建造";
return false;
}
}
failReason = null;
return true;
}
/// <summary>
/// 获取建筑占用的所有单元格
/// </summary>
public static List<IntVec3> GetOccupiedCells(ThingDef buildingDef, IntVec3 center)
{
return GenAdj.OccupiedRect(center, Rot4.North, buildingDef.Size).Cells.ToList();
}
}
}