diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 6ad6fb6f..6f03769a 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/1.6/1.6/Defs/QuestScriptDefs/WULA_Attack_Robber_Camp.xml b/1.6/1.6/Defs/QuestScriptDefs/WULA_Attack_Robber_Camp.xml index cfa384da..f97b9cdb 100644 --- a/1.6/1.6/Defs/QuestScriptDefs/WULA_Attack_Robber_Camp.xml +++ b/1.6/1.6/Defs/QuestScriptDefs/WULA_Attack_Robber_Camp.xml @@ -5,25 +5,11 @@ 0 1 true - true + true -
  • questName->The [bandit] [camp]
  • -
  • questName->[bandit] [camp]
  • -
  • questName->[asker_nameDef] and the [camp]
  • -
  • camp->Camp
  • -
  • camp->Outpost
  • -
  • camp->Lair
  • -
  • camp->Encampment
  • -
  • bandit->Bandit
  • -
  • bandit->Raider
  • -
  • bandit->Outlaw
  • -
  • bandit->Desperado
  • -
  • bandit->Fugitive
  • -
  • bandit->Marauder
  • -
  • bandit->Robber
  • -
  • bandit->Brigand
  • +
  • questName->WULA_Attack_Robber_Camp_questName
  • @@ -46,109 +32,44 @@
  • - true + false
  • -
  • - asker - true - false - 0.15 -
  • - -
  • - siteTile - true - 0.5 - -
  • Oasis
  • -
  • Lake
  • -
  • LakeWithIsland
  • -
  • LakeWithIslands
  • -
  • Pond
  • -
  • DryLake
  • -
  • ToxicLake
  • -
  • Wetland
  • -
  • HotSprings
  • -
  • Archipelago
  • -
  • CoastalIsland
  • -
  • Peninsula
  • -
  • Bay
  • -
  • Valley
  • -
  • Cavern
  • -
  • Chasm
  • -
  • Cliffs
  • -
  • Hollow
  • -
  • TerraformingScar
  • -
  • LavaLake
  • -
  • Dunes
  • -
  • AncientHeatVent
  • - - - -
  • - sitePartDefs - siteFaction - -
  • BanditCamp
  • - +
  • + siteFaction + true $asker + true
  • -
  • - $siteTile +
  • + +
  • + WULA_Camp_Captured_By_Robber + 1 +
  • + + 5 + 10 + Things/Building/Natural/Hive/HiveC + Things/Building/Natural/Hive/HiveC + site $siteFaction - $sitePartDefs - sitePartsParams + + 5 + 10 + + -
  • - sitePoints - $sitePartsParams -
  • Util_GetDefaultRewardValueFromPoints - $sitePoints + $points
  • - -
  • - $rewardValue - 1.75 - rewardValue -
  • - -
  • - Util_GenerateSite -
  • - -
  • - $site -
  • - -
  • - $site - true - $(randInt(12,28)*60000) - site.MapGenerated - true - - -
  • - - The bandit camp has packed up and moved on. The quest [resolvedQuestName] has expired. -
  • -
  • - Fail -
  • - - - -
  • site.Destroyed @@ -199,4 +120,138 @@ + + WULA_Camp_Captured_By_Robber + + (44, 1, 44) + true + 0 + +
  • + (21, 0, 24) + +
  • + Robber + true + RobberGroup + MapFaction + DefendBase + MapGeneration + 1000~2000 +
  • + + + + +
  • + WULA_TransportPod + +
  • (24,20,24,20)
  • +
  • (24,23,24,23)
  • + + +
  • + Brazier + Steel + (16,0,22) +
  • +
  • + Bedroll + Cloth + 3 + +
  • (23,14,23,16)
  • + + +
  • + Wula_Fusion_Generators + 29.99023 + (30,0,27) +
  • +
  • + WULA_Wall_Flag_Building + +
  • (16,13,16,13)
  • +
  • (25,30,25,30)
  • +
  • (27,30,27,30)
  • + + +
  • + WULA_Machine_Recharger + (14,0,16) +
  • +
  • + WulaWall + +
  • (12,14,16,14)
  • +
  • (12,15,12,18)
  • +
  • (11,18,11,18)
  • +
  • (10,20,10,21)
  • +
  • (9,21,9,27)
  • +
  • (20,12,22,12)
  • +
  • (22,13,23,13)
  • +
  • (25,13,29,13)
  • +
  • (10,27,11,27)
  • +
  • (11,28,11,29)
  • +
  • (12,29,12,29)
  • +
  • (29,14,30,14)
  • +
  • (30,15,31,16)
  • +
  • (14,32,14,35)
  • +
  • (31,17,31,21)
  • +
  • (33,16,35,16)
  • +
  • (15,35,21,35)
  • +
  • (35,17,35,18)
  • +
  • (21,33,24,33)
  • +
  • (24,31,25,31)
  • +
  • (21,34,21,34)
  • +
  • (24,32,24,32)
  • +
  • (36,18,37,18)
  • +
  • (32,25,34,25)
  • +
  • (27,31,27,32)
  • +
  • (32,26,32,29)
  • +
  • (37,19,37,22)
  • +
  • (34,24,34,24)
  • +
  • (31,29,31,32)
  • +
  • (28,32,30,32)
  • + + +
  • + WulaDoor + +
  • (32,16,32,16)
  • +
  • (26,31,26,31)
  • + + +
  • + Wula_Sonar_Mine + +
  • (10,22,10,22)
  • +
  • (28,14,28,14)
  • +
  • (30,31,30,31)
  • + + +
  • + Bedroll + Cloth + 1 + +
  • (10,24,10,26)
  • + + +
  • + WulaShelter + +
  • (12,6,15,6)
  • +
  • (9,10,12,10)
  • +
  • (12,7,12,9)
  • +
  • (7,32,7,36)
  • +
  • (34,9,38,9)
  • +
  • (8,36,10,36)
  • +
  • (38,10,38,12)
  • +
  • (35,35,38,35)
  • +
  • (38,32,38,34)
  • + + +
    + \ No newline at end of file diff --git a/1.6/1.6/Defs/ResearchProjectDefs/WULA_ResearchProjects_Remake.xml b/1.6/1.6/Defs/ResearchProjectDefs/WULA_ResearchProjects_Remake.xml index beaa8e47..2853448e 100644 --- a/1.6/1.6/Defs/ResearchProjectDefs/WULA_ResearchProjects_Remake.xml +++ b/1.6/1.6/Defs/ResearchProjectDefs/WULA_ResearchProjects_Remake.xml @@ -668,4 +668,17 @@
  • WULA_Colony_License_LV3_Technology
  • + + + + WULA_Building_Teleporter_Technology + 10.00 + 2.70 + + 允许通过传送将乌拉帝国的建筑直接传送到空投区,这使得建筑可以立刻部署到目标地点,且无视厚岩顶阻挡。 + 2000 + +
  • WULA_Colony_License_LV3_Technology
  • +
    +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml index 7eb5e71e..ac5acc06 100644 --- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml +++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml @@ -71,6 +71,14 @@ true false +
  • + WulaWall + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -248,6 +256,14 @@ true false +
  • + WulaDoor + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -467,6 +483,14 @@ true false +
  • + WulaShelter + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -634,6 +658,14 @@ true false +
  • + WULA_MaintenancePod + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -831,6 +863,14 @@ true false +
  • + WULA_WeaponArmor_Productor + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -1062,7 +1102,7 @@ Misc12 0.5 2200 - Medium + Light 250 1600 @@ -1426,6 +1466,14 @@ true false +
  • + WULA_Machine_Recharger + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -1596,6 +1644,14 @@ true false +
  • + WULA_Charging_Station_Synth + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -1761,6 +1817,14 @@ true false +
  • + WULA_Cube_Productor + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -1948,6 +2012,14 @@ true false +
  • + Wula_DarkEnergy_Generators + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -2150,6 +2222,14 @@ true false +
  • + Wula_Fusion_Generators + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml index e6141685..6da19eac 100644 --- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml +++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml @@ -73,6 +73,14 @@ true false +
  • + Wula_Sonar_Mine + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -328,6 +336,14 @@ true false +
  • + WULA_Cat_Bunker + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -588,6 +604,14 @@ true false +
  • + Wula_Base_ATGun_Turret + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -881,6 +905,14 @@ true false +
  • + Wula_Base_Laser_Turret + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • @@ -1176,6 +1208,14 @@ true false +
  • + Wula_Base_Mortar_Turret + true + WULA_Psi_Skip_Entry + VoidStructure_Emerge + true + WULA_Building_Teleporter_Technology +
  • diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Attack_Robber_Camp.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Attack_Robber_Camp.xml index 7d1f4bf4..2b87d3bf 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Attack_Robber_Camp.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Attack_Robber_Camp.xml @@ -1,12 +1,9 @@  -
  • questName->[bandit][camp]
  • -
  • questName->[bandit][camp]
  • -
  • questName->[asker_nameDef]与[camp]
  • +
  • questName->被[bandit]占据的乌拉帝国[camp]
  • camp->营地
  • camp->哨所
  • -
  • camp->窝点
  • camp->据点
  • bandit->土匪
  • bandit->劫掠者
  • @@ -18,6 +15,6 @@
  • bandit->劫匪
  • -
  • questDescription->[asker_faction_name]的[asker_faction_leaderTitle],[asker_nameFull],向我们发来了一条讯息。根据他们掌握的情报,一个属于[siteFaction_name]派系,由[siteFaction_pawnsPlural]建立的营地一直在袭击他们的远行队。\n\n[asker_nameDef]请求我们帮助他们摧毁这个营地,彻底消灭敌人的成员和炮塔。[asker_label]还提醒我们:\n\n[sitePart0_description]。
  • +
  • questDescription->乌拉帝国行星封锁机关的总控AI向殖民地发送了一个请求。[siteFaction_name]的一群战士控制了一个废弃的乌拉帝国空投前哨站。可能是这个前哨站没有按照标准流程启动报废程序,导致其被异族占据,里面有一些科技造物是不能外泄到异族手中的。\n\n这个营地必须被抹去,其中的乌拉帝国科技造物可以回收,也可以就地销毁。现场人数无法评估,仍然有可能有一些未触发的<color=#AA3020><i>弹射地雷</i></color>,但是未发现已经启动的乌拉帝国炮塔。
  • \ No newline at end of file diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Recycle_PIA_Legion_File.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Recycle_PIA_Legion_File.xml index 8ba7d1fe..72ef50a7 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Recycle_PIA_Legion_File.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/WULA_Recycle_PIA_Legion_File.xml @@ -4,6 +4,6 @@
  • questName->晋级任务:回收皇家机密
  • -
  • questDescription->乌拉帝国行星封锁机关的总控AI向殖民地发送了一个特殊的请求,有一个装着皇室机密的保险箱被乌拉帝国的进步派叛军控制了,殖民地必须将其回收,随后将其交回乌拉帝国舰队。如果保险箱已经被打开,就要杀光在场的所有人。\n\n目前叛军很可能在试图打开这个保险箱,你需要检查其哨站内的工作台。\n\n你只有5天的时间处理这个任务,取得保险箱后,你可以建造<color=#6BB7B7><i>乌拉帝国物资输送舱</i></color>来将保险箱输送到位于轨道上的舰队。
  • +
  • questDescription->乌拉帝国行星封锁机关的总控AI向殖民地发送了一个特殊的请求,有一个装着皇室机密的保险箱被乌拉帝国的进步派叛军控制了,殖民地必须将其回收,随后将其交回乌拉帝国舰队。如果保险箱已经被打开,就要杀光在场的所有人。\n\n目前叛军很可能在试图打开这个保险箱,你需要检查其哨站内的工作台。大约有<color=#AA3020><i>10~20个叛军合成人</i></color>被拍摄到,现场同时发现了正在运作的<color=#AA3020><i>若干炮台</i></color>。\n\n你只有5天的时间处理这个任务,取得保险箱后,你可以建造<color=#6BB7B7><i>乌拉帝国物资输送舱</i></color>来将保险箱输送到位于轨道上的舰队。
  • \ No newline at end of file diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_BuildingSpawner/CompBuildingSpawner.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildingSpawner/CompBuildingSpawner.cs new file mode 100644 index 00000000..b10e7221 --- /dev/null +++ b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildingSpawner/CompBuildingSpawner.cs @@ -0,0 +1,1048 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using RimWorld; +using RimWorld.Planet; +using UnityEngine; +using Verse; +using Verse.AI; +using Verse.Sound; + +namespace WulaFallenEmpire +{ + public class CompBuildingSpawner : ThingComp + { + public CompProperties_BuildingSpawner Props => (CompProperties_BuildingSpawner)props; + + private GlobalStorageWorldComponent _globalStorage; + private GlobalStorageWorldComponent GlobalStorage + { + get + { + if (_globalStorage == null) + { + _globalStorage = Find.World.GetComponent(); + } + return _globalStorage; + } + } + + // 状态变量 + public bool used = false; + private int callTick = -1; + public bool calling = false; + private bool usedGlobalStorage = false; + public bool autoCallScheduled = false; + + // 非玩家派系检查 + public bool IsNonPlayerFaction => parent.Faction != null && parent.Faction != Faction.OfPlayer; + + // 自动呼叫条件 + private bool ShouldAutoCall => IsNonPlayerFaction && Props.canAutoCall && !autoCallScheduled && !used; + + // 科技检查 + public bool HasRequiredResearch + { + get + { + if (Props.requiredResearch == null) + return true; + + if (IsNonPlayerFaction) + return true; // 非玩家派系不需要科技 + + return Props.requiredResearch.IsFinished; + } + } + + // FlyOver 检查 + public bool HasRequiredFlyOver + { + get + { + if (!Props.requireFlyOver) + return true; + + if (parent?.Map == null) + return false; + + try + { + // 检查所有FlyOver类型的物体 + var allFlyOvers = new List(); + var dynamicObjects = parent.Map.dynamicDrawManager.DrawThings; + foreach (var thing in dynamicObjects) + { + if (thing is FlyOver) + { + allFlyOvers.Add(thing); + } + } + + Log.Message($"[BuildingSpawner] Found {allFlyOvers.Count} FlyOvers on map"); + + foreach (var thing in allFlyOvers) + { + if (thing is FlyOver flyOver && !flyOver.Destroyed) + { + var facilitiesComp = flyOver.GetComp(); + if (facilitiesComp == null) + { + Log.Warning($"[BuildingSpawner] FlyOver at {flyOver.Position} has no CompFlyOverFacilities"); + continue; + } + + if (facilitiesComp.HasFacility("BuildingdropperFacility")) + { + Log.Message($"[BuildingSpawner] Found valid FlyOver at {flyOver.Position} with BuildingdropperFacility"); + return true; + } + } + } + + Log.Message("[BuildingSpawner] No FlyOver with BuildingdropperFacility found"); + return false; + } + catch (System.Exception ex) + { + Log.Error($"[BuildingSpawner] Exception while checking for FlyOver: {ex}"); + return false; + } + } + } + + // 屋顶检查 + public bool CheckRoofConditions + { + get + { + if (parent?.Map == null) + return true; + + RoofDef roof = parent.Position.GetRoof(parent.Map); + if (roof == null) + return true; + + if (roof.isThickRoof && !Props.allowThickRoof) + return false; + if (!roof.isThickRoof && !Props.allowThinRoof) + return false; + + return true; + } + } + + // 总体可调用检查 + public bool CanCallBuilding => !used && !calling && HasRequiredResearch && + 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($"[BuildingSpawner] Scheduled auto-call for non-player building {parent.Label} at tick {callTick}"); + } + } + + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Values.Look(ref used, "used", false); + 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) + { + if (autoCallScheduled) + { + ExecuteAutoBuildingSpawn(); + } + else + { + ExecuteBuildingSpawn(); + } + } + } + + // 计算生成位置 + private IntVec3 CalculateSpawnPosition() + { + IntVec3 basePos = parent.Position; + + // 应用偏移 + IntVec3 offsetPos = basePos + new IntVec3(Props.spawnOffset.x, 0, Props.spawnOffset.z); + + return offsetPos; + } + + // 自动生成建筑(非玩家派系) + protected virtual void ExecuteAutoBuildingSpawn() + { + try + { + Log.Message($"[BuildingSpawner] Executing auto building spawn for non-player building at {parent.Position}"); + + if (Props.buildingToSpawn == null) + { + Log.Error("[BuildingSpawner] Building def is null!"); + ResetCall(); + return; + } + + // 处理屋顶 + HandleRoofDestruction(); + + // 获取生成位置 + IntVec3 spawnPos = CalculateSpawnPosition(); + + // 检查位置是否可用 + if (!CanSpawnAtPosition(spawnPos)) + { + Log.Error($"[BuildingSpawner] Cannot spawn building at {spawnPos}"); + ResetCall(); + return; + } + + // 播放效果器 + PlaySpawnEffects(spawnPos); + + // 创建建筑 + Thing newBuilding = CreateBuilding(spawnPos); + + if (newBuilding == null) + { + Log.Error("[BuildingSpawner] Failed to create building!"); + ResetCall(); + return; + } + + // 生成建筑 + GenSpawn.Spawn(newBuilding, spawnPos, parent.Map, Props.buildingRotation); + + // 后处理 + PostSpawnProcessing(newBuilding); + + // 销毁原建筑 + if (Props.destroyBuilding) + { + parent.Destroy(DestroyMode.Vanish); + } + + // 重置状态 + ResetCall(); + autoCallScheduled = false; + + // 显示消息 + Messages.Message("WULA_AutoBuildingSpawned".Translate(Props.buildingToSpawn.label, parent.Faction.Name), + MessageTypeDefOf.NeutralEvent); + } + catch (System.Exception ex) + { + Log.Error($"[BuildingSpawner] Error in ExecuteAutoBuildingSpawn: {ex}"); + ResetCall(); + } + } + + // 手动生成建筑 + public void CallBuilding(bool isAutoCall = false) + { + // 非玩家派系不能手动呼叫 + if (IsNonPlayerFaction && !isAutoCall) + { + Messages.Message("WULA_NonPlayerCannotCall".Translate(), parent, MessageTypeDefOf.RejectInput); + return; + } + + if (!CanCallBuilding) + { + ShowDisabledReason(); + return; + } + + Log.Message($"[BuildingSpawner] Starting building spawn from {parent.Label} at {parent.Position}"); + + calling = true; + used = true; + int delay = isAutoCall ? Props.autoCallDelayTicks : Props.delayTicks; + callTick = Find.TickManager.TicksGame + delay; + + if (delay <= 0) + { + ExecuteBuildingSpawn(); + } + else + { + string messageKey = usedGlobalStorage ? + "WULA_BuildingIncomingFromGlobal" : + "WULA_BuildingIncoming"; + Messages.Message(messageKey.Translate(delay.ToStringTicksToPeriod()), parent, MessageTypeDefOf.ThreatBig); + } + } + + // 显示禁用原因 + private void ShowDisabledReason() + { + if (!HasRequiredResearch) + { + Messages.Message("WULA_MissingResearch".Translate(Props.requiredResearch.label), + parent, MessageTypeDefOf.RejectInput); + } + else if (!HasRequiredFlyOver) + { + Messages.Message("WULA_NoBuildingDropperFlyOver".Translate(), + parent, MessageTypeDefOf.RejectInput); + } + else if (!CheckRoofConditions) + { + string roofType = parent.Position.GetRoof(parent.Map)?.isThickRoof == true ? + "thick" : "thin"; + Messages.Message($"WULA_RoofBlocking_{roofType}".Translate(), + parent, MessageTypeDefOf.RejectInput); + } + } + + // 重置状态 + protected void ResetCall() + { + calling = false; + used = false; + callTick = -1; + usedGlobalStorage = false; + autoCallScheduled = false; + } + + // 执行建筑生成 + protected virtual void ExecuteBuildingSpawn() + { + Log.Message($"[BuildingSpawner] Executing building spawn at {parent.Position}"); + + if (Props.buildingToSpawn == null) + { + Log.Error("[BuildingSpawner] Building def is null!"); + return; + } + + // 检查资源 + var resourceCheck = CheckAndConsumeMaterials(); + if (!resourceCheck.HasEnoughMaterials) + { + Log.Message($"[BuildingSpawner] Aborting building spawn due to insufficient materials."); + ResetCall(); + return; + } + + usedGlobalStorage = resourceCheck.UsedGlobalStorage; + + // 处理屋顶 + HandleRoofDestruction(); + + // 获取生成位置 + IntVec3 spawnPos = CalculateSpawnPosition(); + + // 检查位置是否可用 + if (!CanSpawnAtPosition(spawnPos)) + { + Log.Error($"[BuildingSpawner] Cannot spawn building at {spawnPos}"); + ResetCall(); + return; + } + + // 播放效果器 + PlaySpawnEffects(spawnPos); + + // 创建建筑 + Thing newBuilding = CreateBuilding(spawnPos); + + if (newBuilding == null) + { + Log.Error("[BuildingSpawner] Failed to create building!"); + return; + } + + // 生成建筑 + GenSpawn.Spawn(newBuilding, spawnPos, parent.Map, Props.buildingRotation); + + // 后处理 + PostSpawnProcessing(newBuilding); + + // 销毁原建筑 + if (Props.destroyBuilding) + { + parent.Destroy(DestroyMode.Vanish); + } + + calling = false; + callTick = -1; + } + + // 检查位置是否可用 + private bool CanSpawnAtPosition(IntVec3 spawnPos) + { + if (parent?.Map == null) + return false; + + // 检查是否在地图范围内 + if (!spawnPos.InBounds(parent.Map)) + return false; + + // 检查是否有阻挡物 + if (!Props.canReplaceExisting) + { + List thingsAtPos = spawnPos.GetThingList(parent.Map); + foreach (var thing in thingsAtPos) + { + // 跳过不可穿透的建筑和植物 + if (thing.def.passability == Traversability.Impassable) + return false; + } + } + + return true; + } + + // 处理屋顶破坏 + 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($"[BuildingSpawner] Destroying thin roof at {targetPos}"); + parent.Map.roofGrid.SetRoof(targetPos, null); + + // 生成屋顶破坏效果 + FleckMaker.ThrowDustPuffThick(targetPos.ToVector3Shifted(), parent.Map, 2f, + new Color(1f, 1f, 1f, 2f)); + } + } + + // 播放生成效果 + private void PlaySpawnEffects(IntVec3 spawnPos) + { + if (parent?.Map == null) + return; + + // 播放效果器 + if (Props.spawnEffecter != null) + { + Effecter effecter = Props.spawnEffecter.Spawn(); + effecter.Trigger(new TargetInfo(spawnPos, parent.Map), + new TargetInfo(spawnPos, parent.Map)); + effecter.Cleanup(); + } + + // 播放音效 + if (Props.spawnSound != null) + { + Props.spawnSound.PlayOneShot(new TargetInfo(spawnPos, parent.Map)); + } + + // 播放粒子效果 + FleckMaker.ThrowSmoke(spawnPos.ToVector3Shifted(), parent.Map, 1.5f); + FleckMaker.ThrowLightningGlow(spawnPos.ToVector3Shifted(), parent.Map, 2f); + } + + // 创建建筑实例 + private Thing CreateBuilding(IntVec3 spawnPos) + { + if (Props.buildingToSpawn == null) + return null; + + // 构建参数 + ThingDef stuff = null; + if (Props.buildingToSpawn.MadeFromStuff) + { + // 如果需要材料,可以从配置中获取或使用默认材料 + stuff = ThingDefOf.Steel; + } + + // 创建建筑 + Thing newBuilding = ThingMaker.MakeThing(Props.buildingToSpawn, stuff); + + // 设置派系 + if (Props.inheritFaction && parent.Faction != null) + { + newBuilding.SetFaction(parent.Faction); + } + + // 设置燃料(如果有) + CompRefuelable refuelable = newBuilding.TryGetComp(); + if (refuelable != null) + { + float fuelPercent = Props.fuelRange.RandomInRange; + refuelable.Refuel(refuelable.Props.fuelCapacity * fuelPercent); + } + + return newBuilding; + } + + // 生成后处理 + private void PostSpawnProcessing(Thing newBuilding) + { + // 可以添加额外的后处理逻辑 + // 例如:启动炮塔、设置工作模式等 + + // 如果生成的是炮塔,自动启动 + if (newBuilding is Building_Turret turret) + { + CompPowerTrader powerComp = turret.GetComp(); + if (powerComp != null) + { + powerComp.PowerOn = true; + } + } + + // 记录日志 + Log.Message($"[BuildingSpawner] Successfully spawned {Props.buildingToSpawn.label} at {newBuilding.Position}"); + } + + // 资源管理(与SkyfallerCaller类似) + protected virtual List CostList + { + get + { + if (parent.def?.costList.NullOrEmpty() ?? true) + { + return null; + } + return parent.def.costList; + } + } + + protected struct ResourceCheckResult + { + public bool HasEnoughMaterials; + public bool UsedGlobalStorage; + public Dictionary BeaconMaterials; + public Dictionary GlobalMaterials; + } + + protected ResourceCheckResult CheckAndConsumeMaterials() + { + var result = new ResourceCheckResult + { + HasEnoughMaterials = false, + UsedGlobalStorage = false, + BeaconMaterials = new Dictionary(), + GlobalMaterials = new Dictionary() + }; + + if (DebugSettings.godMode) + { + result.HasEnoughMaterials = true; + return result; + } + + var costList = CostList; + if (costList.NullOrEmpty()) + { + result.HasEnoughMaterials = true; + return result; + } + + if (parent.Map == null) + { + return result; + } + + // 收集信标附近的物资 + var beaconMaterials = CollectBeaconMaterials(); + result.BeaconMaterials = beaconMaterials; + + // 检查信标附近物资是否足够 + bool beaconHasEnough = true; + foreach (var cost in costList) + { + int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; + if (availableInBeacon < cost.count) + { + beaconHasEnough = false; + break; + } + } + + if (beaconHasEnough) + { + ConsumeBeaconMaterials(beaconMaterials, costList); + result.HasEnoughMaterials = true; + result.UsedGlobalStorage = false; + return result; + } + + // 检查全局储存器 + var globalMaterials = CheckGlobalStorageMaterials(); + result.GlobalMaterials = globalMaterials; + + bool globalHasEnough = true; + foreach (var cost in costList) + { + int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; + int availableInGlobal = globalMaterials.ContainsKey(cost.thingDef) ? globalMaterials[cost.thingDef] : 0; + + if (availableInBeacon + availableInGlobal < cost.count) + { + globalHasEnough = false; + break; + } + } + + if (globalHasEnough) + { + ConsumeMixedMaterials(beaconMaterials, globalMaterials, costList); + result.HasEnoughMaterials = true; + result.UsedGlobalStorage = true; + return result; + } + + result.HasEnoughMaterials = false; + return result; + } + + // 资源收集和消耗方法(与SkyfallerCaller相同) + private Dictionary CollectBeaconMaterials() + { + var materials = new Dictionary(); + + if (parent.Map == null) return materials; + + foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map)) + { + foreach (IntVec3 cell in beacon.TradeableCells) + { + List thingList = parent.Map.thingGrid.ThingsListAt(cell); + for (int i = 0; i < thingList.Count; i++) + { + Thing thing = thingList[i]; + if (thing.def.EverHaulable) + { + if (materials.ContainsKey(thing.def)) + { + materials[thing.def] += thing.stackCount; + } + else + { + materials[thing.def] = thing.stackCount; + } + } + } + } + } + + return materials; + } + + // 新增:检查全局储存器中的物资 + private Dictionary CheckGlobalStorageMaterials() + { + var materials = new Dictionary(); + + if (GlobalStorage == null) return materials; + + var costList = CostList; + if (costList.NullOrEmpty()) return materials; + + foreach (var cost in costList) + { + int globalCount = GlobalStorage.GetInputStorageCount(cost.thingDef); + if (globalCount > 0) + { + materials[cost.thingDef] = globalCount; + } + } + + return materials; + } + + // 新增:只消耗信标附近的物资 + private void ConsumeBeaconMaterials(Dictionary beaconMaterials, List costList) + { + var tradeableThings = new List(); + + foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map)) + { + foreach (IntVec3 cell in beacon.TradeableCells) + { + List thingList = parent.Map.thingGrid.ThingsListAt(cell); + for (int i = 0; i < thingList.Count; i++) + { + Thing thing = thingList[i]; + if (thing.def.EverHaulable) + { + tradeableThings.Add(thing); + } + } + } + } + + foreach (var cost in costList) + { + int remaining = cost.count; + for (int i = tradeableThings.Count - 1; i >= 0 && remaining > 0; i--) + { + var thing = tradeableThings[i]; + if (thing.def == cost.thingDef) + { + if (thing.stackCount > remaining) + { + thing.SplitOff(remaining); + remaining = 0; + } + else + { + remaining -= thing.stackCount; + thing.Destroy(); + tradeableThings.RemoveAt(i); + } + } + } + } + } + + // 新增:混合消耗信标和全局储存器的物资 + private void ConsumeMixedMaterials(Dictionary beaconMaterials, Dictionary globalMaterials, List costList) + { + // 先消耗信标附近的物资 + var tradeableThings = new List(); + + foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map)) + { + foreach (IntVec3 cell in beacon.TradeableCells) + { + List thingList = parent.Map.thingGrid.ThingsListAt(cell); + for (int i = 0; i < thingList.Count; i++) + { + Thing thing = thingList[i]; + if (thing.def.EverHaulable) + { + tradeableThings.Add(thing); + } + } + } + } + + // 对每种所需材料进行处理 + foreach (var cost in costList) + { + int remaining = cost.count; + + // 第一步:消耗信标附近的物资 + for (int i = tradeableThings.Count - 1; i >= 0 && remaining > 0; i--) + { + var thing = tradeableThings[i]; + if (thing.def == cost.thingDef) + { + if (thing.stackCount > remaining) + { + thing.SplitOff(remaining); + remaining = 0; + } + else + { + remaining -= thing.stackCount; + thing.Destroy(); + tradeableThings.RemoveAt(i); + } + } + } + + // 第二步:如果还有剩余,从全局储存器扣除 + if (remaining > 0 && GlobalStorage != null) + { + GlobalStorage.RemoveFromInputStorage(cost.thingDef, remaining); + } + } + } + + // 保留原有的 HasEnoughMaterials 方法用于 Gizmo 显示 + public bool HasEnoughMaterials() + { + if (DebugSettings.godMode) return true; + + var costList = CostList; + if (costList.NullOrEmpty()) + { + return true; + } + + // 第一步:检查信标附近物资 + var beaconMaterials = CollectBeaconMaterials(); + bool beaconHasEnough = true; + + foreach (var cost in costList) + { + int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; + if (availableInBeacon < cost.count) + { + beaconHasEnough = false; + break; + } + } + + if (beaconHasEnough) return true; + + // 第二步:检查全局储存器(如果信标附近不够) + if (GlobalStorage == null) return false; + + foreach (var cost in costList) + { + int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; + int availableInGlobal = GlobalStorage.GetInputStorageCount(cost.thingDef); + + if (availableInBeacon + availableInGlobal < cost.count) + { + return false; + } + } + + return true; + } + + // Gizmos + private void CancelCall() + { + calling = false; + used = false; + callTick = -1; + usedGlobalStorage = false; + autoCallScheduled = false; + Messages.Message("WULA_BuildingCallCancelled".Translate(), parent, MessageTypeDefOf.NeutralEvent); + } + + public override IEnumerable CompGetGizmosExtra() + { + foreach (var gizmo in base.CompGetGizmosExtra()) + yield return gizmo; + + // 非玩家派系不显示呼叫按钮 + if (IsNonPlayerFaction) + yield break; + + if (calling) + { + Command_Action cancelCommand = new Command_Action + { + defaultLabel = "WULA_CancelBuilding".Translate(), + defaultDesc = "WULA_CancelBuildingDesc".Translate(), + icon = ContentFinder.Get("UI/Designators/Cancel"), + action = CancelCall + }; + yield return cancelCommand; + } + + if (CanCallBuilding) + { + string reason = GetDisabledReason(); + Command_Action callCommand = new Command_Action + { + defaultLabel = "WULA_CallBuilding".Translate(), + defaultDesc = GetCallDescription(), + icon = ContentFinder.Get("Wula/UI/Commands/WULA_SpawnBuilding"), + action = () => CallBuilding(false), + disabledReason = reason + }; + if (!string.IsNullOrEmpty(reason)) + { + callCommand.Disable(reason); + } + yield return callCommand; + } + } + + // 获取禁用原因 + private string GetDisabledReason() + { + if (IsNonPlayerFaction) + { + return "WULA_NonPlayerCannotCall".Translate(); + } + + if (!HasRequiredResearch) + { + return "WULA_MissingResearch".Translate(Props.requiredResearch.label); + } + + if (Props.requireFlyOver && !HasRequiredFlyOver) + { + return "WULA_NoBuildingDropperFlyOver".Translate(); + } + + if (!CheckRoofConditions) + { + string roofType = parent.Position.GetRoof(parent.Map)?.isThickRoof == true ? + "thick" : "thin"; + return $"WULA_RoofBlocking_{roofType}".Translate(); + } + + if (!HasEnoughMaterials()) + { + return "WULA_InsufficientMaterials".Translate(); + } + + return null; + } + + // 获取呼叫描述 + private string GetCallDescription() + { + var sb = new StringBuilder(); + sb.Append("WULA_CallBuildingDesc".Translate(Props.buildingToSpawn.label)); + + if (Props.requiredResearch != null) + { + sb.AppendLine().Append("WULA_RequiresResearch".Translate(Props.requiredResearch.label)); + } + + if (Props.requireFlyOver && !HasRequiredFlyOver) + { + sb.AppendLine().Append("WULA_RequiresBuildingDropperFlyOver".Translate()); + } + + if (parent?.Map != null) + { + RoofDef roof = parent.Position.GetRoof(parent.Map); + if (roof != null) + { + if (roof.isThickRoof && !Props.allowThickRoof) + { + sb.AppendLine().Append("WULA_ThickRoofBlockingDesc".Translate()); + } + else if (!roof.isThickRoof && !Props.allowThinRoof) + { + sb.AppendLine().Append("WULA_RoofBlockingDesc".Translate()); + } + } + } + + string costString = GetCostString(); + if (!string.IsNullOrEmpty(costString)) + { + sb.AppendLine().AppendLine().Append("WULA_RequiredMaterials".Translate()); + sb.Append(costString); + } + + return sb.ToString(); + } + + // 获取成本字符串 + private string GetCostString() + { + var costList = CostList; + if (costList.NullOrEmpty()) + { + return ""; + } + + var sb = new StringBuilder(); + foreach (var cost in costList) + { + sb.AppendLine($" - {cost.thingDef.LabelCap}: {cost.count}"); + } + return sb.ToString(); + } + + // 检查字符串 + public override string CompInspectStringExtra() + { + if (parent?.Map == null) + { + return base.CompInspectStringExtra(); + } + + var sb = new StringBuilder(); + + // 显示自动呼叫状态 + if (autoCallScheduled && calling) + { + int ticksLeft = callTick - Find.TickManager.TicksGame; + sb.Append("WULA_AutoBuildingArrivingIn".Translate(ticksLeft.ToStringTicksToPeriod())); + } + else if (calling) + { + int ticksLeft = callTick - Find.TickManager.TicksGame; + if (ticksLeft > 0) + { + string messageKey = usedGlobalStorage ? + "WULA_BuildingArrivingInFromGlobal" : + "WULA_BuildingArrivingIn"; + sb.Append(messageKey.Translate(ticksLeft.ToStringTicksToPeriod())); + } + } + else if (!used) + { + if (IsNonPlayerFaction && Props.canAutoCall) + { + sb.Append("WULA_AutoBuildingReady".Translate()); + } + else + { + sb.Append("WULA_ReadyToCallBuilding".Translate(Props.buildingToSpawn.label)); + } + + // 显示科技需求 + if (Props.requiredResearch != null && !HasRequiredResearch) + { + sb.AppendLine().Append("WULA_MissingResearch".Translate(Props.requiredResearch.label)); + } + + // 显示FlyOver需求 + if (Props.requireFlyOver && !HasRequiredFlyOver) + { + sb.AppendLine().Append("WULA_MissingBuildingDropperFlyOver".Translate()); + } + + // 显示屋顶状态 + RoofDef roof = parent.Position.GetRoof(parent.Map); + if (roof != null) + { + if (roof.isThickRoof && !Props.allowThickRoof) + { + sb.AppendLine().Append("WULA_BlockedByThickRoof".Translate()); + } + else if (!roof.isThickRoof && !Props.allowThinRoof) + { + sb.AppendLine().Append("WULA_BlockedByRoof".Translate()); + } + } + + // 显示成本 + string costString = GetCostString(); + if (!string.IsNullOrEmpty(costString)) + { + sb.AppendLine().AppendLine("WULA_RequiredMaterials".Translate()); + sb.Append(costString); + } + } + + string baseInspectString = base.CompInspectStringExtra(); + if (!string.IsNullOrEmpty(baseInspectString)) + { + if (sb.Length > 0) + { + sb.AppendLine(); + } + sb.Append(baseInspectString); + } + + return sb.Length > 0 ? sb.ToString().TrimEnd() : null; + } + } +} diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_BuildingSpawner/CompProperties_BuildingSpawner.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildingSpawner/CompProperties_BuildingSpawner.cs new file mode 100644 index 00000000..ce6f5984 --- /dev/null +++ b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildingSpawner/CompProperties_BuildingSpawner.cs @@ -0,0 +1,82 @@ +using RimWorld; +using System.Collections.Generic; +using Verse; + +namespace WulaFallenEmpire +{ + public class CompProperties_BuildingSpawner : CompProperties + { + public ThingDef buildingToSpawn; + public bool destroyBuilding = true; + public int delayTicks = 0; + + // 自动召唤设置 + public bool canAutoCall = true; + public int autoCallDelayTicks = 0; // 默认10秒 + + // FlyOver 前提条件 + public bool requireFlyOver = false; + + // 屋顶限制 + public bool allowThinRoof = true; + public bool allowThickRoof = true; + + // 新增:科技需求 + public ResearchProjectDef requiredResearch; + + // 新增:生成时的效果器 + public EffecterDef spawnEffecter; + + // 新增:音效 + public SoundDef spawnSound; + + // 新增:建筑朝向设置 + public Rot4 buildingRotation = Rot4.North; + + // 新增:位置偏移 + public IntVec2 spawnOffset = IntVec2.Zero; + + // 新增:允许替换现有建筑 + public bool canReplaceExisting = false; + + // 新增:是否继承原建筑的派系 + public bool inheritFaction = true; + + // 新增:建筑生成后的燃料量(如果适用) + public FloatRange fuelRange = new FloatRange(1f, 1f); + + public CompProperties_BuildingSpawner() + { + compClass = typeof(CompBuildingSpawner); + } + + public override void ResolveReferences(ThingDef parentDef) + { + base.ResolveReferences(parentDef); + + // 验证buildingToSpawn + if (buildingToSpawn != null && buildingToSpawn.category != ThingCategory.Building) + { + Log.Error($"CompProperties_BuildingSpawner: buildingToSpawn must be a building, but got {buildingToSpawn.defName}"); + buildingToSpawn = null; + } + } + + public override IEnumerable ConfigErrors(ThingDef parentDef) + { + foreach (string item in base.ConfigErrors(parentDef)) + { + yield return item; + } + + if (buildingToSpawn == null) + { + yield return "buildingToSpawn is not set"; + } + else if (buildingToSpawn.category != ThingCategory.Building) + { + yield return $"buildingToSpawn must be a building, but got {buildingToSpawn.defName}"; + } + } + } +} diff --git a/Source/WulaFallenEmpire/Designator/Designator_CallSkyfallerInArea.cs b/Source/WulaFallenEmpire/Designator/Designator_CallSkyfallerInArea.cs index 2eb3adca..0dd231e0 100644 --- a/Source/WulaFallenEmpire/Designator/Designator_CallSkyfallerInArea.cs +++ b/Source/WulaFallenEmpire/Designator/Designator_CallSkyfallerInArea.cs @@ -11,17 +11,21 @@ namespace WulaFallenEmpire // 记录已经处理过的建筑(避免重复) private HashSet processedBuildings = new HashSet(); + // 组件类型过滤 + public bool includeBuildingSpawner = true; + public bool includeSkyfallerCaller = true; + public Designator_CallSkyfallerInArea() { - defaultLabel = "WULA_Designator_CallSkyfallerInArea".Translate(); - defaultDesc = "WULA_Designator_CallSkyfallerInAreaDesc".Translate(); - icon = ContentFinder.Get("Wula/UI/Designators/Designator_CallSkyfallerInArea"); + defaultLabel = "WULA_Designator_CallInArea".Translate(); + defaultDesc = "WULA_Designator_CallInAreaDesc".Translate(); + icon = ContentFinder.Get("Wula/UI/Designators/Designator_CallInArea"); soundDragSustain = SoundDefOf.Designate_DragStandard; soundDragChanged = SoundDefOf.Designate_DragStandard_Changed; useMouseIcon = true; soundSucceeded = SoundDefOf.Designate_Claim; hotKey = KeyBindingDefOf.Misc12; - tutorTag = "CallSkyfallerInArea"; + tutorTag = "CallInArea"; } public override DrawStyleCategoryDef DrawStyleCategory => DrawStyleCategoryDefOf.FilledRectangle; @@ -35,13 +39,13 @@ namespace WulaFallenEmpire if (c.Fogged(Map)) return false; - // 只要单元格内有玩家建筑,就允许选择 + // 检查单元格内是否有符合条件的玩家建筑 var things = Map.thingGrid.ThingsListAt(c); foreach (var thing in things) { if (thing.def.category == ThingCategory.Building && thing.Faction == Faction.OfPlayer && - thing.TryGetComp() != null) + HasValidComponent(thing)) { return true; } @@ -50,6 +54,28 @@ namespace WulaFallenEmpire // 即使单元格内没有符合条件的建筑,也允许选择(这样用户可以拖动区域) return true; } + + // 检查建筑是否有有效的组件 + private bool HasValidComponent(Thing thing) + { + // 检查 Building Spawner 组件 + if (includeBuildingSpawner) + { + var buildingSpawner = thing.TryGetComp(); + if (buildingSpawner != null && buildingSpawner.CanCallBuilding) + return true; + } + + // 检查 Skyfaller Caller 组件 + if (includeSkyfallerCaller) + { + var skyfallerCaller = thing.TryGetComp(); + if (skyfallerCaller != null && skyfallerCaller.CanCallSkyfaller) + return true; + } + + return false; + } public override void DesignateSingleCell(IntVec3 c) { @@ -63,30 +89,53 @@ namespace WulaFallenEmpire processedBuildings.Clear(); int totalBuildings = 0; + int buildingSpawnerCount = 0; + int skyfallerCallerCount = 0; // 处理所有选中的单元格 foreach (var cell in cells) { if (cell.InBounds(Map)) { - // 统计该单元格处理的建筑数量 int cellCount = processedBuildings.Count; ProcessCell(cell); int newBuildings = processedBuildings.Count - cellCount; + + // 统计每个组件类型的调用数量 + foreach (var building in processedBuildings) + { + if (building.Destroyed) continue; + + if (building.TryGetComp()?.calling == true) + buildingSpawnerCount++; + else if (building.TryGetComp()?.calling == true) + skyfallerCallerCount++; + } + totalBuildings += newBuildings; } } - // 计算成功和失败的数量 - // 这里需要跟踪每个建筑的调用结果 - // 由于我们直接调用CallSkyfaller,需要知道哪些失败了 - // 简化处理:在ProcessCell中统计 - - // 显示简单的结果消息 + // 显示结果消息 if (totalBuildings > 0) { - Messages.Message("WULA_AreaCallInitiated".Translate(totalBuildings), - MessageTypeDefOf.PositiveEvent); + string message = "WULA_AreaCallInitiated".Translate(totalBuildings); + + if (buildingSpawnerCount > 0 && skyfallerCallerCount > 0) + { + message += "\n" + "WULA_BothComponentsCalled".Translate( + buildingSpawnerCount, skyfallerCallerCount); + } + else if (buildingSpawnerCount > 0) + { + message += "\n" + "WULA_BuildingSpawnerCalled".Translate(buildingSpawnerCount); + } + else if (skyfallerCallerCount > 0) + { + message += "\n" + "WULA_SkyfallerCallerCalled".Translate(skyfallerCallerCount); + } + + Messages.Message(message, MessageTypeDefOf.PositiveEvent); } else { @@ -113,21 +162,44 @@ namespace WulaFallenEmpire if (processedBuildings.Contains(thing)) continue; - // 获取空投组件 - var comp = thing.TryGetComp(); - if (comp == null) - continue; + // 标记为已处理 + processedBuildings.Add(thing); - // 尝试呼叫空投 - if (comp.CanCallSkyfaller) + // 尝试调用两种组件(如果有且可以调用) + bool anyCalled = false; + + // 1. 先尝试 Building Spawner + if (includeBuildingSpawner) { - comp.CallSkyfaller(false); - processedBuildings.Add(thing); + var buildingSpawner = thing.TryGetComp(); + if (buildingSpawner != null && buildingSpawner.CanCallBuilding) + { + buildingSpawner.CallBuilding(false); + anyCalled = true; + + // 如果建筑被销毁,记录日志 + if (thing.Destroyed) + { + Log.Message($"[Designator] Building destroyed after BuildingSpawner call at {cell}"); + } + } } - // 即使不能呼叫,也添加到已处理列表,避免重复尝试 - else + + // 2. 尝试 Skyfaller Caller(如果建筑还存在) + if (!thing.Destroyed && includeSkyfallerCaller) { - processedBuildings.Add(thing); + var skyfallerCaller = thing.TryGetComp(); + if (skyfallerCaller != null && skyfallerCaller.CanCallSkyfaller) + { + skyfallerCaller.CallSkyfaller(false); + anyCalled = true; + } + } + + // 如果没有任何组件被调用,从处理列表中移除(防止重复尝试) + if (!anyCalled) + { + processedBuildings.Remove(thing); } } } @@ -141,20 +213,43 @@ namespace WulaFallenEmpire if (t.Faction != Faction.OfPlayer) return false; - var comp = t.TryGetComp(); - if (comp == null) - return false; - - return true; + return HasValidComponent(t); } public override void DesignateThing(Thing t) { // 用于反向设计器(右键菜单) - var comp = t.TryGetComp(); - if (comp != null && comp.CanCallSkyfaller) + processedBuildings.Add(t); + + // 尝试调用两种组件 + bool anyCalled = false; + + // 1. 先尝试 Building Spawner + if (includeBuildingSpawner) { - comp.CallSkyfaller(false); + var buildingSpawner = t.TryGetComp(); + if (buildingSpawner != null && buildingSpawner.CanCallBuilding) + { + buildingSpawner.CallBuilding(false); + anyCalled = true; + } + } + + // 2. 尝试 Skyfaller Caller(如果建筑还存在) + if (!t.Destroyed && includeSkyfallerCaller) + { + var skyfallerCaller = t.TryGetComp(); + if (skyfallerCaller != null && skyfallerCaller.CanCallSkyfaller) + { + skyfallerCaller.CallSkyfaller(false); + anyCalled = true; + } + } + + if (!anyCalled) + { + Messages.Message("WULA_NoComponentCanCall".Translate(), + t, MessageTypeDefOf.RejectInput); } } @@ -162,8 +257,32 @@ namespace WulaFallenEmpire { // 参考Designator_Deconstruct,只绘制鼠标悬停方框 GenUI.RenderMouseoverBracket(); + + // 可以添加额外的视觉效果来显示哪些建筑将被影响 + if (Find.DesignatorManager.SelectedDesignator == this) + { + DrawAffectedBuildings(); + } + } + + // 绘制受影响的建筑 + private void DrawAffectedBuildings() + { + if (Map == null) return; + + // 这里可以绘制高亮显示哪些建筑会被影响 + // 但由于性能考虑,只在特定条件下绘制 + if (DebugSettings.godMode) + { + foreach (var building in Map.listerBuildings.allBuildingsColonist) + { + if (HasValidComponent(building)) + { + GenDraw.DrawFieldEdges(new List { building.Position }, + building.Destroyed ? Color.red : Color.green); + } + } + } } } - - } diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj index c767427d..6259969a 100644 --- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj +++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj @@ -106,6 +106,8 @@ + +