Files
ArachnaeSwarm/Source/Documents/SpawnPawnFromList_Design.md
2025-09-05 12:57:38 +08:00

5.7 KiB
Raw Blame History

CompSpawnPawnFromList 功能设计文档

1. 功能概述

CompSpawnPawnFromList 是一个 ThingComp 组件,它允许一个特定的 Pawn 通过右键菜单与一个物体交互,从一个可配置的列表中选择一个 Pawn并在经过一段可配置的延迟后生成这个 Pawn。在延迟期间剩余时间会显示在建筑的检查面板上。可以选择在生成 Pawn 后是否摧毁建筑。

2. XML 配置

2.1. 配置属性

  • pawnKinds (List<PawnKindDef>): 一个 PawnKindDef 的列表,用于填充右键菜单的选项。
  • whitelist (List<PawnKindDef>): 一个 PawnKindDef 的白名单,只有在这个列表中的 Pawn 才能看到并使用这个右键菜单。
  • delay (int): 延迟时间,单位为 Ticks (1秒 = 60 Ticks)。
  • destroyOnSpawn (bool): (可选, 默认为 false) 如果为 true,则在生成 Pawn 后摧毁建筑。
  • lordJob (Type): (可选) 生成的 Pawn 要执行的集体任务。

2.2. XML 示例

<ThingDef ParentName="BuildingBase">
  <defName>ARA_PawnSpawner</defName>
  <label>Pawn Spawner</label>
  <description>A device that can be used to spawn pawns after a delay.</description>
  <!-- 其他属性 -->
  <comps>
    <li Class="ArachnaeSwarm.CompProperties_SpawnPawnFromList">
      <pawnKinds>
        <li>Megascarab</li>
        <li>Spelopede</li>
      </pawnKinds>
      <whitelist>
        <li>Colonist</li>
      </whitelist>
      <delay>300</delay> <!-- 5秒 -->
      <destroyOnSpawn>true</destroyOnSpawn>
      <lordJob>RimWorld.LordJob_AssaultColony</lordJob>
    </li>
  </comps>
</ThingDef>

3. 类设计

3.1. CompProperties_SpawnPawnFromList.cs

using System;
using System.Collections.Generic;
using Verse;

namespace ArachnaeSwarm
{
    public class CompProperties_SpawnPawnFromList : CompProperties
    {
        public List<PawnKindDef> pawnKinds;
        public List<PawnKindDef> whitelist;
        public int delay = 0;
        public bool destroyOnSpawn = false;
        public Type lordJob;

        public CompProperties_SpawnPawnFromList()
        {
            compClass = typeof(CompSpawnPawnFromList);
        }

        public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
        {
            foreach (string item in base.ConfigErrors(parentDef))
            {
                yield return item;
            }
            if (lordJob != null && !typeof(RimWorld.LordJob).IsAssignableFrom(lordJob))
            {
                yield return $"lordJob {lordJob} must be of type LordJob";
            }
        }
    }
}

3.2. CompSpawnPawnFromList.cs

using System.Collections.Generic;
using Verse;
using RimWorld;

namespace ArachnaeSwarm
{
    public class CompSpawnPawnFromList : ThingComp
    {
        private CompProperties_SpawnPawnFromList Props => (CompProperties_SpawnPawnFromList)props;

        private int spawnUntilTick = -1;
        private PawnKindDef spawningPawnKind;
        
        public override IEnumerable<FloatMenuOption> CompFloatMenuOptions(Pawn selPawn)
        {
            if (spawnUntilTick > 0)
            {
                yield break; // 正在延迟中,不显示菜单
            }

            if (Props.whitelist == null || !Props.whitelist.Contains(selPawn.kindDef))
            {
                yield break;
            }

            if (Props.pawnKinds != null)
            {
                foreach (PawnKindDef pawnKind in Props.pawnKinds)
                {
                    yield return new FloatMenuOption($"Spawn {pawnKind.label}", () =>
                    {
                        StartDelayedSpawn(pawnKind);
                    });
                }
            }
        }

        private void StartDelayedSpawn(PawnKindDef pawnKind)
        {
            spawningPawnKind = pawnKind;
            spawnUntilTick = Find.TickManager.TicksGame + Props.delay;
        }

        public override void CompTick()
        {
            base.CompTick();
            if (spawnUntilTick > 0 && Find.TickManager.TicksGame >= spawnUntilTick)
            {
                SpawnPawn(spawningPawnKind);
                spawnUntilTick = -1;
                spawningPawnKind = null;
            }
        }

        private void SpawnPawn(PawnKindDef pawnKind)
        {
            Pawn pawn = PawnGenerator.GeneratePawn(new PawnGenerationRequest(pawnKind, parent.Faction));
            GenSpawn.Spawn(pawn, parent.Position, parent.Map);

            if (Props.lordJob != null)
            {
                Lord lord = LordMaker.MakeNewLord(parent.Faction, (LordJob)System.Activator.CreateInstance(Props.lordJob), parent.Map);
                lord.AddPawn(pawn);
            }

            if (Props.destroyOnSpawn)
            {
                parent.Destroy(DestroyMode.Vanish);
            }
        }

        public override string CompInspectStringExtra()
        {
            if (spawnUntilTick > 0)
            {
                int remainingTicks = spawnUntilTick - Find.TickManager.TicksGame;
                return $"Spawning in: {remainingTicks.ToStringTicksToPeriod()}";
            }
            return base.CompInspectStringExtra();
        }

        public override void PostExposeData()
        {
            base.PostExposeData();
            Scribe_Values.Look(ref spawnUntilTick, "spawnUntilTick", -1);
            Scribe_Defs.Look(ref spawningPawnKind, "spawningPawnKind");
        }
    }
}

4. 使用示例

  1. 一个 Colonist 右键点击 ARA_PawnSpawner,选择 "Spawn Megascarab"。
  2. ARA_PawnSpawner 进入延迟状态。当玩家选中它时,检查面板会显示 "Spawning in: 5 seconds"。
  3. 5 秒后,一个新的 Megascarab 被生成。
  4. 由于 destroyOnSpawntrueARA_PawnSpawner 建筑被摧毁。