diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 719cde20..8e38dab2 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/DroneWorkModeDefs/DroneWorkModes.xml b/1.6/1.6/Defs/DroneWorkModeDefs/DroneWorkModes.xml new file mode 100644 index 00000000..7a3a91f6 --- /dev/null +++ b/1.6/1.6/Defs/DroneWorkModeDefs/DroneWorkModes.xml @@ -0,0 +1,36 @@ + + + + + Work + + The drone will work normally. + UI/Commands/Attack + 10 + + + + Recharge + + The drone will seek a charger to recharge. + UI/Commands/DesirePower + 20 + + + + Shutdown + + The drone will find a safe spot to self-shutdown. + UI/Commands/ToggleVent + 30 + + + + AutoFight + + The drone will automatically seek and attack enemies. + UI/Commands/Attack + 40 + + + \ No newline at end of file diff --git a/1.6/1.6/Defs/JobDefs/Jobs_WULA_AutonomousMech.xml b/1.6/1.6/Defs/JobDefs/Jobs_WULA_AutonomousMech.xml new file mode 100644 index 00000000..244b77e7 --- /dev/null +++ b/1.6/1.6/Defs/JobDefs/Jobs_WULA_AutonomousMech.xml @@ -0,0 +1,12 @@ + + + + + WULA_DroneSelfShutdown + WulaFallenEmpire.JobDriver_DroneSelfShutdown + self-shutting down. + false + true + + + \ No newline at end of file diff --git a/1.6/1.6/Defs/PawnTableDefs/PawnTables_WULA.xml b/1.6/1.6/Defs/PawnTableDefs/PawnTables_WULA.xml new file mode 100644 index 00000000..db81724f --- /dev/null +++ b/1.6/1.6/Defs/PawnTableDefs/PawnTables_WULA.xml @@ -0,0 +1,35 @@ + + + + + + Mechs + +
  • Label
  • +
  • WULA_DroneWorkMode
  • +
  • WULA_DroneEnergy
  • +
  • ControlGroup
  • +
  • Overseer
  • +
  • AllowedArea
  • +
  • WorkPriority
  • +
    +
    + + + WULA_DroneWorkMode + WulaFallenEmpire.PawnColumnWorker_DroneWorkMode + true + UI/Commands/Attack + Work Mode + 24 + + + + WULA_DroneEnergy + WulaFallenEmpire.PawnColumnWorker_DroneEnergy + true + Energy + 120 + + +
    \ No newline at end of file diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml index ea9cff67..9506574f 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml @@ -148,57 +148,6 @@ 修复了 {0} 的结构损伤 距上次维护 - - 舰队生产 - 舰队生产订单 - 添加生产订单 - 恢复 - 暂停 - 删除 - 等待资源 - 完成 - 未知 - 帝国舰队存储的资源不足 - 无可用配方 - - 可用配方 - 产物 - 工作量 - - - 查看存储 - 输入存储(原材料) - 输出存储(产品) - 未找到全局存储组件 - 无物品 - 存储统计 - 种输入物品 - 种输出物品 - - - 空投成品 - 将乌拉帝国母舰和工程舰上完成加工的所有物品通过空投舱投放到指定区域 - 无法执行空投:工作台未就绪 - 没有可空投的物品 - 目标距离超出最大空投范围({0}) - 没有有效的空投落点。请选择另一个位置 - 无法分配物品到空投舱 - 成功空投了{0}个空投舱 - 需要拥有-生产设施-的战舰部署在殖民地轨道上才能空投 - 当前地图上没有拥有-生产设施-的战舰部署在殖民地轨道上,无法进行空投 - - - 发射到乌拉帝国舰队 - 将物品发送到乌拉帝国舰队,以便其使用这些材料进行加工。\n\n如果装备、武器和尸体被送到乌拉帝国舰队,则它们会在下一次成品空投被扔回来,其他的物资若被乌拉帝国舰队接收则一概不退。 - 没有物品可以发送到全局存储 - - {0}件物资被舰队接收,{1}件物资被舰队退回(随着下一次成品空投一起退回) - {0}件物资被舰队接收 - {0}件物资被舰队退回(随着下一次成品空投一起退回) - 输入存储: {0} - 输出存储: {0} - 舰队拒绝接收物资——里面包含了殖民者、动物、尸体或有毒垃圾 - 材质 为此武器选择材质 当前材质:{0} diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml index 543716e7..b8afd2e5 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml @@ -6,10 +6,10 @@ 空闲 运行中 储存的零部件 - 进入维护 + 进入维护 命令一个需要维护的殖民者进入维护舱。 需要 {0} 零部件 - 没有殖民者需要维护。 + 没有殖民者需要维护。 中止维护并弹出殖民者。 {PAWN_nameDef} 的维护已完成。 维护已取消。 @@ -17,56 +17,84 @@ {PAWN_nameDef} 的维护已完成,但{PAWN_nameDef} 没有可修复的受损部位。 没有可用的零部件来搬运。 - {0} 获得了 {1} 层伤害护盾! + {0} 获得了 {1} 层伤害护盾! 无法对已死亡的Pawn使用。 伤害护盾已达到最大层数。 使用:增加 {0} 层伤害护盾 自律机械体 - - 准备材料中 - 订单 {0} 已开始生产 - 上传中 - 全球订单 - 全球生产 - 装备 - 武器 - 机械体 - 添加订单 + + 舰队生产 + 舰队生产订单 + 添加生产订单 + 恢复 + 暂停 + 删除 等待资源 完成 未知 - 删除 - 暂停 - 已暂停 - 恢复 - 资源不足 - 固定原料 + 帝国舰队存储的资源不足 + 无可用配方 + + 可用配方 产物 工作量 - 全球存储 - 输入存储 - 输出存储 + + + 查看存储 + 输入存储(原材料) + 输出存储(产品) + 未找到全局存储组件 无物品 - 未找到全球存储组件 - 空投产物 - 将云端存储的产物空投到指定位置。 - 选择空投位置 - 确认空投 - 将消耗 {0} 个空投舱,空投 {1} 个物品。 - 没有可空投的产物。 - 没有工厂设施飞船。 - 无法分配物品到空投舱。 - 成功发射了 {0} 个空投舱。 - 发射到全球存储 - 将发射舱内的物品发送到全球存储。 - 没有物品可发送。 - 发射取消:包含禁止物品 {0} - 已发送 {0} 个物品到输入存储,{1} 个物品到输出存储。 - 输入物品 - 输出物品 - 已发送 {0} 个物品到输入存储。 - 已发送 {0} 个物品到输出存储。 - 没有处理任何物品。 + 存储统计 + 种输入物品 + 种输出物品 + + + 空投成品 + 将乌拉帝国母舰和工程舰上完成加工的所有物品通过空投舱投放到指定区域 + 无法执行空投:工作台未就绪 + 没有可空投的物品 + 目标距离超出最大空投范围({0}) + 没有有效的空投落点。请选择另一个位置 + 无法分配物品到空投舱 + 成功空投了{0}个空投舱 + 需要拥有-生产设施-的战舰部署在殖民地轨道上才能空投 + 当前地图上没有拥有-生产设施-的战舰部署在殖民地轨道上,无法进行空投 + + + 发射到乌拉帝国舰队 + 将物品发送到乌拉帝国舰队,以便其使用这些材料进行加工。\n\n如果装备、武器和尸体被送到乌拉帝国舰队,则它们会在下一次成品空投被扔回来,其他的物资若被乌拉帝国舰队接收则一概不退。 + 没有物品可以发送到全局存储 + + {0}件物资被舰队接收,{1}件物资被舰队退回(随着下一次成品空投一起退回) + {0}件物资被舰队接收 + {0}件物资被舰队退回(随着下一次成品空投一起退回) + 输入存储: {0} + 输出存储: {0} + 舰队拒绝接收物资——里面包含了殖民者、动物、尸体或有毒垃圾 + + + [能量: {0}] + 当前能量: {0} + [低能量!] + [临界能量!] + 切换机械工作模式 + 机械模式: {0} + 工作 + 充电 + 关机 + 未知 + 切换到工作模式 - 机械将执行分配的工作 + 切换到充电模式 - 机械将寻找充电站并充电 + 切换到关机模式 - 机械将立即进入休眠状态 + {0} 已切换到 {1} 模式 + (能量: {0}) + 自主控制 + 自主模式: {0} + {0} 能量低,自动切换到充电模式 + {0} 已充满电,自动切换到工作模式 + {0} 能量临界! + \ No newline at end of file diff --git a/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/EventDef/EventDef_Examples_RandomList.xml b/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/EventDef/EventDef_Examples_RandomList.xml deleted file mode 100644 index b1c71e5f..00000000 --- a/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/EventDef/EventDef_Examples_RandomList.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - 系统管理员 - 这是一个演示随机效果列表功能的事件。 - - 点击下面的选项,你会看到一条固定消息,然后会随机获得一件物品。 - 试一试! - 关闭 - \ No newline at end of file diff --git a/Source/Documentation/AutonomousMech_Improvement_Plan.md b/Source/Documentation/AutonomousMech_Improvement_Plan.md new file mode 100644 index 00000000..5cc2f1fe --- /dev/null +++ b/Source/Documentation/AutonomousMech_Improvement_Plan.md @@ -0,0 +1,58 @@ +# 自主机械体系统改进计划 + +基于对 `AncotLibrary` 的分析,我们将对现有的 `WULA_AutonomousMech` 系统进行全面升级,旨在提供更灵活的配置、更友好的 UI 交互以及更智能的 AI 行为。 + +## 1. 核心架构重构 + +### 1.1 工作模式数据驱动化 +* **目标**: 废弃硬编码的 `AutonomousWorkMode` 枚举,转为使用 XML 定义的 `DroneWorkModeDef`。 +* **实现**: + * 创建 `DroneWorkModeDef` 类,包含 `iconPath` (图标路径), `uiOrder` (排序), `label` (名称), `description` (描述) 等字段。 + * 在 `CompAutonomousMech` 中使用 `DroneWorkModeDef` 类型的字段替代原有的枚举。 + * 预定义基础模式:`Work` (工作), `Recharge` (充电), `Shutdown` (休眠), `AutoFight` (自动战斗)。 + +### 1.2 自动战斗系统 (`AutoFight`) +* **目标**: 允许机械体在非征召状态下自动寻找并攻击敌人。 +* **实现**: + * 引入 `CompMechAutoFight` 组件(或集成到 `CompAutonomousMech` 中)。 + * 添加 `ThinkNode_ConditionalAutoFight` 行为树节点。 + * 实现自动索敌和攻击的 AI 逻辑(参考 `JobGiver_AIFightEnemies`)。 + * **威胁判定**: 确保开启自动战斗的机械体能被敌人正确识别为威胁(已部分实现,需完善)。 + +## 2. UI 交互增强 + +### 2.1 高级 Gizmo (`DroneGizmo`) +* **目标**: 提供更直观的控制面板。 +* **实现**: + * **能量条**: 在 Gizmo 上直接显示当前能量百分比和剩余工作时间。 + * **拖动设置**: 允许玩家通过拖动条设置“自动充电阈值”(例如:低于 30% 去充电)。 + * **模式切换**: 点击图标弹出 `FloatMenu` 选择工作模式。 + * **批量操作**: 当选中多个同类机械体时,Gizmo 操作应同步应用到所有选中的单位。 + +### 2.2 列表视图增强 (`PawnColumnWorker`) +* **目标**: 在“动物/机械体”概览面板中提供关键信息。 +* **实现**: + * `PawnColumnWorker_DroneEnergy`: 显示能量条。 + * `PawnColumnWorker_DroneWorkMode`: 显示当前工作模式图标,点击可快速切换。 + +## 3. AI 行为优化 + +### 3.1 智能充电与休眠 +* **目标**: 防止机械体在工作途中突然断电倒地。 +* **实现**: + * **低电量保护**: 当能量低于临界值(如 5%)且无法到达充电站时,自动寻找最近的安全地点(如室内、屋顶下)进入休眠状态 (`JobDriver_DroneSelfShutdown`)。 + * **智能充电**: 优化 `JobGiver_GetDroneEnergy`,根据距离和当前工作优先级动态决定何时去充电。 + +### 3.2 永远可控 (`EverControllable`) +* **目标**: 确保无论发生什么(如断网、无监管者),玩家始终能控制机械体。 +* **实现**: + * 参考 `AncotPatch_MechanitorUtility_EverControllable`,通过 Harmony 补丁强制 `MechanitorUtility.EverControllable` 返回 true。 + +## 4. 实施步骤 + +1. **定义 Defs**: 创建 `DroneWorkModeDef` 及相关 XML 配置。 +2. **重构 Comp**: 修改 `CompAutonomousMech` 以支持新的 Def 和逻辑。 +3. **UI 开发**: 实现 `DroneGizmo` 和 `PawnColumnWorker`。 +4. **AI 移植**: 移植并适配 `JobDriver_DroneSelfShutdown` 和相关 ThinkNodes。 +5. **补丁完善**: 添加 `EverControllable` 等缺失的 Harmony 补丁。 +6. **测试与验证**: 确保新旧系统平滑过渡,无红字报错。 diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_Alert_MechLacksOverseer.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_Alert_MechLacksOverseer.cs new file mode 100644 index 00000000..c9fd1309 --- /dev/null +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_Alert_MechLacksOverseer.cs @@ -0,0 +1,56 @@ +using HarmonyLib; +using RimWorld; +using RimWorld.Planet; +using System.Collections.Generic; +using System.Linq; +using Verse; + +namespace WulaFallenEmpire +{ + [HarmonyPatch(typeof(Alert_SubjectHasNowOverseer), "GetReport")] + public static class Patch_Alert_SubjectHasNowOverseer + { + [HarmonyPostfix] + public static void Postfix(ref AlertReport __result) + { + if (!__result.active) + { + return; + } + + // AlertReport 是一个结构体,没有 culprits 属性,需要通过 AllCulprits 获取 + // 并且 AlertReport 的字段是只读的或者不方便直接修改,所以我们需要重新构建 + + List allCulprits = __result.AllCulprits.ToList(); + bool changed = false; + + for (int i = allCulprits.Count - 1; i >= 0; i--) + { + Pawn pawn = allCulprits[i].Thing as Pawn; + if (pawn != null) + { + var comp = pawn.GetComp(); + // 如果是自主机械体,且允许自主工作,则从警报列表中移除 + if (comp != null && comp.CanBeAutonomous) + { + allCulprits.RemoveAt(i); + changed = true; + } + } + } + + // 如果列表发生了变化,重新生成 AlertReport + if (changed) + { + if (allCulprits.Count > 0) + { + __result = AlertReport.CulpritsAre(allCulprits); + } + else + { + __result = AlertReport.Inactive; + } + } + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MainTabWindow_Mechs_Pawns.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MainTabWindow_Mechs_Pawns.cs new file mode 100644 index 00000000..dcaae258 --- /dev/null +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MainTabWindow_Mechs_Pawns.cs @@ -0,0 +1,23 @@ +using HarmonyLib; +using RimWorld; +using System.Collections.Generic; +using System.Linq; +using Verse; + +namespace WulaFallenEmpire +{ + [HarmonyPatch(typeof(MainTabWindow_Mechs), "Pawns", MethodType.Getter)] + public static class Patch_MainTabWindow_Mechs_Pawns + { + [HarmonyPostfix] + public static void Postfix(ref IEnumerable __result) + { + // 获取所有自主机械体 + var autonomousMechs = Find.CurrentMap.mapPawns.PawnsInFaction(Faction.OfPlayer) + .Where(p => p.RaceProps.IsMechanoid && p.GetComp()?.CanBeAutonomous == true); + + // 将自主机械体合并到结果中,并去重 + __result = __result.Concat(autonomousMechs).Distinct(); + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MechanitorUtility_EverControllable.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MechanitorUtility_EverControllable.cs new file mode 100644 index 00000000..78978c40 --- /dev/null +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MechanitorUtility_EverControllable.cs @@ -0,0 +1,19 @@ +using HarmonyLib; +using RimWorld; +using Verse; + +namespace WulaFallenEmpire +{ + [HarmonyPatch(typeof(MechanitorUtility), "EverControllable")] + public static class Patch_MechanitorUtility_EverControllable + { + [HarmonyPostfix] + public static void Postfix(Pawn mech, ref bool __result) + { + if (!__result && mech.TryGetComp() != null) + { + __result = true; + } + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/CompAutonomousMech.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/CompAutonomousMech.cs index 0db2d616..3c68123b 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/CompAutonomousMech.cs +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/CompAutonomousMech.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using RimWorld; +using UnityEngine; using Verse; using Verse.AI; @@ -30,14 +31,6 @@ namespace WulaFallenEmpire } } - // 新增:自主工作模式枚举 - public enum AutonomousWorkMode - { - Work, // 工作模式:通过 thinktree 寻找工作 - Recharge, // 充电模式:优先充电,完成后休眠 - Shutdown // 关机模式:立即休眠 - } - public class CompProperties_AutonomousMech : CompProperties { public bool enableAutonomousDrafting = true; @@ -50,6 +43,8 @@ namespace WulaFallenEmpire public float criticalEnergyThreshold = 0.1f; // 临界能量阈值 public float rechargeCompleteThreshold = 0.9f; // 充电完成阈值 + public DroneWorkModeDef initialWorkMode; + public CompProperties_AutonomousMech() { compClass = typeof(CompAutonomousMech); @@ -62,7 +57,7 @@ namespace WulaFallenEmpire public Pawn MechPawn => parent as Pawn; - private AutonomousWorkMode currentWorkMode = AutonomousWorkMode.Work; + private DroneWorkModeDef currentWorkMode; private bool wasLowEnergy = false; // 记录上次是否处于低能量状态 public bool CanBeAutonomous @@ -147,7 +142,7 @@ namespace WulaFallenEmpire } } - public AutonomousWorkMode CurrentWorkMode => currentWorkMode; + public DroneWorkModeDef CurrentWorkMode => currentWorkMode; // 新增:能量状态检查方法 public float GetEnergyLevel() @@ -164,6 +159,11 @@ namespace WulaFallenEmpire { base.PostSpawnSetup(respawningAfterLoad); + if (currentWorkMode == null) + { + currentWorkMode = Props.initialWorkMode ?? WulaDefOf.Work; + } + // 确保使用独立战斗系统 InitializeAutonomousCombat(); } @@ -208,10 +208,10 @@ namespace WulaFallenEmpire if (isLowEnergyNow) { // 进入低能量状态 - if (currentWorkMode == AutonomousWorkMode.Work) + if (currentWorkMode == WulaDefOf.Work) { // 自动切换到充电模式 - SetWorkMode(AutonomousWorkMode.Recharge); + SetWorkMode(WulaDefOf.Recharge); Messages.Message("WULA_LowEnergySwitchToRecharge".Translate(MechPawn.LabelCap), MechPawn, MessageTypeDefOf.CautionInput); } @@ -219,10 +219,10 @@ namespace WulaFallenEmpire else { // 恢复能量状态 - if (currentWorkMode == AutonomousWorkMode.Recharge && IsFullyCharged) + if (currentWorkMode == WulaDefOf.Recharge && IsFullyCharged) { // 充满电后自动切换回工作模式 - SetWorkMode(AutonomousWorkMode.Work); + SetWorkMode(WulaDefOf.Work); Messages.Message("WULA_FullyChargedSwitchToWork".Translate(MechPawn.LabelCap), MechPawn, MessageTypeDefOf.PositiveEvent); } @@ -232,12 +232,12 @@ namespace WulaFallenEmpire } // 临界能量警告 - if (IsCriticalEnergy && currentWorkMode != AutonomousWorkMode.Recharge && currentWorkMode != AutonomousWorkMode.Shutdown) + if (IsCriticalEnergy && currentWorkMode != WulaDefOf.Recharge && currentWorkMode != WulaDefOf.Shutdown) { Messages.Message("WULA_CriticalEnergyLevels".Translate(MechPawn.LabelCap), MechPawn, MessageTypeDefOf.ThreatBig); // 强制切换到充电模式 - SetWorkMode(AutonomousWorkMode.Recharge); + SetWorkMode(WulaDefOf.Recharge); } } @@ -249,77 +249,11 @@ namespace WulaFallenEmpire // 工作模式切换按钮 if (CanWorkAutonomously) { - string energyInfo = "WULA_EnergyInfo".Translate(GetEnergyLevel().ToStringPercent()); - yield return new Command_Action - { - defaultLabel = "WULA_Mech_WorkMode".Translate(GetCurrentWorkModeDisplay()) + energyInfo, - defaultDesc = GetWorkModeDescription(), - icon = GetWorkModeIcon(), - action = () => ShowWorkModeMenu() - }; + yield return new DroneGizmo(this); } } - // 修改:返回包含能量信息的描述 - private string GetWorkModeDescription() - { - string baseDesc = "WULA_Switch_Mech_WorkMode".Translate(); - string energyInfo = "WULA_CurrentEnergy".Translate(GetEnergyLevel().ToStringPercent()); - - if (IsLowEnergy) - energyInfo += "WULA_EnergyLow".Translate(); - if (IsCriticalEnergy) - energyInfo += "WULA_EnergyCritical".Translate(); - - return baseDesc + "\n" + energyInfo; - } - - // 新增:根据能量状态返回不同的图标 - private UnityEngine.Texture2D GetWorkModeIcon() - { - if (IsCriticalEnergy) - return TexCommand.DesirePower; - else if (IsLowEnergy) - return TexCommand.ToggleVent; - else - return TexCommand.Attack; - } - - private string GetCurrentWorkModeDisplay() - { - switch (currentWorkMode) - { - case AutonomousWorkMode.Work: - return "WULA_WorkMode_Work".Translate(); - case AutonomousWorkMode.Recharge: - return "WULA_WorkMode_Recharge".Translate(); - case AutonomousWorkMode.Shutdown: - return "WULA_WorkMode_Shutdown".Translate(); - default: - return "WULA_WorkMode_Unknown".Translate(); - } - } - - private void ShowWorkModeMenu() - { - List list = new List(); - - // 工作模式 - list.Add(new FloatMenuOption("WULA_WorkMode_Work_Desc".Translate(), - () => SetWorkMode(AutonomousWorkMode.Work))); - - // 充电模式 - list.Add(new FloatMenuOption("WULA_WorkMode_Recharge_Desc".Translate(), - () => SetWorkMode(AutonomousWorkMode.Recharge))); - - // 休眠模式 - list.Add(new FloatMenuOption("WULA_WorkMode_Shutdown_Desc".Translate(), - () => SetWorkMode(AutonomousWorkMode.Shutdown))); - - Find.WindowStack.Add(new FloatMenu(list)); - } - - private void SetWorkMode(AutonomousWorkMode mode) + public void SetWorkMode(DroneWorkModeDef mode) { currentWorkMode = mode; @@ -329,8 +263,7 @@ namespace WulaFallenEmpire MechPawn.jobs.StopAll(); } - string modeName = GetCurrentWorkModeDisplay(); - Messages.Message("WULA_SwitchedToMode".Translate(MechPawn.LabelCap, modeName), + Messages.Message("WULA_SwitchedToMode".Translate(MechPawn.LabelCap, mode.label), MechPawn, MessageTypeDefOf.NeutralEvent); } @@ -352,13 +285,13 @@ namespace WulaFallenEmpire if (MechPawn.Drafted) return "WULA_Autonomous_Drafted".Translate() + energyInfo; else - return "WULA_Autonomous_Mode".Translate(GetCurrentWorkModeDisplay()) + energyInfo; + return "WULA_Autonomous_Mode".Translate(currentWorkMode?.label ?? "Unknown") + energyInfo; } public override void PostExposeData() { base.PostExposeData(); - Scribe_Values.Look(ref currentWorkMode, "currentWorkMode", AutonomousWorkMode.Work); + Scribe_Defs.Look(ref currentWorkMode, "currentWorkMode"); Scribe_Values.Look(ref wasLowEnergy, "wasLowEnergy", false); } } diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneGizmo.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneGizmo.cs new file mode 100644 index 00000000..d73d3ef4 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneGizmo.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Verse; + +namespace WulaFallenEmpire +{ + [StaticConstructorOnStartup] + public class DroneGizmo : Gizmo + { + private CompAutonomousMech comp; + private HashSet groupedComps; + + private static readonly Texture2D BarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.34f, 0.42f, 0.43f)); + private static readonly Texture2D BarHighlightTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.43f, 0.54f, 0.55f)); + private static readonly Texture2D EmptyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.03f, 0.035f, 0.05f)); + // private static readonly Texture2D DragBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.74f, 0.97f, 0.8f)); + + // private static bool draggingBar; + + public DroneGizmo(CompAutonomousMech comp) + { + this.comp = comp; + } + + public override float GetWidth(float maxWidth) + { + return 160f; + } + + public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms) + { + Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f); + Rect rect2 = rect.ContractedBy(10f); + Widgets.DrawWindowBackground(rect); + + string text = "WULA_AutonomousMech".Translate(); + Rect rect3 = new Rect(rect2.x, rect2.y, rect2.width, Text.CalcHeight(text, rect2.width) + 8f); + Text.Font = GameFont.Small; + Widgets.Label(rect3, text); + + Rect rect4 = new Rect(rect2.x, rect3.yMax, rect2.width, rect2.height - rect3.height); + DraggableBarForGroup(rect4); + + Text.Anchor = TextAnchor.MiddleCenter; + string energyText = comp.GetEnergyLevel().ToStringPercent(); + Widgets.Label(rect4, energyText); + Text.Anchor = TextAnchor.UpperLeft; + + TooltipHandler.TipRegion(rect4, () => "WULA_EnergyInfo".Translate(energyText), Gen.HashCombineInt(comp.GetHashCode(), 34242419)); + + // Work Mode Button + Rect rect6 = new Rect(rect2.x + rect2.width - 24f, rect2.y, 24f, 24f); + if (Widgets.ButtonImageFitted(rect6, comp.CurrentWorkMode?.uiIcon ?? BaseContent.BadTex)) + { + Find.WindowStack.Add(new FloatMenu(GetWorkModeOptions(comp, groupedComps).ToList())); + } + TooltipHandler.TipRegion(rect6, "WULA_Switch_Mech_WorkMode".Translate()); + Widgets.DrawHighlightIfMouseover(rect6); + + return new GizmoResult(GizmoState.Clear); + } + + private void DraggableBarForGroup(Rect rect) + { + // We are not actually dragging the energy level, but maybe a threshold? + // For now, just display the energy level. + // If we want to set recharge threshold, we need a property in CompAutonomousMech for that. + // Assuming we want to visualize energy level: + + Widgets.FillableBar(rect, comp.GetEnergyLevel(), BarTex, EmptyBarTex, false); + } + + public static IEnumerable GetWorkModeOptions(CompAutonomousMech comp, HashSet groupedComps = null) + { + foreach (DroneWorkModeDef mode in DefDatabase.AllDefs.OrderBy(d => d.uiOrder)) + { + yield return new FloatMenuOption(mode.LabelCap, delegate + { + comp.SetWorkMode(mode); + if (groupedComps != null) + { + foreach (CompAutonomousMech groupedComp in groupedComps) + { + groupedComp.SetWorkMode(mode); + } + } + }, mode.uiIcon, Color.white); + } + } + + public override bool GroupsWith(Gizmo other) + { + return other is DroneGizmo; + } + + public override void MergeWith(Gizmo other) + { + base.MergeWith(other); + if (other is DroneGizmo droneGizmo) + { + if (groupedComps == null) + { + groupedComps = new HashSet(); + } + groupedComps.Add(droneGizmo.comp); + if (droneGizmo.groupedComps != null) + { + groupedComps.AddRange(droneGizmo.groupedComps); + } + } + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneWorkModeDef.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneWorkModeDef.cs new file mode 100644 index 00000000..bd13d310 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneWorkModeDef.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using Verse; + +namespace WulaFallenEmpire +{ + public class DroneWorkModeDef : Def + { + [NoTranslate] + public string iconPath; + + public Texture2D uiIcon; + + public int uiOrder; + + public override void PostLoad() + { + if (!string.IsNullOrEmpty(iconPath)) + { + LongEventHandler.ExecuteWhenFinished(delegate + { + uiIcon = ContentFinder.Get(iconPath); + }); + } + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobDriver_DroneSelfShutdown.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobDriver_DroneSelfShutdown.cs new file mode 100644 index 00000000..6205f7df --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobDriver_DroneSelfShutdown.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using RimWorld; +using Verse; +using Verse.AI; + +namespace WulaFallenEmpire +{ + public class JobDriver_DroneSelfShutdown : JobDriver + { + public const TargetIndex RestSpotIndex = TargetIndex.A; + + public override bool TryMakePreToilReservations(bool errorOnFailed) + { + return pawn.Reserve(base.TargetA, job, 1, -1, null, errorOnFailed); + } + + protected override IEnumerable MakeNewToils() + { + yield return Toils_Goto.GotoCell(TargetIndex.A, PathEndMode.OnCell); + Toil layDown = SelfShutdown(); + layDown.PlaySoundAtStart(SoundDefOf.MechSelfShutdown); + yield return layDown; + } + + public static Toil SelfShutdown() + { + Toil layDown = ToilMaker.MakeToil("WULA_DroneSelfShutdown"); + layDown.initAction = delegate + { + Pawn actor = layDown.actor; + actor.pather?.StopDead(); + JobDriver curDriver = actor.jobs.curDriver; + actor.jobs.posture = PawnPosture.Standing; + actor.mindState.lastBedDefSleptIn = null; + curDriver.asleep = true; + }; + layDown.defaultCompleteMode = ToilCompleteMode.Never; + layDown.AddFinishAction(delegate + { + layDown.actor.jobs.curDriver.asleep = false; + }); + return layDown; + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobGiver_DroneSelfShutdown.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobGiver_DroneSelfShutdown.cs new file mode 100644 index 00000000..9edf3ba3 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobGiver_DroneSelfShutdown.cs @@ -0,0 +1,20 @@ +using RimWorld; +using Verse; +using Verse.AI; + +namespace WulaFallenEmpire +{ + public class JobGiver_DroneSelfShutdown : ThinkNode_JobGiver + { + protected override Job TryGiveJob(Pawn pawn) + { + if (RCellFinder.TryFindNearbyMechSelfShutdownSpot(pawn.Position, pawn, pawn.Map, out var result, allowForbidden: true)) + { + Job job = JobMaker.MakeJob(WulaDefOf.WULA_DroneSelfShutdown, result); + job.forceSleep = true; + return job; + } + return null; + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneEnergy.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneEnergy.cs new file mode 100644 index 00000000..b53fca02 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneEnergy.cs @@ -0,0 +1,42 @@ +using RimWorld; +using UnityEngine; +using Verse; + +namespace WulaFallenEmpire +{ + [StaticConstructorOnStartup] + public class PawnColumnWorker_DroneEnergy : PawnColumnWorker + { + private const int Width = 120; + + private const int BarPadding = 4; + + public static readonly Texture2D EnergyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color32(252, byte.MaxValue, byte.MaxValue, 65)); + + public override void DoCell(Rect rect, Pawn pawn, PawnTable table) + { + CompAutonomousMech comp = pawn.TryGetComp(); + if (comp == null || !comp.CanBeAutonomous) + { + return; + } + + Widgets.FillableBar(rect.ContractedBy(4f), comp.GetEnergyLevel(), EnergyBarTex, BaseContent.ClearTex, doBorder: false); + Text.Font = GameFont.Small; + Text.Anchor = TextAnchor.MiddleCenter; + Widgets.Label(rect, comp.GetEnergyLevel().ToStringPercent()); + Text.Anchor = TextAnchor.UpperLeft; + Text.Font = GameFont.Small; + } + + public override int GetMinWidth(PawnTable table) + { + return Mathf.Max(base.GetMinWidth(table), 120); + } + + public override int GetMaxWidth(PawnTable table) + { + return Mathf.Min(base.GetMaxWidth(table), GetMinWidth(table)); + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneWorkMode.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneWorkMode.cs new file mode 100644 index 00000000..a7e88e6f --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneWorkMode.cs @@ -0,0 +1,42 @@ +using System.Linq; +using RimWorld; +using UnityEngine; +using Verse; + +namespace WulaFallenEmpire +{ + public class PawnColumnWorker_DroneWorkMode : PawnColumnWorker_Icon + { + protected override int Padding => 0; + + public override void DoCell(Rect rect, Pawn pawn, PawnTable table) + { + CompAutonomousMech comp = pawn.TryGetComp(); + if (comp == null || !comp.CanBeAutonomous) + { + return; + } + + if (Widgets.ButtonInvisible(rect)) + { + Find.WindowStack.Add(new FloatMenu(DroneGizmo.GetWorkModeOptions(comp).ToList())); + } + base.DoCell(rect, pawn, table); + } + + protected override Texture2D GetIconFor(Pawn pawn) + { + return pawn?.TryGetComp()?.CurrentWorkMode?.uiIcon; + } + + protected override string GetIconTip(Pawn pawn) + { + string text = pawn.TryGetComp()?.CurrentWorkMode?.description; + if (!text.NullOrEmpty()) + { + return text; + } + return null; + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs index 13f5536f..a4fbcd23 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs @@ -6,7 +6,7 @@ namespace WulaFallenEmpire { public class ThinkNode_ConditionalAutonomousWorkMode : ThinkNode_Conditional { - public AutonomousWorkMode requiredMode = AutonomousWorkMode.Work; + public DroneWorkModeDef requiredMode; protected override bool Satisfied(Pawn pawn) { diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalLowEnergy_Drone.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalLowEnergy_Drone.cs new file mode 100644 index 00000000..94e0af50 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalLowEnergy_Drone.cs @@ -0,0 +1,18 @@ +using Verse; +using Verse.AI; + +namespace WulaFallenEmpire +{ + public class ThinkNode_ConditionalLowEnergy_Drone : ThinkNode_Conditional + { + protected override bool Satisfied(Pawn pawn) + { + CompAutonomousMech compDrone = pawn.TryGetComp(); + if (compDrone != null && compDrone.IsLowEnergy) + { + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalWorkMode_Drone.cs b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalWorkMode_Drone.cs new file mode 100644 index 00000000..3634844d --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalWorkMode_Drone.cs @@ -0,0 +1,32 @@ +using RimWorld; +using Verse; +using Verse.AI; + +namespace WulaFallenEmpire +{ + public class ThinkNode_ConditionalWorkMode_Drone : ThinkNode_Conditional + { + public DroneWorkModeDef workMode; + + public override ThinkNode DeepCopy(bool resolve = true) + { + ThinkNode_ConditionalWorkMode_Drone thinkNode_ConditionalWorkMode_Drone = (ThinkNode_ConditionalWorkMode_Drone)base.DeepCopy(resolve); + thinkNode_ConditionalWorkMode_Drone.workMode = workMode; + return thinkNode_ConditionalWorkMode_Drone; + } + + protected override bool Satisfied(Pawn pawn) + { + if (!pawn.RaceProps.IsMechanoid || pawn.Faction != Faction.OfPlayer) + { + return false; + } + CompAutonomousMech compDrone = pawn.TryGetComp(); + if (compDrone == null) + { + return false; + } + return compDrone.CurrentWorkMode == workMode; + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/WulaDefOf.cs b/Source/WulaFallenEmpire/WulaDefOf.cs index 0c2f2c33..ae59029c 100644 --- a/Source/WulaFallenEmpire/WulaDefOf.cs +++ b/Source/WulaFallenEmpire/WulaDefOf.cs @@ -3,6 +3,7 @@ using Verse; namespace WulaFallenEmpire { + [DefOf] public static class ThingDefOf_WULA { @@ -75,4 +76,18 @@ namespace WulaFallenEmpire } } -} + [DefOf] + public static class WulaDefOf + { + public static JobDef WULA_DroneSelfShutdown; + public static DroneWorkModeDef Work; + public static DroneWorkModeDef Recharge; + public static DroneWorkModeDef Shutdown; + public static DroneWorkModeDef AutoFight; + + static WulaDefOf() + { + DefOfHelper.EnsureInitializedInCtor(typeof(WulaDefOf)); + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj index 40a41b97..1daa7b70 100644 --- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj +++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj @@ -170,6 +170,7 @@ + @@ -180,6 +181,16 @@ + + + + + + + + + +