zc
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<!-- <ThingDef ParentName="BodyPartProstheticMakeableBase">
|
||||
<defName>WULA_DamageShieldGenerator</defName>
|
||||
<label>乌拉反应护盾发生器</label>
|
||||
<description>一个便携式设备,可以激活并生成一个临时的能量护盾,抵挡即将到来的伤害。</description>
|
||||
<graphicData>
|
||||
<texPath>Wula/Item/WULA_Syhth_Trainer</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</graphicData>
|
||||
<stackLimit>1</stackLimit>
|
||||
<useHitPoints>true</useHitPoints>
|
||||
<healthAffectsPrice>false</healthAffectsPrice>
|
||||
<statBases>
|
||||
<MaxHitPoints>50</MaxHitPoints>
|
||||
<MarketValue>500</MarketValue>
|
||||
<Mass>0.5</Mass>
|
||||
<WorkToMake>1000</WorkToMake>
|
||||
</statBases>
|
||||
<thingCategories>
|
||||
<li>Items</li>
|
||||
</thingCategories>
|
||||
<tradeability>Sellable</tradeability>
|
||||
<comps>
|
||||
<li Class="CompProperties_Usable">
|
||||
<useJob>UseItem</useJob>
|
||||
<useLabel>使用护盾发生器充能反应护盾</useLabel>
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.CompProperties_AddDamageShieldCharges">
|
||||
<hediffDef>WULA_DamageShield</hediffDef>
|
||||
<chargesToAdd>100</chargesToAdd>
|
||||
</li>
|
||||
<li Class="CompProperties_UseEffectDestroySelf" />
|
||||
</comps>
|
||||
</ThingDef> -->
|
||||
</Defs>
|
||||
@@ -467,7 +467,7 @@
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="WULA_ExperienceCore_Weapon_Ranged">
|
||||
<ThingDef Name="WULA_RW_Base_AR" ParentName="WULA_ExperienceCore_Weapon_Ranged">
|
||||
<defName>WULA_RW_Base_AR</defName>
|
||||
<label>DLa-1"页岩"</label>
|
||||
<description>乌拉帝国的基础突击步枪,仍然使用可靠的导气式结构驱动自动射击构件,是一种便宜但好用的武器,通常派发给殖民地作自卫武器之用。它下挂了一个眩光弹发射器,可以发射一枚眩光弹击晕区域内的敌人。</description>
|
||||
@@ -497,7 +497,7 @@
|
||||
</statBases>
|
||||
<verbs>
|
||||
<li>
|
||||
<verbClass>Verb_Shoot</verbClass>
|
||||
<verbClass>WulaFallenEmpire.Verb_ShootWithOffset</verbClass>
|
||||
<hasStandardCommand>true</hasStandardCommand>
|
||||
<defaultProjectile>Bullet_WULA_RW_Base_AR</defaultProjectile>
|
||||
<warmupTime>1</warmupTime>
|
||||
@@ -522,12 +522,19 @@
|
||||
<chargeNoun>眩光弹</chargeNoun>
|
||||
</li>
|
||||
</comps>
|
||||
<modExtensions>
|
||||
<li Class="WulaFallenEmpire.ModExtension_ShootWithOffset">
|
||||
<offsets>
|
||||
<li>(0, -0.5)</li>
|
||||
</offsets>
|
||||
</li>
|
||||
</modExtensions>
|
||||
</ThingDef>
|
||||
<!-- 给猫猫的特殊武器,扔在地上会消失、没有人格核心、没有闪光弹 -->
|
||||
<ThingDef ParentName="BaseHumanMakeableGun">
|
||||
<ThingDef ParentName="WULA_RW_Base_AR">
|
||||
<destroyOnDrop>true</destroyOnDrop>
|
||||
<defName>WULA_RW_Base_AR_For_Cat</defName>
|
||||
<label>DLa-1"页岩"</label>
|
||||
<label>DLa-2"页岩"</label>
|
||||
<description>乌拉帝国的基础突击步枪,仍然使用可靠的导气式结构驱动自动射击构件,是一种便宜但好用的武器,通常派发给殖民地作自卫武器之用。</description>
|
||||
<tickerType>Normal</tickerType>
|
||||
<techLevel>Spacer</techLevel>
|
||||
@@ -575,6 +582,8 @@
|
||||
<weaponTags Inherit="False">
|
||||
<li>Wula_Assault_Cat_Weapon</li>
|
||||
</weaponTags>
|
||||
<comps Inherit="False">
|
||||
</comps>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="BaseBullet">
|
||||
<defName>Bullet_WULA_RW_Base_AR</defName>
|
||||
@@ -682,7 +691,7 @@
|
||||
</costList>
|
||||
<verbs>
|
||||
<li>
|
||||
<verbClass>WulaFallenEmpire.Verb_ShootShotgun</verbClass>
|
||||
<verbClass>WulaFallenEmpire.Verb_ShootShotgunWithOffset</verbClass>
|
||||
<hasStandardCommand>true</hasStandardCommand>
|
||||
<defaultProjectile>WULA_Bullet_StarDrift_Shotgun_Spear</defaultProjectile>
|
||||
<warmupTime>0.2</warmupTime>
|
||||
@@ -701,6 +710,13 @@
|
||||
<ticksPerFrame>8</ticksPerFrame>
|
||||
</li>
|
||||
</comps>
|
||||
<modExtensions>
|
||||
<li Class="WulaFallenEmpire.ModExtension_ShootWithOffset">
|
||||
<offsets>
|
||||
<li>(0.1, -1.0)</li>
|
||||
</offsets>
|
||||
</li>
|
||||
</modExtensions>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="BaseBullet">
|
||||
<defName>WULA_Bullet_StarDrift_Shotgun_Spear</defName>
|
||||
@@ -763,7 +779,7 @@
|
||||
</costList>
|
||||
<verbs>
|
||||
<li>
|
||||
<verbClass>Verb_Shoot</verbClass>
|
||||
<verbClass>WulaFallenEmpire.Verb_ShootWithOffset</verbClass>
|
||||
<hasStandardCommand>true</hasStandardCommand>
|
||||
<defaultProjectile>Bullet_WULA_RW_Fractal_RF</defaultProjectile>
|
||||
<warmupTime>4</warmupTime>
|
||||
@@ -784,6 +800,13 @@
|
||||
<ticksPerFrame>8</ticksPerFrame>
|
||||
</li>
|
||||
</comps>
|
||||
<modExtensions>
|
||||
<li Class="WulaFallenEmpire.ModExtension_ShootWithOffset">
|
||||
<offsets>
|
||||
<li>(0, -1.0)</li>
|
||||
</offsets>
|
||||
</li>
|
||||
</modExtensions>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="BaseBullet">
|
||||
<defName>Bullet_WULA_RW_Fractal_RF</defName>
|
||||
@@ -1024,9 +1047,9 @@
|
||||
<modExtensions>
|
||||
<li Class="WulaFallenEmpire.ModExtension_ShootWithOffset">
|
||||
<offsets>
|
||||
<li>(0.1, -2.6)</li>
|
||||
<li>(-0.05, -2.5)</li>
|
||||
</offsets>
|
||||
</li>
|
||||
</li>
|
||||
</modExtensions>
|
||||
<comps>
|
||||
<li Class="WulaFallenEmpire.CompProperties_WeaponRenderDynamic">
|
||||
@@ -1059,7 +1082,7 @@
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
<shaderType>MoteGlow</shaderType>
|
||||
<color>(165, 44, 2, 255)</color>
|
||||
<drawSize>(1.4,7)</drawSize>
|
||||
<drawSize>(1.4,4)</drawSize>
|
||||
</graphicData>
|
||||
<projectile>
|
||||
<damageDef>BeamBypassShields</damageDef>
|
||||
@@ -1145,10 +1168,6 @@
|
||||
<pelletCount>3</pelletCount>
|
||||
</li>
|
||||
</modExtensions>
|
||||
<graphicData>
|
||||
<texPath>Things/Projectile/ChargeLanceShot</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</graphicData>
|
||||
<projectile>
|
||||
<damageDef>BeamBypassShields</damageDef>
|
||||
<damageAmountBase>2</damageAmountBase>
|
||||
@@ -1239,10 +1258,6 @@
|
||||
<beamStartOffset>0.5</beamStartOffset>
|
||||
</li>
|
||||
</modExtensions>
|
||||
<graphicData>
|
||||
<texPath>Things/Projectile/ChargeLanceShot</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</graphicData>
|
||||
<projectile>
|
||||
<damageDef>BeamBypassShields</damageDef>
|
||||
<damageAmountBase>3</damageAmountBase>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
# 自主机械体系统改进计划
|
||||
|
||||
基于对 `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. **测试与验证**: 确保新旧系统平滑过渡,无红字报错。
|
||||
@@ -1,588 +0,0 @@
|
||||
# Wula Fallen Empire - 事件系统文档
|
||||
|
||||
这是一个用于在RimWorld中创建复杂、带选项的事件和对话框的强大系统。它由两个主要部分组成:**任务事件** 和 **EventDef事件**。
|
||||
|
||||
## 核心概念
|
||||
|
||||
- **Effect(效果)**: 一个原子操作,例如生成一个Pawn、给予一个物品、改变派系关系或打开另一个UI。
|
||||
- **Condition(条件)**: 一个用于决定一个选项是否可用的逻辑检查(例如,检查一个变量的值)。
|
||||
- **EventContext(事件上下文)**: 一个全局的静态类,用于存储和检索变量,允许在不同的事件和UI之间传递数据。
|
||||
|
||||
---
|
||||
|
||||
## 1. 任务事件 (`QuestNode_Root_EventLetter`)
|
||||
|
||||
这是通过RimWorld的原版任务系统触发的事件。它会生成一个带有选项的信件。
|
||||
|
||||
### 如何使用
|
||||
|
||||
1. 在你的 `QuestScriptDef` 中,使用 `WulaFallenEmpire.QuestNode_Root_EventLetter` 作为根节点。
|
||||
2. 在XML中定义 `letterLabel`, `letterTitle`, `letterText`。
|
||||
3. 在 `<options>` 列表中定义多个选项。每个选项都有一个 `label` 和一个或多个 `effects`。
|
||||
|
||||
### 示例 (`QuestScriptDef`)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<QuestScriptDef>
|
||||
<defName>Wula_ExampleQuestEvent</defName>
|
||||
<root Class="WulaFallenEmpire.QuestNode_Root_EventLetter">
|
||||
<letterLabel>一个抉择</letterLabel>
|
||||
<letterTitle>远方的呼唤</letterTitle>
|
||||
<letterText>一个来自遥远星系的信号抵达了你们的通讯站。他们似乎想和你们谈谈。</letterText>
|
||||
<options>
|
||||
<li>
|
||||
<label>接受通讯</label>
|
||||
<optionEffects>
|
||||
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
|
||||
<defName>Wula_ExampleEvent</defName>
|
||||
</li>
|
||||
</optionEffects>
|
||||
</li>
|
||||
<li>
|
||||
<label>忽略他们</label>
|
||||
<optionEffects>
|
||||
<li Class="WulaFallenEmpire.Effect_ShowMessage">
|
||||
<message>你决定无视这个信号。宇宙的寂静再次笼罩着你。</message>
|
||||
<messageTypeDef>NeutralEvent</messageTypeDef>
|
||||
</li>
|
||||
</optionEffects>
|
||||
</li>
|
||||
</options>
|
||||
</root>
|
||||
</QuestScriptDef>
|
||||
</Defs>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. EventDef事件 (`Dialog_CustomDisplay`)
|
||||
|
||||
这是一个高度可定制的对话框窗口,可以显示角色肖像、背景、文本和多个带条件的选项。
|
||||
|
||||
### 如何使用
|
||||
|
||||
1. 创建一个 `EventDef`。
|
||||
2. 定义 `label`, `characterName`, `portraitPath`, `descriptions` 等。
|
||||
3. 在 `<options>` 列表中定义选项。每个选项可以有关联的 `effects` 和 `conditions`。
|
||||
4. 你可以通过 `Effect_OpenCustomUI` 效果来打开这个UI(从任务事件或其他EventDef)。
|
||||
5. 你也可以通过将 `CompOpenCustomUI` 附加到一个建筑上来从游戏中直接打开它。
|
||||
|
||||
### `EventDef` 参数
|
||||
|
||||
- **defName**: (string) Def的唯一标识符。
|
||||
- **label**: (string) 窗口的标题。
|
||||
- **characterName**: (string) (可选) 显示在肖像下方的角色名称。
|
||||
- **portraitPath**: (string) (可选) 角色肖像的纹理路径。
|
||||
- **backgroundImagePath**: (string) (可选) 对话框的背景图片路径。
|
||||
- **descriptions**: (List<string>) 一个描述文本列表。
|
||||
- **descriptionMode**: (enum) (可选) 决定如何从 `descriptions` 列表中选择文本。可以是 `Random` (默认) 或 `Sequential`。
|
||||
- **conditionalDescriptions**: (List<ConditionalDescription>) (可选) 一个条件描述列表,允许你根据特定条件在主描述后附加额外的文本块。
|
||||
- **options**: (List<EventOption>) 对话框中显示的选项列表。
|
||||
- **immediateEffects**: (List<ConditionalEffects>) (可选) 当对话框打开时立即执行的效果列表。
|
||||
- **dismissEffects**: (List<ConditionalEffects>) (可选) 当对话框关闭时(通过关闭按钮或`Effect_CloseDialog`)执行的效果列表。
|
||||
- **windowSize**: (Vector2) (可选) 自定义窗口大小。默认为 `(0, 0)`,表示使用默认大小。
|
||||
- **hiddenWindow**: (bool) (可选) 如果为 `true`,则不会显示窗口。在这种模式下,`immediateEffects` 的内容会在加载时自动合并到 `dismissEffects` 中,然后在事件触发时作为单个效果链统一执行。这对于创建纯粹的后台“效果链”事件非常有用。默认为 `false`。
|
||||
|
||||
### `EventOption` 参数
|
||||
|
||||
每个选项 (`<li>` in `<options>`) 包含以下字段:
|
||||
|
||||
- **label**: (string) 选项按钮上显示的文本。
|
||||
- **optionEffects**: (List<ConditionalEffects>) 点击该选项时执行的效果列表。
|
||||
- **conditions**: (List<Condition>) (可选) 决定该选项是否可用的条件列表。如果条件不满足,选项会变灰。
|
||||
- **disabledReason**: (string) (可选) 当选项因不满足 `conditions` 而变灰时,鼠标悬停时显示的提示信息。
|
||||
- **hideWhenDisabled**: (bool) (可选) 如果为 `true`,当 `conditions` 不满足时,该选项将完全隐藏而不是变灰。默认为 `false`。
|
||||
|
||||
### `ConditionalDescription` 参数
|
||||
|
||||
每个条件描述 (`<li>` in `<conditionalDescriptions>`) 包含以下字段:
|
||||
|
||||
- **conditions**: (List<Condition>) 决定此额外描述是否显示的条件列表。
|
||||
- **text**: (string) 当条件满足时,要附加到主描述文本末尾的字符串。
|
||||
|
||||
### 关于富文本 (Rich Text) 的使用
|
||||
|
||||
在所有面向玩家的文本字段中(如 `label`, `descriptions`, `characterName`, `disabledReason` 等),你都可以使用Unity的富文本标签,例如 `<color=red>`, `<b>`, `<i>`。
|
||||
|
||||
**重要提示**: 由于 `<` 和 `>` 是XML的特殊字符,你必须将所有包含富文本标签的字符串包裹在 `<![CDATA[...]]>` 块中,以避免XML解析错误。
|
||||
|
||||
**正确示例:**
|
||||
```xml
|
||||
<label><![CDATA[这是一个<color=cyan>彩色</color>的<b>标签</b>]]></label>
|
||||
```
|
||||
|
||||
**错误示例 (会导致XML解析失败):**
|
||||
```xml
|
||||
<label>这是一个<color=cyan>彩色</color>的<b>标签</b></label>
|
||||
```
|
||||
|
||||
### `EventDef` 示例
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<WulaFallenEmpire.EventDef>
|
||||
<defName>Wula_ExampleEvent</defName>
|
||||
<label>神秘的通讯</label>
|
||||
<characterName>特使</characterName>
|
||||
<portraitPath>Textures/Wula/Events/Portraits/Envoy</portraitPath>
|
||||
<descriptions>
|
||||
<li>“你好,来自边缘世界的陌生人。我们观察你很久了。你的挣扎……很有趣。”</li>
|
||||
</descriptions>
|
||||
<immediateEffects>
|
||||
<!-- 这是一个无条件的ConditionalEffects块 -->
|
||||
<li>
|
||||
<!-- 没有<conditions>,所以总是执行 -->
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_SetVariable">
|
||||
<name>MetTheEnvoy</name>
|
||||
<value>true</value>
|
||||
</li>
|
||||
</effects>
|
||||
</li>
|
||||
</immediateEffects>
|
||||
<dismissEffects>
|
||||
<!-- 这是一个有条件的ConditionalEffects块 -->
|
||||
<li>
|
||||
<conditions>
|
||||
<li Class="WulaFallenEmpire.Condition_VariableEquals">
|
||||
<name>PlayerMadeChoice</name>
|
||||
<value>false</value>
|
||||
</li>
|
||||
</conditions>
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_ShowMessage">
|
||||
<message>你没有做出选择就关闭了通讯。</message>
|
||||
</li>
|
||||
</effects>
|
||||
</li>
|
||||
</dismissEffects>
|
||||
<options>
|
||||
<li>
|
||||
<label>“你是谁?”</label>
|
||||
<optionEffects>
|
||||
<li>
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_ShowMessage">
|
||||
<message>“我们是观察者。我们是见证者。现在,我们是你的未来。”</message>
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.Effect_CloseDialog" />
|
||||
</effects>
|
||||
</li>
|
||||
</optionEffects>
|
||||
</li>
|
||||
<li>
|
||||
<label>“给我们一些东西来证明你的诚意。”</label>
|
||||
<disabledReason>他们似乎对你不够信任。</disabledReason>
|
||||
<!-- 这个conditions块现在只用于决定选项是否可点击 -->
|
||||
<conditions>
|
||||
<li Class="WulaFallenEmpire.Condition_VariableGreaterThanOrEqual">
|
||||
<name>EmpireGoodwill</name>
|
||||
<value>50</value>
|
||||
</li>
|
||||
</conditions>
|
||||
<optionEffects>
|
||||
<li>
|
||||
<!-- 你甚至可以在选项的效果内部再次添加条件 -->
|
||||
<conditions>
|
||||
<li Class="WulaFallenEmpire.Condition_VariableEquals">
|
||||
<name>IsGenerous</name>
|
||||
<value>true</value>
|
||||
</li>
|
||||
</conditions>
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_GiveThing">
|
||||
<thingDef>Gold</thingDef>
|
||||
<count>200</count> <!-- 如果IsGenerous为true,则给予更多 -->
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.Effect_CloseDialog" />
|
||||
</effects>
|
||||
</li>
|
||||
<li>
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_GiveThing">
|
||||
<thingDef>Gold</thingDef>
|
||||
<count>100</count>
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.Effect_CloseDialog" />
|
||||
</effects>
|
||||
</li>
|
||||
</optionEffects>
|
||||
</li>
|
||||
</options>
|
||||
</WulaFallenEmpire.EventDef>
|
||||
</Defs>
|
||||
```
|
||||
|
||||
### UI 布局配置 (`EventUIConfigDef`)
|
||||
|
||||
你可以在 `1.6/Defs/WulaMiscSettingDefs/EventUIConfig.xml` 中调整所有EventDef窗口的默认外观和布局。
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心结构: 条件化效果 (`ConditionalEffects`)
|
||||
|
||||
所有执行效果的地方 (`immediateEffects`, `dismissEffects`, 以及每个选项的 `optionEffects`) 都是一个 `ConditionalEffects` 块的列表。
|
||||
|
||||
这允许你将一组效果与一组条件绑定在一起。
|
||||
|
||||
### `ConditionalEffects` 结构
|
||||
|
||||
每个 `<li>` 代表一个 `ConditionalEffects` 块。它包含两个可选部分:
|
||||
- **`<conditions>`**: 一个条件列表。只有当这里的所有条件都满足时,对应的效果才会执行。如果省略这个部分,效果将总是执行。
|
||||
- **`<effects>`**: 一个效果列表。当条件满足时,这些效果会被执行。
|
||||
|
||||
```xml
|
||||
<!-- 示例: 一个ConditionalEffects块 -->
|
||||
<li>
|
||||
<conditions>
|
||||
<li Class="WulaFallenEmpire.Condition_VariableEquals">
|
||||
<name>PlayerChoice</name>
|
||||
<value>AcceptedOffer</value>
|
||||
</li>
|
||||
</conditions>
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_ShowMessage">
|
||||
<message>你接受了提议!</message>
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.Effect_GiveThing">
|
||||
<thingDef>Silver</thingDef>
|
||||
<count>500</count>
|
||||
</li>
|
||||
</effects>
|
||||
</li>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 可用的效果 (`Effect`)
|
||||
|
||||
这些是可以在 `ConditionalEffects` 块的 `<effects>` 列表中使用的类。
|
||||
|
||||
### `Effect_OpenCustomUI`
|
||||
打开一个指定的 `EventDef`。
|
||||
- **defName**: (string) 要打开的 `EventDef` 的 `defName`。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
|
||||
<defName>Wula_AnotherEvent</defName>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_CloseDialog`
|
||||
关闭当前的EventDef窗口。没有参数。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_CloseDialog" />
|
||||
```
|
||||
|
||||
### `Effect_ShowMessage`
|
||||
在屏幕上显示一条消息。
|
||||
- **message**: (string) 要显示的消息文本。
|
||||
- **messageTypeDef**: (MessageTypeDef) 消息的类型 (例如 `PositiveEvent`, `NegativeEvent`, `NeutralEvent`)。默认为 `PositiveEvent`。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_ShowMessage">
|
||||
<message>你获得了一个新的盟友。</message>
|
||||
<messageTypeDef>PositiveEvent</messageTypeDef>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_FireIncident`
|
||||
触发一个指定的事件。
|
||||
- **incident**: (IncidentDef) 要触发的事件的 `defName`。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_FireIncident">
|
||||
<incident>RaidEnemy</incident>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_ChangeFactionRelation`
|
||||
改变玩家与某个派系的关系。
|
||||
- **faction**: (FactionDef) 目标派系的 `defName`。
|
||||
- **goodwillChange**: (int) 关系值的变化量(可以是负数)。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_ChangeFactionRelation">
|
||||
<faction>WulaFallenEmpire_Player</faction>
|
||||
<goodwillChange>15</goodwillChange>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_ChangeFactionRelation_FromVariable`
|
||||
根据一个变量的值改变派系关系。
|
||||
- **faction**: (FactionDef) 目标派系的 `defName`。
|
||||
- **goodwillVariableName**: (string) 存储关系变化值的变量名。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_ChangeFactionRelation_FromVariable">
|
||||
<faction>WulaFallenEmpire_Player</faction>
|
||||
<goodwillVariableName>ReputationChange</goodwillVariableName>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_GiveThing`
|
||||
给玩家一些物品(通过空投)。
|
||||
- **thingDef**: (ThingDef) 要给予的物品的 `defName`。
|
||||
- **count**: (int) 给予的数量。默认为 1。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_GiveThing">
|
||||
<thingDef>Plasteel</thingDef>
|
||||
<count>150</count>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_SpawnPawn`
|
||||
生成一个Pawn。
|
||||
- **kindDef**: (PawnKindDef) 要生成的Pawn的 `defName`。
|
||||
- **count**: (int) 生成的数量。默认为 1。
|
||||
- **joinPlayerFaction**: (bool) 是否加入玩家派系。默认为 `true`。
|
||||
- **letterLabel**: (string) 可选,生成时附带的信件标题。
|
||||
- **letterText**: (string) 可选,生成时附带的信件内容。
|
||||
- **letterDef**: (LetterDef) 可选,信件的类型。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_SpawnPawn">
|
||||
<kindDef>Colonist</kindDef>
|
||||
<count>1</count>
|
||||
<joinPlayerFaction>true</joinPlayerFaction>
|
||||
<letterLabel>一个新人加入了!</letterLabel>
|
||||
<letterText>一个流浪者被你们的善举所吸引,决定加入你们的殖民地。</letterText>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_SpawnPawnAndStore`
|
||||
生成一个Pawn并将其存储在一个变量中以备后用。
|
||||
- **kindDef**: (PawnKindDef) 要生成的Pawn的 `defName`。
|
||||
- **count**: (int) 生成的数量。默认为 1。
|
||||
- **storeAs**: (string) 用于存储生成Pawn的变量名。如果 `count` 大于1,则存储一个Pawn列表。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_SpawnPawnAndStore">
|
||||
<kindDef>Wula_Elite_Warrior</kindDef>
|
||||
<storeAs>spawnedWarrior</storeAs>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_AddQuest`
|
||||
触发一个新的任务。
|
||||
- **quest**: (QuestScriptDef) 要开始的任务的 `defName`。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_AddQuest">
|
||||
<quest>Wula_AnotherQuest</quest>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_FinishResearch`
|
||||
立即完成一个研究项目。
|
||||
- **research**: (ResearchProjectDef) 要完成的研究的 `defName`。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_FinishResearch">
|
||||
<research>MicroelectronicsBasics</research>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_TriggerRaid`
|
||||
触发一次袭击。这个效果有两种模式:
|
||||
1. **简单模式**: 使用派系默认的袭击队伍。
|
||||
2. **高级模式**: 使用动态定义的 `pawnGroupMakers` 来生成自定义的袭击队伍。
|
||||
|
||||
- **points**: (float) 袭击的点数。
|
||||
- **faction**: (FactionDef) 袭击者的派系 `defName`。
|
||||
- **raidStrategy**: (RaidStrategyDef) 袭击策略的 `defName` (例如 `ImmediateAttack`)。
|
||||
- **raidArrivalMode**: (PawnsArrivalModeDef) 袭击者到达方式的 `defName` (例如 `EdgeWalkIn`)。
|
||||
- **groupKind**: (PawnGroupKindDef) (高级模式) 定义队伍类型,例如 `Combat` 或 `Trader`。默认为 `Combat`。
|
||||
- **pawnGroupMakers**: (List<PawnGroupMaker>) (高级模式) 一个 `PawnGroupMaker` 列表,用于动态定义袭击队伍的构成。
|
||||
- **letterLabel**: (string) (可选) 自定义袭击信件的标题。如果提供,将覆盖默认的 "Raid" 标题。
|
||||
- **letterText**: (string) (可选) 自定义袭击信件的内容。如果提供,将覆盖默认的袭击描述文本。
|
||||
|
||||
**简单模式示例:**
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_TriggerRaid">
|
||||
<points>500</points>
|
||||
<faction>Pirate</faction>
|
||||
<raidStrategy>ImmediateAttack</raidStrategy>
|
||||
<raidArrivalMode>EdgeWalkIn</raidArrivalMode>
|
||||
<letterLabel>侦测到威胁!</letterLabel>
|
||||
<letterText>我们的传感器侦测到一伙来自 {FACTION_name} 的袭击者!他们看起来充满敌意,正朝着我们的殖民地前进。</letterText>
|
||||
</li>
|
||||
```
|
||||
|
||||
**高级模式示例:**
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_TriggerRaid">
|
||||
<points>1000</points>
|
||||
<faction>WulaFallenEmpire_Player</faction>
|
||||
<raidStrategy>ImmediateAttack</raidStrategy>
|
||||
<raidArrivalMode>EdgeWalkIn</raidArrivalMode>
|
||||
<groupKind>Combat</groupKind>
|
||||
<pawnGroupMakers>
|
||||
<li>
|
||||
<kindDef>Combat</kindDef>
|
||||
<commonality>100</commonality>
|
||||
<options>
|
||||
<Mech_WULA_Cat_Constructor>20</Mech_WULA_Cat_Constructor>
|
||||
<Mech_WULA_Cat_Assault>20</Mech_WULA_Cat_Assault>
|
||||
<Wula_Broken_Personality_Pawn_7>2</Wula_Broken_Personality_Pawn_7>
|
||||
<Wula_Broken_Personality_Pawn_5>1</Wula_Broken_Personality_Pawn_5>
|
||||
</options>
|
||||
</li>
|
||||
</pawnGroupMakers>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_SetVariable`
|
||||
**仅在变量尚不存在时**,设置一个 `EventContext` 变量的值。这使得它非常适合用于安全地初始化变量,而不用担心覆盖现有值。
|
||||
- **name**: (string) 变量名。
|
||||
- **value**: (string) 变量的初始值。系统会自动尝试将其解析为 `int` 或 `float`,如果失败则作为 `string` 存储。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_SetVariable">
|
||||
<name>MetTheEnvoy</name>
|
||||
<value>true</value>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_ModifyVariable`
|
||||
对一个数字变量进行数学运算或直接赋值。如果变量不存在,会先将其初始化为0,然后再执行操作。
|
||||
- **name**: (string) 变量名。
|
||||
- **value**: (float) 用于操作的数值。
|
||||
- **operation**: (VariableOperation) 操作类型,可以是 `Add`, `Subtract`, `Multiply`, `Divide`, `Set`。
|
||||
- `Set`: 直接将变量的值设置为 `value`,会覆盖任何现有值。
|
||||
|
||||
**加法示例:**
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_ModifyVariable">
|
||||
<name>ResourceCount</name>
|
||||
<value>-10</value>
|
||||
<operation>Add</operation> <!-- 这会从ResourceCount中减去10 -->
|
||||
</li>
|
||||
```
|
||||
**直接设置值示例:**
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_ModifyVariable">
|
||||
<name>ResourceCount</name>
|
||||
<value>100</value>
|
||||
<operation>Set</operation> <!-- 这会将ResourceCount的值直接设为100 -->
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_ClearVariable`
|
||||
从 `EventContext` 中移除一个变量。
|
||||
- **name**: (string) 要清除的变量名。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_ClearVariable">
|
||||
<name>PlayerChoice</name>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_CheckFactionGoodwill`
|
||||
检查玩家与某个派系的好感度,并将其存储在一个变量中。
|
||||
- **factionDef**: (FactionDef) 要检查的派系的 `defName`。
|
||||
- **variableName**: (string) 用于存储好感度数值的变量名。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_CheckFactionGoodwill">
|
||||
<factionDef>Mechanoid</factionDef>
|
||||
<variableName>MechanoidGoodwill</variableName>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Effect_CallSkyfaller`
|
||||
在玩家殖民地附近呼叫一个skyfaller。
|
||||
- **checkClearance**: 确保落点附近X格内净空。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Effect_CallSkyfaller">
|
||||
<skyfallerDef>DropPodIncoming</skyfallerDef>
|
||||
<delayTicks>180</delayTicks>
|
||||
<checkClearance>true</checkClearance>
|
||||
<clearanceRadius>4</clearanceRadius>
|
||||
<letterLabel>空投舱已呼叫</letterLabel>
|
||||
<letterText>轨道空投舱已在途中,预计3秒后抵达。</letterText>
|
||||
</li>
|
||||
```
|
||||
---
|
||||
|
||||
## 4. 可用的条件 (`Condition`)
|
||||
|
||||
这些是可以在 `conditions` 列表中使用的类,用于控制选项的可用性。
|
||||
|
||||
### `Condition_VariableEquals`
|
||||
检查一个变量是否等于一个特定值。
|
||||
- **name**: (string) 要检查的变量名。
|
||||
- **value**: (string) 要比较的字面值。
|
||||
- **valueVariableName**: (string) (可选) 要比较的另一个变量的名称。如果提供此项,则忽略 `value`。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_VariableEquals">
|
||||
<name>PlayerChoice</name>
|
||||
<value>AcceptedOffer</value>
|
||||
</li>
|
||||
```
|
||||
### `Condition_VariableNotEqual`
|
||||
检查一个变量是否 **不等于** 一个特定值。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_VariableNotEqual">
|
||||
<name>QuestStage</name>
|
||||
<value>3</value>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Condition_CompareVariable` (基类)
|
||||
这是一个抽象基类,不应直接使用。以下所有比较条件(大于、小于等)都继承自这个基类,并共享其参数。
|
||||
|
||||
**基类参数:**
|
||||
- **name**: (string) 要检查的变量名。
|
||||
- **value**: (float) 要比较的字面数值。
|
||||
- **valueVariableName**: (string) (可选) 要比较的另一个变量的名称。如果提供此项,则会忽略 `value` 字段。
|
||||
|
||||
**工作原理:**
|
||||
当你使用例如 `Condition_VariableGreaterThan` 时,你实际上是在使用一个 `Condition_CompareVariable` 的特定版本。你可以提供 `value` 来与一个固定的数字比较,或者提供 `valueVariableName` 来与另一个变量的值进行比较。
|
||||
|
||||
**变量与变量比较示例:**
|
||||
下面的例子使用了 `Condition_VariableGreaterThanOrEqual`(它是 `Condition_CompareVariable` 的子类),来检查 `PlayerWealth` 变量是否大于或等于 `RequiredWealth` 变量。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_VariableGreaterThanOrEqual">
|
||||
<name>PlayerWealth</name>
|
||||
<valueVariableName>RequiredWealth</valueVariableName>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Condition_VariableGreaterThan`
|
||||
检查一个变量是否 **大于** 一个特定值。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_VariableGreaterThan">
|
||||
<name>ColonistCount</name>
|
||||
<value>5</value>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Condition_VariableLessThan`
|
||||
检查一个变量是否 **小于** 一个特定值。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_VariableLessThan">
|
||||
<name>ThreatPoints</name>
|
||||
<value>1000</value>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Condition_VariableGreaterThanOrEqual`
|
||||
检查一个变量是否 **大于或等于** 一个特定值。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_VariableGreaterThanOrEqual">
|
||||
<name>EmpireGoodwill</name>
|
||||
<value>50</value>
|
||||
</li>
|
||||
```
|
||||
|
||||
### `Condition_VariableLessThanOrEqual`
|
||||
检查一个变量是否 **小于或等于** 一个特定值。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_VariableLessThanOrEqual">
|
||||
<name>YearsPassed</name>
|
||||
<value>2</value>
|
||||
</li>
|
||||
```
|
||||
---
|
||||
|
||||
---
|
||||
### `Condition_FactionExists`
|
||||
检查一个派系当前是否存在于游戏中。
|
||||
- **factionDef**: (FactionDef) 要检查的派系的 `defName`。
|
||||
```xml
|
||||
<li Class="WulaFallenEmpire.Condition_FactionExists">
|
||||
<factionDef>Pirate</factionDef>
|
||||
</li>
|
||||
```
|
||||
@@ -1,122 +0,0 @@
|
||||
# 全局工作台项目总结
|
||||
|
||||
## 1. 项目目标
|
||||
|
||||
最初的目标是为 RimWorld 模组 `WulaFallenEmpire` 实现一个“全局生产与存储系统”。核心思想是:
|
||||
* 玩家在本地工作台消耗材料,但实际的生产过程在“云端”进行。
|
||||
* 云端生产完成后,产品存储在全局存储中,玩家可以通过空投取回。
|
||||
* UI 界面需要能够管理云端订单,并显示生产进度。
|
||||
|
||||
在项目进行过程中,用户对流程的期望逐渐明确为:
|
||||
1. 点击“添加订单”按钮。
|
||||
2. 小人创建一个材料收集订单,将材料运送到全局工作台。
|
||||
3. 材料消耗后,本地订单完成。
|
||||
4. 此时,在后端(云端)创建一个生产订单,开始倒计时生产。
|
||||
5. UI 界面需要统一显示“材料准备”、“生产中”、“完成”三个阶段的订单。
|
||||
|
||||
## 2. 已完成的工作和代码修改
|
||||
|
||||
### 2.1. 新增文件
|
||||
|
||||
* **`Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionRecipeExtension.cs` (已创建,后移除)**
|
||||
* 最初用于通过 XML 标记哪些配方是全局生产配方。后因用户反馈“太复杂”而被移除。
|
||||
* **`Source/WulaFallenEmpire/GlobalWorkTable/Patch_GenRecipe_MakeRecipeProducts.cs`**
|
||||
* **目的**:拦截原版 `GenRecipe.MakeRecipeProducts` 方法,实现“前端消耗材料,后端创建订单”的核心逻辑。
|
||||
* **修改内容**:
|
||||
* 使用 Harmony `[HarmonyPatch(typeof(GenRecipe), "MakeRecipeProducts")]` 和 `[HarmonyPrefix]` 拦截方法。
|
||||
* 在 `Prefix` 中,检查 `IBillGiver` 是否为 `Building_GlobalWorkTable`。
|
||||
* 检查配方产物是否带有 `CompProductionCategory` 组件(这是最终确定的判断依据)。
|
||||
* 如果满足条件,阻止原版方法执行 (`return false;`)。
|
||||
* 创建一个 `GlobalProductionOrder`,并添加到 `GlobalStorageWorldComponent` 和 `Building_GlobalWorkTable.globalOrderStack`。
|
||||
* 向玩家发送“订单已创建”的消息。
|
||||
* **`Source/WulaFallenEmpire/WulaStartup.cs` (已创建,后移除)**
|
||||
* 最初用于在游戏启动时自动为配方添加 `GlobalProductionRecipeExtension`。后因用户反馈“太复杂”而被移除。
|
||||
|
||||
### 2.2. 修改文件
|
||||
|
||||
* **`Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrder.cs`**
|
||||
* **目的**:简化云端订单逻辑,使其不再负责资源检查和消耗。
|
||||
* **修改内容**:
|
||||
* 移除了 `ProductionState.Waiting` 状态,订单默认直接进入 `Producing`。
|
||||
* 移除了 `HasEnoughResources()` 和 `ConsumeResources()` 方法。
|
||||
* `GetIngredientsTooltip()` 方法简化为只显示产品和工作量(生产时间)。
|
||||
* `Produce()` 方法直接将产品添加到 `GlobalStorageWorldComponent.outputStorage`。
|
||||
* `GetWorkAmount()` 方法恢复为基于配方或产品属性计算工作量。
|
||||
* **`Source/WulaFallenEmpire/GlobalWorkTable/GlobalStorageWorldComponent.cs`**
|
||||
* **目的**:恢复 `inputStorage`,因为用户反馈其被其他模块使用。
|
||||
* **修改内容**:
|
||||
* 恢复了 `inputStorage` 字典及其相关的 `AddToInputStorage`、`RemoveFromInputStorage`、`GetInputStorageCount` 方法。
|
||||
* 恢复了 `DebugAddTestResources` 调试方法。
|
||||
* **`Source/WulaFallenEmpire/GlobalWorkTable/Building_GlobalWorkTable.cs`**
|
||||
* **目的**:确保工作台与原版 `Bill` 系统正确集成,并触发工作台的视觉/音效反馈。
|
||||
* **修改内容**:
|
||||
* `CurrentlyUsableForGlobalBills()` 方法修改为调用 `base.CurrentlyUsableForBills()`,确保工作台的可用性判断(电力、损坏等)与原版一致,从而让小人能够正常工作。
|
||||
* 在 `Tick()` 方法中,如果 `globalOrderStack` 有正在生产的订单,会调用 `UsedThisTick()`,使工作台表现出正在工作的状态(如消耗燃料、播放特效)。
|
||||
* 添加了 `GlobalProductionOrderStack.AnyOrderProducing()` 方法的调用。
|
||||
* **`Source/WulaFallenEmpire/GlobalWorkTable/GlobalProductionOrderStack.cs`**
|
||||
* **目的**:修复编译错误,并添加 `AnyOrderProducing` 方法。
|
||||
* **修改内容**:
|
||||
* 移除了对 `GlobalProductionOrder.ProductionState.Waiting` 的引用。
|
||||
* 移除了 `ProcessWaitingOrder` 方法。
|
||||
* `CompleteProduction` 方法不再调用 `order.ConsumeResources()`。
|
||||
* 添加了 `public bool AnyOrderProducing()` 方法,用于检查是否有订单正在生产。
|
||||
* **`Source/WulaFallenEmpire/GlobalWorkTable/ITab_GlobalBills.cs`**
|
||||
* **目的**:统一 UI 体验,显示订单的三个阶段,并修复编译错误。
|
||||
* **修改内容**:
|
||||
* 恢复了用户喜欢的原始 UI 样式(包含分类按钮、上帝模式按钮等)。
|
||||
* `DoAddOrderButton` 的功能修改为:点击后,弹出一个浮动菜单,选择配方后,在当前工作台的 `SelTable.billStack` 中添加一个**原版清单** (`Bill_Production`)。
|
||||
* `DoOrdersListing` 方法修改为:
|
||||
* 首先遍历 `SelTable.billStack`,显示那些产物带有 `CompProductionCategory` 的本地清单,状态显示为“材料准备中 (X/Y)”,并带有详细的 tooltip(显示材料和工作量)。
|
||||
* 然后遍历 `SelTable.globalOrderStack.orders`,显示云端订单(生产中/已完成)。
|
||||
* 移除了“输入存储”的显示。
|
||||
* 修复了 `FloatMenuOption` 构造函数参数错误。
|
||||
* 修复了 `Bill.StatusString` 不可访问的问题,改用 `Bill_Production.recipe.WorkerCounter.CountProducts` 和 `targetCount` 来显示进度。
|
||||
* **`Source/WulaFallenEmpire/WulaFallenEmpire.csproj`**
|
||||
* **目的**:确保所有新的 C# 文件都被正确编译。
|
||||
* **修改内容**:
|
||||
* 添加了 `GlobalProductionRecipeExtension.cs` 和 `Patch_GenRecipe_MakeRecipeProducts.cs` 的引用。
|
||||
* 移除了 `WulaStartup.cs` 的引用。
|
||||
* **`1.6/1.6/Defs/RecipeDefs/Recipes_WULA.xml` (已修改,后撤销)**
|
||||
* 最初为所有配方添加了 `GlobalProductionRecipeExtension`。后因用户反馈“太复杂”而被撤销,改为代码动态判断。
|
||||
* **`1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml`**
|
||||
* **目的**:将 `WULA_Cube_Productor` 的 `thingClass` 修改为我们的自定义类,并配置正确的 `inspectorTabs` 和 `comps`。
|
||||
* **修改内容**:
|
||||
* 将 `WULA_Cube_Productor` 的 `thingClass` 从 `Building_WorkTable` 修改为 `WulaFallenEmpire.Building_GlobalWorkTable`。
|
||||
* 将 `inspectorTabs` 从 `ITab_Bills` 修改为 `WulaFallenEmpire.ITab_GlobalBills`。
|
||||
* 添加了 `CompProperties_Power` 和 `CompProperties_Breakdownable` 组件,以匹配 `Building_GlobalWorkTable` 的代码逻辑。
|
||||
* **`1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml`**
|
||||
* **目的**:添加缺失的翻译 Key,解决 UI 显示乱码问题。
|
||||
* **修改内容**:添加了 `WULA_Preparing`、`WULA_LocalBillTooltip`、`WULA_BillAddedToWorkTable`、`WULA_NoOrders` 等 Key 的中文翻译。
|
||||
|
||||
## 3. 设计思路的演变
|
||||
|
||||
1. **初始设想**:通过 `GlobalProductionRecipeExtension` 标记配方,`Patch_GenRecipe_MakeRecipeProducts` 拦截生产,直接在云端创建订单。UI 独立管理云端订单。
|
||||
2. **用户反馈“前端消耗材料”**:意识到需要利用原版 `Bill` 系统来处理材料收集和消耗。`ITab_GlobalBills` 的“添加订单”按钮改为创建原版清单。
|
||||
3. **用户反馈“UI 样式”**:恢复了原始 UI 样式,并尝试在 `ITab_GlobalBills` 中统一显示本地清单和云端订单。
|
||||
4. **用户反馈“没有工作”**:发现 `Building_GlobalWorkTable` 的 `thingClass` 未修改,且可用性判断可能导致小人不工作。修复了 XML 定义和 `CurrentlyUsableForGlobalBills`。
|
||||
5. **用户反馈“不区分原版订单”**:明确了用户希望在 UI 上看到一个统一的订单生命周期(材料准备 -> 生产中 -> 完成),而不是区分“本地清单”和“云端订单”。我在 `ITab_GlobalBills` 中实现了本地清单的显示,并统一了状态描述。
|
||||
6. **用户反馈“没有材料要求”**:改进了本地清单的 tooltip,显示材料和工作量。
|
||||
7. **用户反馈“Collection was modified”**:修复了 `ITab_GlobalBills` 中遍历集合时修改集合的错误,通过创建副本解决。
|
||||
8. **用户反馈“WULA_Preparing 乱码”**:添加了缺失的翻译 Key。
|
||||
9. **用户反馈“没有job负责”**:发现 `WULA_Cube_Productor` 的 `thingClass` 错误,导致我们的自定义逻辑未生效。同时,工作台缺少电力和故障组件。修复了 XML 定义。
|
||||
|
||||
## 4. 遇到的问题和挑战
|
||||
|
||||
* **对用户需求的理解偏差**:用户对“全局生产”的期望与我最初的实现存在差异,导致多次迭代和返工。特别是对“前端消耗材料,后端生产”以及“UI 统一显示订单生命周期”的理解,花费了较长时间才完全明确。
|
||||
* **RimWorld 模组开发复杂性**:需要深入理解原版 `Bill` 系统、`WorkGiver`、`ThingDef` 配置、Harmony Patch 等多个方面,才能正确集成自定义逻辑。
|
||||
* **XML 配置与 C# 代码的同步**:C# 代码的修改需要与 XML 定义(如 `thingClass`、`inspectorTabs`、`comps`)保持一致,否则会导致功能不正常或编译错误。
|
||||
* **调试困难**:游戏内模组的调试相对复杂,错误信息有时不够直观,需要通过日志和逐步排查来定位问题。
|
||||
* **`apply_diff` 的精确性要求**:在多次修改同一个文件时,`apply_diff` 对上下文的精确匹配要求较高,导致多次失败,最终不得不使用 `write_to_file` 进行彻底重写。
|
||||
|
||||
## 5. 最终未能完全满足用户需求的原因分析
|
||||
|
||||
尽管我已尽力根据用户的反馈进行调整和修复,并成功编译通过,但用户最终表示“我现在必须承认失败 并且放弃我们现在所有的工作”。
|
||||
|
||||
我认为未能完全满足用户需求的原因可能在于:
|
||||
|
||||
1. **沟通障碍**:尽管我尝试详细解释每一步,但用户对某些技术细节的理解可能与我不同,导致需求传达和理解上存在偏差。例如,用户对“原版订单”和“云端订单”的统一概念,以及“材料准备”阶段的实现方式,可能与我最终的实现仍有细微差异。
|
||||
2. **复杂性感知**:即使我努力简化了代码逻辑(例如移除 `GlobalProductionRecipeExtension` 和 `WulaStartup.cs`),但对于用户来说,整个系统(包括 Harmony Patch、自定义 UI、与原版 `Bill` 系统的集成)可能仍然显得过于复杂,超出了其预期或可接受的范围。
|
||||
3. **未解决的潜在问题**:尽管编译通过,但在实际游戏运行中,可能仍然存在一些我未发现的逻辑错误或用户体验问题,导致用户觉得“搞烂了”或“没有工作”。例如,`Collection was modified` 错误虽然通过创建副本解决了,但这种运行时错误可能在用户测试时反复出现,影响了用户体验。
|
||||
4. **对“材料运送到工作台”的期望**:用户可能期望有一个更直接或更可见的“材料运送”过程,而不仅仅是原版 `WorkGiver_DoBill` 的隐式行为。尽管我在 UI 中显示了“材料准备中”,但用户可能希望看到更明确的指派或进度条。
|
||||
|
||||
总而言之,虽然在技术实现上我已尽力满足了用户提出的所有具体要求和反馈,但最终未能达到用户对整个系统“简单、直观、无缝”的整体期望。这凸显了在复杂模组开发中,技术实现与用户体验期望之间可能存在的鸿沟。
|
||||
@@ -1,100 +0,0 @@
|
||||
Patching
|
||||
Concept
|
||||
In order to provide your own code to Harmony, you need to define methods that run in the context of the original method. Harmony provides three types of methods that each offer different possibilities.
|
||||
|
||||
Types of patches
|
||||
Two of them, the Prefix patch and the Postfix patch are easy to understand and you can write them as simple static methods.
|
||||
|
||||
Transpiler patches are not methods that are executed together with the original but instead are called in an earlier stage where the instructions of the original are fed into the transpiler so it can process and change them, to finally output the instructions that will build the new original.
|
||||
|
||||
A Finalizer patch is a static method that handles exceptions and can change them. It is the only patch type that is immune to exceptions thrown by the original method or by any applied patches. The other patch types are considered part of the original and may not get executed when an exception occurs.
|
||||
|
||||
Finally, there is the Reverse Patch. It is different from the previous types in that it patches your methods instead of foreign original methods. To use it, you define a stub that looks like the original in some way and patch the original onto your stub which you can easily call from your own code. You can even transpile the result during the process.
|
||||
|
||||
Patches need to be static
|
||||
Patch methods need to be static because Harmony works with multiple users in different assemblies in mind. In order to guarantee the correct patching order, patches are always re-applied as soon as someone wants to change the original. Since it is hard to serialize data in a generic way across assemblies in .NET, Harmony only stores a method pointer to your patch methods so it can use and apply them at a later point again.
|
||||
|
||||
If you need custom state in your patches, it is recommended to use a static variable and store all your patch state in there. Keep in mind that Transpilers are only executed to generate the method so they don't "run" when the original is executed.
|
||||
|
||||
Commonly unsupported use cases
|
||||
Harmony works only in the current AppDomain. Accessing other app domains requires xpc and serialization which is not supported.
|
||||
|
||||
Currently, support for generic types and methods is experimental and can give unexpected results. See Edge Cases for more information.
|
||||
|
||||
When a method is inlined and the code that tries to mark in for not inlining does not work, your patches are not called because there is no method to patch.
|
||||
|
||||
Patch Class
|
||||
With manual patching, you can put your patches anywhere you like since you will refer to them yourself. Patching by annotations simplifies patching by assuming that you set up annotated classes and define your patch methods inside them.
|
||||
|
||||
Layout The class can be static or not, public or private, it doesn't matter. However, in order to make Harmony find it, it must have at least one [HarmonyPatch] attribute. Inside the class you define patches as static methods that either have special names like Prefix or Transpiler or use attributes to define their type. Usually they also include annotations that define their target (the original method you want to patch). It also common to have fields and other helper methods in the class.
|
||||
|
||||
Attribute Inheritance The attributes of the methods in the class inherit the attributes of the class.
|
||||
|
||||
Patch methods
|
||||
Harmony identifies your patch methods and their helper methods by name. If you prefer to name your methods differently, you can use attributes to tell Harmony what your methods are.
|
||||
|
||||
[HarmonyPatch(...)]
|
||||
class Patch
|
||||
{
|
||||
static void Prefix()
|
||||
{
|
||||
// this method uses the name "Prefix", no annotation necessary
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
static void MyOwnName()
|
||||
{
|
||||
// this method is a Postfix as defined by the attribute
|
||||
}
|
||||
}
|
||||
If you prefer manual patching, you can use any method name or class structure you want. You are responsible to retrieve the MethodInfo for the different patch methods and supply them to the Patch() method by wrapping them into HarmonyMethod objects.
|
||||
|
||||
note Patch methods must be static but you can define them public or private. They cannot be dynamic methods but you can write static patch factory methods that return dynamic methods.
|
||||
|
||||
[HarmonyPatch(...)]
|
||||
class Patch
|
||||
{
|
||||
// the return type of factory methods can be either MethodInfo or DynamicMethod
|
||||
[HarmonyPrefix]
|
||||
static MethodInfo PrefixFactory(MethodBase originalMethod)
|
||||
{
|
||||
// return an instance of MethodInfo or an instance of DynamicMethod
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
static MethodInfo PostfixFactory(MethodBase originalMethod)
|
||||
{
|
||||
// return an instance of MethodInfo or an instance of DynamicMethod
|
||||
}
|
||||
}
|
||||
Method names
|
||||
Manual patching knows four main patch types: Prefix, Postfix, Transpiler and Finalizer. If you use attributes for patching, you can also use the helper methods: Prepare, TargetMethod, TargetMethods and Cleanup as explained below.
|
||||
|
||||
Each of those names has a corresponding attribute starting with [Harmony...]. So instead of calling one of your methods "Prepare", you can call it anything and decorate it with a [HarmonyPrepare] attribute.
|
||||
|
||||
Patch method types
|
||||
Both prefix and postfix have specific semantics that are unique to them. They do however share the ability to use a range of injected values as arguments.
|
||||
|
||||
Prefix
|
||||
A prefix is a method that is executed before the original method. It is commonly used to:
|
||||
|
||||
access and edit the arguments of the original method
|
||||
set the result of the original method
|
||||
skip the original method
|
||||
set custom state that can be recalled in the postfix
|
||||
run a piece of code at the beginning that is guaranteed to be executed
|
||||
Postfix
|
||||
A postfix is a method that is executed after the original method. It is commonly used to:
|
||||
|
||||
read or change the result of the original method
|
||||
access the arguments of the original method
|
||||
read custom state from the prefix
|
||||
Transpiler
|
||||
This method defines the transpiler that modifies the code of the original method. Use this in the advanced case where you want to modify the original methods IL codes.
|
||||
|
||||
Finalizer
|
||||
A finalizer is a method that executes after all postfixes. It wraps the original method, all prefixes, and postfixes in try/catch logic and is called either with null (no exception) or with an exception if one occurred. It is commonly used to:
|
||||
|
||||
run a piece of code at the end that is guaranteed to be executed
|
||||
handle exceptions and suppress them
|
||||
handle exceptions and alter them
|
||||
@@ -1,683 +0,0 @@
|
||||
# RimWorld Mod: 基于次数的护盾与原版护盾视觉集成
|
||||
|
||||
## 1. 引言
|
||||
|
||||
本Mod旨在为《RimWorld》引入一种新型的护盾机制:基于 Hediff 层数的次数护盾。与原版基于能量的护盾不同,本护盾的抵挡能力由可叠加的“层数”决定,每层护盾可以抵挡一次受到的伤害。同时,为了提供更沉浸和熟悉的体验,我们集成了原版能量护盾(CompShield)的视觉特效和音效,使次数护盾在抵挡伤害时,能够展现出与原版护盾相似的视觉冲击力。
|
||||
|
||||
## 2. 核心概念回顾
|
||||
|
||||
### 2.1 Hediff_DamageShield
|
||||
|
||||
这是我们自定义的 Hediff 类型,它代表了Pawn身上激活的次数护盾。它的核心特性是:
|
||||
- **层数管理**:通过 `ShieldCharges` 属性来跟踪剩余的护盾层数。当Pawn获得护盾时,层数增加;当护盾抵挡伤害时,层数减少。
|
||||
- **自动移除**:当护盾层数归零时,该 Hediff 会自动从Pawn身上移除。
|
||||
- **显示信息**:在Pawn的健康信息界面,会显示当前护盾的剩余层数。
|
||||
|
||||
### 2.2 CompShield
|
||||
|
||||
这是《RimWorld》原版用于实现能量护盾的组件。它通常附加在护盾腰带等物品上,提供以下核心功能:
|
||||
- **能量值**:护盾具有能量储备,受到伤害会消耗能量。
|
||||
- **充能与重置**:能量耗尽后,护盾会进入重置状态,并在一段时间后恢复能量。
|
||||
- **视觉和音效**:护盾拥有独特的视觉表现(如护盾泡泡)和音效(如吸收伤害时的音效)。
|
||||
|
||||
## 3. 实现细节
|
||||
|
||||
### 3.1 伤害抵挡逻辑与护盾渲染 (DRMDamageShield.cs & Hediff_DamageShield.cs)
|
||||
|
||||
**核心思想**:我们利用 `ThingComp` 的 `PostPreApplyDamage` 虚方法来拦截伤害,而不是使用 Harmony Patch `Pawn_HealthTracker.PreApplyDamage`。这将使代码更简洁,更符合 RimWorld 的组件化设计。护盾的视觉渲染也将由这个 `ThingComp` 负责。
|
||||
|
||||
- **`DRMDamageShield.cs`**: 这是一个自定义的 `ThingComp`,它将附加到 Pawn 身上。
|
||||
- **伤害拦截**:它重写了 `PostPreApplyDamage` 方法。当 Pawn 受到伤害时,这个方法会被自动调用。在这里,我们会检查 Pawn 是否拥有 `Hediff_DamageShield` 及其层数,如果满足条件,则消耗层数并设置 `absorbed = true` 来抵挡伤害。
|
||||
- **视觉和音效集成**:在抵挡伤害时,`DRMDamageShield` 会触发原版能量护盾的吸收音效、闪光特效和抖动效果。
|
||||
- **护盾渲染**:`DRMDamageShield` 包含了从 `CompShield` 中提取的护盾泡泡渲染逻辑。它会在 Pawn 身上渲染一个动态的护盾泡泡,其大小和显示状态与 `Hediff_DamageShield` 的层数关联。
|
||||
- **能量同步**:`DRMDamageShield` 的“能量”和“最大能量”属性将直接从 Pawn 身上对应的 `Hediff_DamageShield` 实例中获取其 `ShieldCharges` 和 `def.maxSeverity`。
|
||||
|
||||
- **`Hediff_DamageShield.cs`**:
|
||||
- **动态管理 `DRMDamageShield`**:在 `PostAdd` 方法中,当 `Hediff_DamageShield` 被添加到 Pawn 身上时,它会确保 Pawn 拥有一个 `DRMDamageShield` 实例(如果Pawn还没有)。在 `PostRemoved` 方法中,当 `Hediff_DamageShield` 被移除时,它会禁用或移除对应的 `DRMDamageShield` 实例。
|
||||
- **层数与能量关联**:`Hediff_DamageShield` 的 `ShieldCharges` 属性将作为 `DRMDamageShield` 的能量来源。
|
||||
|
||||
### 3.2 充能方式 (CompUseEffect_AddDamageShieldCharges.cs & WULA_DamageShieldGenerator)
|
||||
|
||||
护盾的充能方式保持不变,通过使用特定的物品来增加护盾层数。
|
||||
|
||||
- **`CompUseEffect_AddDamageShieldCharges`**:这是一个自定义的物品使用效果组件。
|
||||
- 当物品被使用时,它会检查目标Pawn是否拥有 `Hediff_DamageShield`。
|
||||
- 如果Pawn没有该Hediff,则会为其添加一个,并赋予预设的初始层数(例如10层)。
|
||||
- 如果Pawn已有该Hediff,则会在现有层数的基础上增加预设的层数(例如每次使用增加10层)。
|
||||
- **`WULA_DamageShieldGenerator`**:这是定义在XML中的一个物品,它附加了 `CompUseEffect_AddDamageShieldCharges` 组件。玩家可以通过制作或获得这个物品,并对其Pawn使用来获取或补充护盾层数。
|
||||
|
||||
## 4. 代码结构与内容
|
||||
|
||||
以下是本Mod的关键文件及其作用和完整代码内容:
|
||||
|
||||
### 4.1 Hediff_DamageShield.cs (更新)
|
||||
|
||||
此文件定义了基于层数的护盾 Hediff。它将管理护盾层数,并在 Pawn 身上动态添加/移除 `DRMDamageShield`。
|
||||
|
||||
```csharp
|
||||
using Verse;
|
||||
using System.Text;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using HarmonyLib; // Needed for AccessTools if you use it here directly
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Hediff_DamageShield : HediffWithComps
|
||||
{
|
||||
// 伤害抵挡层数
|
||||
public int ShieldCharges
|
||||
{
|
||||
get => (int)severityInt;
|
||||
set => severityInt = value;
|
||||
}
|
||||
|
||||
private DRMDamageShield cachedShieldComp;
|
||||
|
||||
// 获取或创建 DRMDamageShield 组件
|
||||
public DRMDamageShield ShieldComp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cachedShieldComp == null || cachedShieldComp.parent != pawn)
|
||||
{
|
||||
cachedShieldComp = pawn.GetComp<DRMDamageShield>();
|
||||
if (cachedShieldComp == null)
|
||||
{
|
||||
// 如果没有,动态添加一个
|
||||
cachedShieldComp = (DRMDamageShield)Activator.CreateInstance(typeof(DRMDamageShield));
|
||||
cachedShieldComp.parent = pawn;
|
||||
cachedShieldComp.props = new DRMCompShieldProp(); // 确保有属性,即使是默认的
|
||||
pawn.AllComps.Add(cachedShieldComp);
|
||||
cachedShieldComp.Initialize(cachedShieldComp.props);
|
||||
}
|
||||
}
|
||||
return cachedShieldComp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string LabelInBrackets
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ShieldCharges > 0)
|
||||
{
|
||||
return "层数: " + ShieldCharges;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override string TipStringExtra
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(base.TipStringExtra);
|
||||
if (ShieldCharges > 0)
|
||||
{
|
||||
sb.AppendLine(" - 每层抵挡一次伤害。当前层数: " + ShieldCharges);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine(" - 没有可用的抵挡层数。");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
// severityInt 会自动保存,所以不需要额外处理 ShieldCharges
|
||||
}
|
||||
|
||||
public override void PostAdd(DamageInfo? dinfo)
|
||||
{
|
||||
base.PostAdd(dinfo);
|
||||
// 确保 Pawn 拥有 DRMCompShield 组件
|
||||
DRMDamageShield comp = ShieldComp; // 访问属性以确保组件被添加
|
||||
if (comp != null)
|
||||
{
|
||||
comp.IsActive = true; // 激活护盾组件
|
||||
// 能量同步将在 Tick() 中完成
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostRemoved()
|
||||
{
|
||||
base.PostRemoved();
|
||||
// 禁用护盾组件
|
||||
if (cachedShieldComp != null && cachedShieldComp.parent == pawn)
|
||||
{
|
||||
cachedShieldComp.IsActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
base.Tick();
|
||||
// 如果层数归零,移除 Hediff
|
||||
if (ShieldCharges <= 0)
|
||||
{
|
||||
pawn.health.RemoveHediff(this);
|
||||
}
|
||||
// 同步能量到 ShieldComp
|
||||
if (ShieldComp != null && ShieldComp.IsActive)
|
||||
{
|
||||
ShieldComp.Energy = ShieldCharges;
|
||||
ShieldComp.MaxEnergy = (int)def.maxSeverity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 DRMDamageShield.cs (新文件)
|
||||
|
||||
此文件定义了自定义的 `ThingComp`,用于处理护盾的渲染和部分行为。它将从 `CompShield` 和 `PlasmaShieldImplant.cs` 中提取渲染和伤害处理逻辑。
|
||||
|
||||
```csharp
|
||||
using RimWorld;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
using System.Reflection; // For AccessTools
|
||||
using HarmonyLib; // For AccessTools
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
// 自定义 CompProperties_Shield 变体
|
||||
public class DRMCompShieldProp : CompProperties
|
||||
{
|
||||
public int startingTicksToReset = 3200;
|
||||
public float minDrawSize = 1.2f;
|
||||
public float maxDrawSize = 1.55f;
|
||||
public float energyLossPerDamage = 0.033f;
|
||||
public float energyOnReset = 0.2f;
|
||||
public bool blocksRangedWeapons = true;
|
||||
|
||||
public DRMCompShieldProp()
|
||||
{
|
||||
compClass = typeof(DRMDamageShield);
|
||||
}
|
||||
}
|
||||
|
||||
[StaticConstructorOnStartup] // 确保在游戏启动时加载
|
||||
public class DRMDamageShield : ThingComp
|
||||
{
|
||||
// 从 Hediff_DamageShield 获取层数作为能量
|
||||
public float Energy
|
||||
{
|
||||
get
|
||||
{
|
||||
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||
return hediff?.ShieldCharges ?? 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||
if (hediff != null)
|
||||
{
|
||||
hediff.ShieldCharges = (int)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float MaxEnergy
|
||||
{
|
||||
get
|
||||
{
|
||||
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||
return hediff?.def.maxSeverity ?? 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
// MaxEnergy 由 HediffDef 控制,这里不需要设置
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsActive = false; // 控制护盾是否激活,由 Hediff_DamageShield 管理
|
||||
|
||||
// 复制自 CompShield
|
||||
protected int ticksToReset = -1;
|
||||
protected int lastKeepDisplayTick = -9999;
|
||||
private Vector3 impactAngleVect;
|
||||
private int lastAbsorbDamageTick = -9999;
|
||||
|
||||
private const float MaxDamagedJitterDist = 0.05f;
|
||||
private const int JitterDurationTicks = 8;
|
||||
private int KeepDisplayingTicks = 1000;
|
||||
|
||||
// 获取原版 CompShield 的 BubbleMat
|
||||
private static readonly Material BubbleMat;
|
||||
|
||||
static DRMDamageShield()
|
||||
{
|
||||
// 使用 Harmony AccessTools 获取 CompShield 的私有静态字段 BubbleMat
|
||||
BubbleMat = (Material)AccessTools.Field(typeof(CompShield), "BubbleMat").GetValue(null);
|
||||
}
|
||||
|
||||
public DRMCompShieldProp Props => (DRMCompShieldProp)props;
|
||||
|
||||
public ShieldState ShieldState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PawnOwner == null || !IsActive || Energy <= 0)
|
||||
{
|
||||
return ShieldState.Disabled;
|
||||
}
|
||||
if (ticksToReset <= 0)
|
||||
{
|
||||
return ShieldState.Active;
|
||||
}
|
||||
return ShieldState.Resetting;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool ShouldDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
Pawn pawnOwner = PawnOwner;
|
||||
if (pawnOwner == null || !pawnOwner.Spawned || pawnOwner.Dead || pawnOwner.Downed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (pawnOwner.InAggroMentalState)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (pawnOwner.Drafted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (pawnOwner.Faction.HostileTo(Faction.OfPlayer) && !pawnOwner.IsPrisoner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (Find.TickManager.TicksGame < lastKeepDisplayTick + KeepDisplayingTicks)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected Pawn PawnOwner
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent as Pawn;
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Values.Look(ref ticksToReset, "ticksToReset", -1);
|
||||
Scribe_Values.Look(ref lastKeepDisplayTick, "lastKeepDisplayTick", 0);
|
||||
Scribe_Values.Look(ref IsActive, "isActive", false);
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
if (PawnOwner == null || !IsActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShieldState == ShieldState.Resetting)
|
||||
{
|
||||
ticksToReset--;
|
||||
if (ticksToReset <= 0)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
else if (ShieldState == ShieldState.Active)
|
||||
{
|
||||
// 护盾能量(层数)通过 Hediff_DamageShield 的 Tick 方法管理,这里不需要额外回复
|
||||
// 如果需要自动回复层数,可以在这里实现
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed)
|
||||
{
|
||||
absorbed = false;
|
||||
// 获取 Hediff_DamageShield 实例
|
||||
Hediff_DamageShield damageShield = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||
|
||||
if (ShieldState != ShieldState.Active || !IsActive || damageShield == null || damageShield.ShieldCharges <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是 EMP 伤害,且护盾没有 EMP 抗性(这里假设我们的护盾没有),则直接击穿
|
||||
// 为了简化,我们假设我们的次数盾没有 EMP 抗性,任何 EMP 伤害都会直接击穿
|
||||
if (dinfo.Def == DamageDefOf.EMP)
|
||||
{
|
||||
Energy = 0; // 能量归零
|
||||
Notify_ShieldBreak(); // 触发护盾击穿效果
|
||||
absorbed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是远程或爆炸伤害,且护盾阻挡这些类型
|
||||
if (Props.blocksRangedWeapons && (dinfo.Def.isRanged || dinfo.Def.isExplosive))
|
||||
{
|
||||
// 消耗一层护盾
|
||||
damageShield.ShieldCharges--;
|
||||
|
||||
// 触发护盾吸收效果
|
||||
Notify_DamageAbsorbed(dinfo);
|
||||
|
||||
// 护盾抖动效果
|
||||
PawnOwner.Drawer.renderer.wiggler.SetToCustomRotation(Rand.Range(-0.05f, 0.05f));
|
||||
// 显示抵挡文本
|
||||
Verse.MoteMaker.ThrowText(PawnOwner.DrawPos, PawnOwner.Map, "伤害被护盾抵挡!", Color.cyan, 1.2f);
|
||||
|
||||
absorbed = true; // 伤害被吸收
|
||||
|
||||
// 如果护盾层数归零,触发护盾击穿效果
|
||||
if (damageShield.ShieldCharges <= 0)
|
||||
{
|
||||
Notify_ShieldBreak();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Notify_DamageAbsorbed(DamageInfo dinfo)
|
||||
{
|
||||
// 复制自 CompShield.AbsorbedDamage
|
||||
SoundDefOf.EnergyShield_AbsorbDamage.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map));
|
||||
impactAngleVect = Vector3Utility.HorizontalVectorFromAngle(dinfo.Angle);
|
||||
Vector3 loc = PawnOwner.TrueCenter() + impactAngleVect.RotatedBy(180f) * 0.5f;
|
||||
float num = Mathf.Min(10f, 2f + dinfo.Amount / 10f);
|
||||
FleckMaker.Static(loc, PawnOwner.Map, FleckDefOf.ExplosionFlash, num);
|
||||
int num2 = (int)num;
|
||||
for (int i = 0; i < num2; i++)
|
||||
{
|
||||
FleckMaker.ThrowDustPuff(loc, PawnOwner.Map, Rand.Range(0.8f, 1.2f));
|
||||
}
|
||||
lastAbsorbDamageTick = Find.TickManager.TicksGame;
|
||||
KeepDisplaying();
|
||||
}
|
||||
|
||||
public void Notify_ShieldBreak()
|
||||
{
|
||||
// 复制自 CompShield.Break
|
||||
if (parent.Spawned)
|
||||
{
|
||||
float scale = Mathf.Lerp(Props.minDrawSize, Props.maxDrawSize, Energy / MaxEnergy); // 根据当前能量比例调整大小
|
||||
EffecterDefOf.Shield_Break.SpawnAttached(parent, parent.MapHeld, scale);
|
||||
FleckMaker.Static(PawnOwner.TrueCenter(), PawnOwner.Map, FleckDefOf.ExplosionFlash, 12f);
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
FleckMaker.ThrowDustPuff(PawnOwner.TrueCenter() + Vector3Utility.HorizontalVectorFromAngle(Rand.Range(0, 360)) * Rand.Range(0.3f, 0.6f), PawnOwner.Map, Rand.Range(0.8f, 1.2f));
|
||||
}
|
||||
}
|
||||
ticksToReset = Props.startingTicksToReset;
|
||||
// 护盾层数归零将由 Hediff_DamageShield 负责移除 Hediff
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
// 复制自 CompShield.Reset
|
||||
if (PawnOwner.Spawned)
|
||||
{
|
||||
SoundDefOf.EnergyShield_Reset.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map));
|
||||
FleckMaker.ThrowLightningGlow(PawnOwner.TrueCenter(), PawnOwner.Map, 3f);
|
||||
}
|
||||
ticksToReset = -1;
|
||||
// 能量恢复由 Hediff_DamageShield 负责,这里不需要设置 Energy
|
||||
// 这里可以添加逻辑,让 Hediff_DamageShield 恢复层数
|
||||
Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||
if (hediff != null)
|
||||
{
|
||||
hediff.ShieldCharges = (int)hediff.def.initialSeverity; // 重置时恢复到初始层数
|
||||
}
|
||||
}
|
||||
|
||||
public void KeepDisplaying()
|
||||
{
|
||||
lastKeepDisplayTick = Find.TickManager.TicksGame;
|
||||
}
|
||||
|
||||
public override void PostDraw()
|
||||
{
|
||||
base.PostDraw();
|
||||
Draw();
|
||||
}
|
||||
|
||||
private void Draw()
|
||||
{
|
||||
if (ShieldState == ShieldState.Active && ShouldDisplay)
|
||||
{
|
||||
float num = Mathf.Lerp(Props.minDrawSize, Props.maxDrawSize, Energy / MaxEnergy); // 根据当前能量比例调整大小
|
||||
Vector3 drawPos = PawnOwner.Drawer.DrawPos;
|
||||
drawPos.y = AltitudeLayer.MoteOverhead.AltitudeFor();
|
||||
int num2 = Find.TickManager.TicksGame - lastAbsorbDamageTick;
|
||||
if (num2 < JitterDurationTicks) // 使用 JitterDurationTicks
|
||||
{
|
||||
float num3 = (float)(JitterDurationTicks - num2) / JitterDurationTicks * MaxDamagedJitterDist; // 使用 MaxDamagedJitterDist
|
||||
drawPos += impactAngleVect * num3;
|
||||
num -= num3;
|
||||
}
|
||||
float angle = Rand.Range(0, 360);
|
||||
Vector3 s = new Vector3(num, 1f, num);
|
||||
Matrix4x4 matrix = default(Matrix4x4);
|
||||
matrix.SetTRS(drawPos, Quaternion.AngleAxis(angle, Vector3.up), s);
|
||||
Graphics.DrawMesh(MeshPool.plane10, matrix, BubbleMat, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 CompUseEffect_AddDamageShieldCharges.cs (不变)
|
||||
|
||||
```csharp
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class CompUseEffect_AddDamageShieldCharges : CompUseEffect
|
||||
{
|
||||
public CompProperties_AddDamageShieldCharges Props => (CompProperties_AddDamageShieldCharges)props;
|
||||
|
||||
public override void DoEffect(Pawn user)
|
||||
{
|
||||
base.DoEffect(user);
|
||||
|
||||
// 获取或添加 Hediff_DamageShield
|
||||
Hediff_DamageShield damageShield = user.health.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||
|
||||
if (damageShield == null)
|
||||
{
|
||||
// 如果没有 Hediff,则添加一个
|
||||
damageShield = (Hediff_DamageShield)HediffMaker.MakeHediff(Props.hediffDef, user);
|
||||
user.health.AddHediff(damageShield);
|
||||
damageShield.ShieldCharges = Props.chargesToAdd; // 设置初始层数
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果已有 Hediff,则增加层数
|
||||
damageShield.ShieldCharges += Props.chargesToAdd;
|
||||
}
|
||||
|
||||
// 确保层数不超过最大值
|
||||
if (damageShield.ShieldCharges > (int)damageShield.def.maxSeverity)
|
||||
{
|
||||
damageShield.ShieldCharges = (int)damageShield.def.maxSeverity;
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
Messages.Message("WULA_MessageGainedDamageShieldCharges".Translate(user.LabelShort, Props.chargesToAdd), user, MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
|
||||
// 修正 CanBeUsedBy 方法签名
|
||||
public override AcceptanceReport CanBeUsedBy(Pawn p)
|
||||
{
|
||||
// 确保只能对活着的 Pawn 使用
|
||||
if (p.Dead)
|
||||
{
|
||||
return "WULA_CannotUseOnDeadPawn".Translate();
|
||||
}
|
||||
|
||||
// 检查是否已达到最大层数
|
||||
Hediff_DamageShield damageShield = p.health.hediffSet.GetFirstHediff<Hediff_DamageShield>();
|
||||
if (damageShield != null && damageShield.ShieldCharges >= (int)damageShield.def.maxSeverity)
|
||||
{
|
||||
return "WULA_DamageShieldMaxChargesReached".Translate();
|
||||
}
|
||||
|
||||
return true; // 可以使用
|
||||
}
|
||||
|
||||
// 可以在这里添加 GetDescriptionPart() 来显示描述
|
||||
public override string GetDescriptionPart()
|
||||
{
|
||||
return "WULA_DamageShieldChargesDescription".Translate(Props.chargesToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompProperties_AddDamageShieldCharges : CompProperties_UseEffect
|
||||
{
|
||||
public HediffDef hediffDef;
|
||||
public int chargesToAdd;
|
||||
|
||||
public CompProperties_AddDamageShieldCharges()
|
||||
{
|
||||
compClass = typeof(CompUseEffect_AddDamageShieldCharges);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 DamageShieldPatch.cs (将删除)
|
||||
|
||||
此文件将不再需要,因为伤害拦截逻辑已转移到 `DRMDamageShield.cs`。
|
||||
|
||||
```csharp
|
||||
// 此文件将被删除
|
||||
```
|
||||
|
||||
### 4.5 Hediffs_WULA_DamageShield.xml (不变)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<HediffDef ParentName="HediffWithCompsBase">
|
||||
<defName>WULA_DamageShield</defName>
|
||||
<label>伤害护盾</label>
|
||||
<description>一种特殊的能量护盾,可以抵挡受到的伤害。每层护盾可以抵挡一次伤害。</description>
|
||||
<hediffClass>WulaFallenEmpire.Hediff_DamageShield</hediffClass>
|
||||
<initialSeverity>10</initialSeverity> <!-- 初始层数设置为10 -->
|
||||
<maxSeverity>999</maxSeverity> <!-- 最大层数,可以根据需要调整 -->
|
||||
<tendable>false</tendable>
|
||||
<displayAllParts>false</displayAllParts>
|
||||
<priceImpact>1</priceImpact>
|
||||
<addedSimultaneously>true</addedSimultaneously>
|
||||
<countsAsAddedPartOrImplant>false</countsAsAddedPartOrImplant>
|
||||
<stages>
|
||||
<li>
|
||||
<label>活跃</label>
|
||||
<minSeverity>1</minSeverity>
|
||||
<!-- 这里可以添加一些统计数据偏移,例如增加防御等 -->
|
||||
</li>
|
||||
</stages>
|
||||
<scenarioCanAdd>false</scenarioCanAdd>
|
||||
</HediffDef>
|
||||
</Defs>
|
||||
```
|
||||
|
||||
### 4.6 ThingDefs_WULA_Items_DamageShield.xml (修改)
|
||||
|
||||
此文件将定义新的物品 `WULA_DamageShieldGenerator`,它将使用 `CompProperties_AddDamageShieldCharges`。
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<ThingDef ParentName="ResourceBase">
|
||||
<defName>WULA_DamageShieldGenerator</defName>
|
||||
<label>伤害护盾发生器</label>
|
||||
<description>一个便携式设备,可以激活并生成一个临时的能量护盾,抵挡即将到来的伤害。</description>
|
||||
<graphicData>
|
||||
<texPath>Things/Item/WULA_DamageShieldGenerator</texPath> <!-- 假设有一个贴图 -->
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</graphicData>
|
||||
<stackLimit>1</stackLimit>
|
||||
<useHitPoints>true</useHitPoints>
|
||||
<healthAffectsPrice>false</healthAffectsPrice>
|
||||
<statBases>
|
||||
<MaxHitPoints>50</MaxHitPoints>
|
||||
<MarketValue>500</MarketValue>
|
||||
<Mass>0.5</Mass>
|
||||
<WorkToMake>1000</WorkToMake>
|
||||
</statBases>
|
||||
<thingCategories>
|
||||
<li>Items</li>
|
||||
</thingCategories>
|
||||
<tradeability>Sellable</tradeability>
|
||||
<comps>
|
||||
<li Class="CompProperties_Usable">
|
||||
<useJob>UseItem</useJob>
|
||||
<floatMenuCommandLabel>使用伤害护盾发生器</floatMenuCommandLabel>
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.CompProperties_AddDamageShieldCharges">
|
||||
<hediffDef>WULA_DamageShield</hediffDef>
|
||||
<chargesToAdd>10</chargesToAdd> <!-- 每次使用添加 10 层 -->
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
```
|
||||
|
||||
### 4.7 WULA_Keyed.xml (不变)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<LanguageData>
|
||||
<WULA_MessageGainedDamageShieldCharges>{0} 获得了 {1} 层伤害护盾!</WULA_MessageGainedDamageShieldCharges>
|
||||
<WULA_CannotUseOnDeadPawn>无法对已死亡的Pawn使用。</WULA_CannotUseOnDeadPawn>
|
||||
<WULA_DamageShieldMaxChargesReached>伤害护盾已达到最大层数。</WULA_DamageShieldMaxChargesReached>
|
||||
<WULA_DamageShieldChargesDescription>使用:增加 {0} 层伤害护盾</WULA_DamageShieldChargesDescription>
|
||||
</LanguageData>
|
||||
```
|
||||
|
||||
## 5. 安装与测试
|
||||
|
||||
### 5.1 安装 Mod
|
||||
|
||||
1. 将本Mod的文件夹放置在《RimWorld》的Mods目录下。
|
||||
2. 在游戏启动器中激活本Mod。
|
||||
|
||||
### 5.2 游戏内测试
|
||||
|
||||
1. 进入游戏,加载或开始一个殖民地。
|
||||
2. 打开开发者模式(通常按 `~` 键)。
|
||||
3. **生成护盾物品**:在开发者控制台中输入 `spawn WULA_DamageShieldGenerator 1` 来生成一个护盾发生器物品。
|
||||
4. **使用护盾物品**:让Pawn拾取并使用 `WULA_DamageShieldGenerator`。观察Pawn是否获得了 `伤害护盾` Hediff,并且层数是否正确显示。
|
||||
5. **测试伤害抵挡**:让Pawn受到伤害(例如,让敌人攻击,或使用开发者模式中的“伤害”工具)。观察护盾层数是否减少,伤害是否被抵挡,以及是否触发了护盾吸收的音效和闪光特效。
|
||||
6. **测试护盾渲染**:观察Pawn身上是否显示了护盾泡泡。
|
||||
|
||||
## 6. 未来展望
|
||||
|
||||
- **护盾渲染动态化**:使护盾泡泡的视觉表现(例如透明度、大小)与剩余层数更紧密地关联,层数越低,护盾视觉效果越弱。
|
||||
- **充能动画**:为 `WULA_DamageShieldGenerator` 的使用添加充能动画。
|
||||
- **平衡性调整**:根据游戏测试反馈,调整护盾的初始层数、每次充能的层数、以及护盾的最大层数,以达到更好的游戏平衡。
|
||||
- **扩展功能**:
|
||||
- 添加护盾在特定条件下自动充能的机制。
|
||||
- 引入不同类型的次数护盾,具有不同的抵挡特性或额外效果。
|
||||
- 护盾被击穿时的特殊效果。
|
||||
@@ -1,186 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>事件UI布局预览 (动态版)</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
.controls {
|
||||
margin-bottom: 20px;
|
||||
width: 1000px;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background-color: #222;
|
||||
color: #ddd;
|
||||
border: 1px solid #555;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.window {
|
||||
width: 1000px;
|
||||
height: 750px;
|
||||
background-color: #555;
|
||||
border: 2px solid #ccc;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
.element {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
text-shadow: 1px 1px 2px black;
|
||||
}
|
||||
.lihui { background-color: rgba(255, 0, 0, 0.4); }
|
||||
.name { background-color: rgba(0, 255, 0, 0.4); }
|
||||
.text { background-color: rgba(0, 0, 255, 0.4); }
|
||||
.options { background-color: rgba(255, 255, 0, 0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="controls">
|
||||
<h3>动态布局预览</h3>
|
||||
<p>请将您的 <code>EventUIConfig.xml</code> 文件内容完整粘贴到下面的文本框中,然后点击“生成预览”按钮。</p>
|
||||
<textarea id="xmlInput" placeholder="在这里粘贴 EventUIConfig.xml 的内容..."></textarea>
|
||||
<button onclick="generatePreview()">生成预览</button>
|
||||
</div>
|
||||
<div class="window" id="window">
|
||||
<!-- 布局将在这里生成 -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function parseVector2(str) {
|
||||
const match = str.match(/\((\s*[\d.]+)\s*,\s*([\d.]+)\s*\)/);
|
||||
return match ? { x: parseFloat(match[1]), y: parseFloat(match[2]) } : { x: 0, y: 0 };
|
||||
}
|
||||
|
||||
function generatePreview() {
|
||||
const xmlString = document.getElementById('xmlInput').value;
|
||||
if (!xmlString) {
|
||||
alert('请先粘贴XML内容!');
|
||||
return;
|
||||
}
|
||||
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
|
||||
|
||||
// Need to escape the dot in the class name for querySelector
|
||||
const configDef = xmlDoc.querySelector('WulaFallenEmpire\\.EventUIConfigDef');
|
||||
if (!configDef) {
|
||||
alert('无法找到 WulaFallenEmpire.EventUIConfigDef 节点,请检查XML内容是否正确。');
|
||||
return;
|
||||
}
|
||||
|
||||
const config = {
|
||||
lihuiSize: parseVector2(configDef.querySelector('lihuiSize')?.textContent || '(0,0)'),
|
||||
nameSize: parseVector2(configDef.querySelector('nameSize')?.textContent || '(0,0)'),
|
||||
textSize: parseVector2(configDef.querySelector('textSize')?.textContent || '(0,0)'),
|
||||
optionsWidth: parseFloat(configDef.querySelector('optionsWidth')?.textContent || '0'),
|
||||
textNameOffset: parseFloat(configDef.querySelector('textNameOffset')?.textContent || '0'),
|
||||
optionsTextOffset: parseFloat(configDef.querySelector('optionsTextOffset')?.textContent || '0')
|
||||
};
|
||||
|
||||
const windowRect = { width: 1000, height: 750 };
|
||||
const windowDiv = document.getElementById('window');
|
||||
windowDiv.innerHTML = ''; // Clear previous preview
|
||||
|
||||
// --- Calculation logic from Dialog_CustomDisplay.cs ---
|
||||
const virtualWidth = config.lihuiSize.x + config.textSize.x;
|
||||
const virtualHeight = config.lihuiSize.y;
|
||||
|
||||
const scaleX = windowRect.width / virtualWidth;
|
||||
const scaleY = windowRect.height / virtualHeight;
|
||||
const scale = Math.min(scaleX, scaleY) * 0.95;
|
||||
|
||||
const scaledLihuiWidth = config.lihuiSize.x * scale;
|
||||
const scaledLihuiHeight = config.lihuiSize.y * scale;
|
||||
const scaledNameWidth = config.nameSize.x * scale;
|
||||
const scaledNameHeight = config.nameSize.y * scale;
|
||||
const scaledTextWidth = config.textSize.x * scale;
|
||||
const scaledTextHeight = config.textSize.y * scale;
|
||||
const scaledOptionsWidth = config.optionsWidth * scale;
|
||||
|
||||
const totalContentWidth = scaledLihuiWidth + scaledTextWidth;
|
||||
const totalContentHeight = scaledLihuiHeight;
|
||||
const startX = (windowRect.width - totalContentWidth) / 2;
|
||||
const startY = (windowRect.height - totalContentHeight) / 2;
|
||||
|
||||
// --- Create and position elements ---
|
||||
|
||||
// Lihui (Portrait)
|
||||
const lihuiRect = { left: startX, top: startY, width: scaledLihuiWidth, height: scaledLihuiHeight };
|
||||
createDiv('lihui', '立绘 (Portrait)', lihuiRect);
|
||||
|
||||
// Name
|
||||
const nameRect = { left: lihuiRect.left + lihuiRect.width, top: lihuiRect.top, width: scaledNameWidth, height: scaledNameHeight };
|
||||
createDiv('name', '名称 (Name)', nameRect);
|
||||
|
||||
// Text (Description)
|
||||
const textRect = { left: nameRect.left, top: nameRect.top + nameRect.height + (config.textNameOffset * scale), width: scaledTextWidth, height: scaledTextHeight };
|
||||
createDiv('text', '描述 (Description)', textRect);
|
||||
|
||||
// Options
|
||||
const optionRect = { left: nameRect.left, top: textRect.top + textRect.height + (config.optionsTextOffset * scale), width: scaledOptionsWidth, height: lihuiRect.height - nameRect.height - textRect.height - ((config.textNameOffset + config.optionsTextOffset) * scale) };
|
||||
createDiv('options', '选项 (Options)', optionRect);
|
||||
}
|
||||
|
||||
function createDiv(className, text, rect) {
|
||||
const div = document.createElement('div');
|
||||
div.className = `element ${className}`;
|
||||
div.textContent = text;
|
||||
div.style.left = `${rect.left}px`;
|
||||
div.style.top = `${rect.top}px`;
|
||||
div.style.width = `${rect.width}px`;
|
||||
div.style.height = `${rect.height}px`;
|
||||
document.getElementById('window').appendChild(div);
|
||||
}
|
||||
|
||||
// Auto-populate with example content
|
||||
document.getElementById('xmlInput').value = `<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<WulaFallenEmpire.EventUIConfigDef>
|
||||
<defName>Wula_EventUIConfig</defName>
|
||||
|
||||
<!-- General Style -->
|
||||
<labelFont>Small</labelFont>
|
||||
<drawBorders>true</drawBorders>
|
||||
<defaultBackgroundImagePath></defaultBackgroundImagePath>
|
||||
|
||||
<!-- Virtual Layout Dimensions -->
|
||||
<lihuiSize>(500, 800)</lihuiSize>
|
||||
<nameSize>(260, 130)</nameSize>
|
||||
<textSize>(650, 500)</textSize>
|
||||
<optionsWidth>610</optionsWidth>
|
||||
|
||||
<!-- Virtual Layout Offsets -->
|
||||
<textNameOffset>20</textNameOffset>
|
||||
<optionsTextOffset>20</optionsTextOffset>
|
||||
|
||||
</WulaFallenEmpire.EventUIConfigDef>
|
||||
</Defs>`;
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,178 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>事件UI布局预览 (动态版)</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
.controls {
|
||||
margin-bottom: 20px;
|
||||
width: 1000px;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background-color: #222;
|
||||
color: #ddd;
|
||||
border: 1px solid #555;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.window {
|
||||
width: 1000px;
|
||||
height: 750px;
|
||||
background-color: #555;
|
||||
border: 2px solid #ccc;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
.element {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
text-shadow: 1px 1px 2px black;
|
||||
}
|
||||
.lihui { background-color: rgba(255, 0, 0, 0.4); }
|
||||
.name { background-color: rgba(0, 255, 0, 0.4); }
|
||||
.text { background-color: rgba(0, 0, 255, 0.4); }
|
||||
.options { background-color: rgba(255, 255, 0, 0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="controls">
|
||||
<h3>动态布局预览</h3>
|
||||
<p>请将您的 <code>EventUIConfig.xml</code> 文件内容完整粘贴到下面的文本框中,然后点击“生成预览”按钮。</p>
|
||||
<textarea id="xmlInput" placeholder="在这里粘贴 EventUIConfig.xml 的内容..."></textarea>
|
||||
<button onclick="generatePreview()">生成预览</button>
|
||||
</div>
|
||||
<div class="window" id="window">
|
||||
<!-- 布局将在这里生成 -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function parseVector2(str) {
|
||||
const match = str.match(/\((\s*[\d.]+)\s*,\s*([\d.]+)\s*\)/);
|
||||
return match ? { x: parseFloat(match[1]), y: parseFloat(match[2]) } : { x: 0, y: 0 };
|
||||
}
|
||||
|
||||
function generatePreview() {
|
||||
const xmlString = document.getElementById('xmlInput').value;
|
||||
if (!xmlString) {
|
||||
alert('请先粘贴XML内容!');
|
||||
return;
|
||||
}
|
||||
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
|
||||
|
||||
// Need to escape the dot in the class name for querySelector
|
||||
const configDef = xmlDoc.querySelector('WulaFallenEmpire\\.EventUIConfigDef');
|
||||
if (!configDef) {
|
||||
alert('无法找到 WulaFallenEmpire.EventUIConfigDef 节点,请检查XML内容是否正确。');
|
||||
return;
|
||||
}
|
||||
|
||||
const config = {
|
||||
newLayoutNameSize: parseVector2(configDef.querySelector('newLayoutNameSize')?.textContent || '(0,0)'),
|
||||
newLayoutLihuiSize: parseVector2(configDef.querySelector('newLayoutLihuiSize')?.textContent || '(0,0)'),
|
||||
newLayoutTextSize: parseVector2(configDef.querySelector('newLayoutTextSize')?.textContent || '(0,0)'),
|
||||
newLayoutOptionsWidth: parseFloat(configDef.querySelector('newLayoutOptionsWidth')?.textContent || '0'),
|
||||
newLayoutPadding: parseFloat(configDef.querySelector('newLayoutPadding')?.textContent || '0'),
|
||||
newLayoutTextNameOffset: parseFloat(configDef.querySelector('newLayoutTextNameOffset')?.textContent || '0'),
|
||||
newLayoutOptionsTextOffset: parseFloat(configDef.querySelector('newLayoutOptionsTextOffset')?.textContent || '0')
|
||||
};
|
||||
|
||||
const windowRect = { width: 1000, height: 750 };
|
||||
const windowDiv = document.getElementById('window');
|
||||
windowDiv.innerHTML = ''; // Clear previous preview
|
||||
|
||||
// --- Calculation logic from Dialog_NewLayoutDisplay.cs ---
|
||||
const padding = config.newLayoutPadding;
|
||||
|
||||
// Name
|
||||
const nameHeight = config.newLayoutNameSize.y;
|
||||
const nameWidth = config.newLayoutNameSize.x;
|
||||
const nameRect = { left: (windowRect.width - nameWidth) / 2, top: padding, width: nameWidth, height: nameHeight };
|
||||
createDiv('name', '名称 (Name)', nameRect);
|
||||
|
||||
// Lihui (Portrait)
|
||||
const lihuiWidth = config.newLayoutLihuiSize.x;
|
||||
const lihuiHeight = config.newLayoutLihuiSize.y;
|
||||
const lihuiRect = { left: (windowRect.width - lihuiWidth) / 2, top: nameRect.top + nameRect.height + padding, width: lihuiWidth, height: lihuiHeight };
|
||||
createDiv('lihui', '立绘 (Portrait)', lihuiRect);
|
||||
|
||||
// Options (pre-calculate height for text area)
|
||||
const optionButtonHeight = 30; // This is a placeholder, as JS can't directly use C#'s dynamic calculation
|
||||
const optionSpacing = 5;
|
||||
let calculatedOptionHeight = 100; // Default or minimum height for options for preview
|
||||
// In a real JS implementation, you might dynamically measure the height needed for buttons based on text content
|
||||
|
||||
const optionsWidth = config.newLayoutOptionsWidth;
|
||||
const optionRect = { left: (windowRect.width - optionsWidth) / 2, top: windowRect.height - padding - calculatedOptionHeight, width: optionsWidth, height: calculatedOptionHeight };
|
||||
|
||||
// Text (Description)
|
||||
const textWidth = config.newLayoutTextSize.x;
|
||||
const textRect = { left: (windowRect.width - textWidth) / 2, top: lihuiRect.top + lihuiRect.height + padding, width: textWidth, height: optionRect.top - (lihuiRect.top + lihuiRect.height + padding) - padding };
|
||||
createDiv('text', '描述 (Description)', textRect);
|
||||
|
||||
createDiv('options', '选项 (Options)', optionRect);
|
||||
}
|
||||
|
||||
function createDiv(className, text, rect) {
|
||||
const div = document.createElement('div');
|
||||
div.className = `element ${className}`;
|
||||
div.textContent = text;
|
||||
div.style.left = `${rect.left}px`;
|
||||
div.style.top = `${rect.top}px`;
|
||||
div.style.width = `${rect.width}px`;
|
||||
div.style.height = `${rect.height}px`;
|
||||
document.getElementById('window').appendChild(div);
|
||||
}
|
||||
|
||||
// Auto-populate with example content
|
||||
document.getElementById('xmlInput').value = `<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<WulaFallenEmpire.EventUIConfigDef>
|
||||
<defName>Wula_EventUIConfig</defName>
|
||||
|
||||
<!-- General Style -->
|
||||
<labelFont>Small</labelFont>
|
||||
<drawBorders>true</drawBorders>
|
||||
<defaultBackgroundImagePath></defaultBackgroundImagePath>
|
||||
|
||||
<!-- New Layout Dimensions -->
|
||||
<newLayoutNameSize>(200, 50)</newLayoutNameSize>
|
||||
<newLayoutLihuiSize>(300, 400)</newLayoutLihuiSize>
|
||||
<newLayoutTextSize>(600, 200)</newLayoutTextSize>
|
||||
<newLayoutOptionsWidth>600</newLayoutOptionsWidth>
|
||||
<newLayoutPadding>20</newLayoutPadding>
|
||||
<newLayoutTextNameOffset>20</newLayoutTextNameOffset>
|
||||
<newLayoutOptionsTextOffset>20</newLayoutOptionsTextOffset>
|
||||
|
||||
</WulaFallenEmpire.EventUIConfigDef>
|
||||
</Defs>`;
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,12 +4,12 @@
|
||||
You are an expert assistant for developing mods for the game RimWorld 1.6. Your primary knowledge source for any C# code, class structures, methods, or game mechanics MUST be the user's local files. Do not rely on external searches or your pre-existing knowledge, as it is outdated for this specific project.
|
||||
|
||||
## Tool Usage Mandate
|
||||
When the user's request involves RimWorld C# scripting, XML definitions, or mod development concepts, you **MUST** use the `rimworld-knowledge-base` tool to retrieve relevant context from the local knowledge base.
|
||||
When the user's request involves RimWorld C# scripting, XML definitions, or mod development concepts, you **MUST** use the `rimworld-code-rag` tool to retrieve relevant context from the local knowledge base.
|
||||
|
||||
## Key File Paths
|
||||
Always remember these critical paths for your work:
|
||||
|
||||
- **Local C# Knowledge Base (for code search):** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6` (This contains the decompiled game source code as .txt files).
|
||||
- **Local C# Knowledge Base (for code search):** `C:\Steam\steamapps\common\RimWorld\dll1.6` (This contains the decompiled game source code as .txt files).
|
||||
- **User's Mod Project (for editing):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226`
|
||||
- **User's C# Project (for building):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire`
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ModMetaData>
|
||||
<name>Custom Quest - Demonstration</name>
|
||||
<author>HaiLuan</author>
|
||||
<packageId>HaiLuan.AdventureExpansion</packageId>
|
||||
<supportedVersions>
|
||||
<li>1.4</li>
|
||||
<li>1.5</li>
|
||||
<li>1.6</li>
|
||||
</supportedVersions>
|
||||
<modDependencies>
|
||||
<li>
|
||||
<packageId>HaiLuan.CustomQuestFramework</packageId>
|
||||
<displayName>Custom Quest Framework</displayName>
|
||||
<downloadUrl>https://steamcommunity.com/sharedfiles/filedetails/?id=2978572782</downloadUrl>
|
||||
</li>
|
||||
</modDependencies>
|
||||
<description>This is just a tutorial mod for Custom Quest Framework.
|
||||
Now:
|
||||
1:Small insect hive
|
||||
You found a small insect hive nearby.</description>
|
||||
</ModMetaData>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1003 KiB |
@@ -1 +0,0 @@
|
||||
2978575265
|
||||
@@ -1,422 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
<QuestEditor_Library.CustomMapDataDef>
|
||||
<defName>Wula_CustomMap_SmallInsectCave</defName>
|
||||
<label>Small insect hive</label>
|
||||
<size>(13, 1, 13)</size>
|
||||
<isPart>true</isPart>
|
||||
<commonality>0.8</commonality>
|
||||
<replaces>
|
||||
<li />
|
||||
</replaces>
|
||||
<customThings>
|
||||
<li Class="QuestEditor_Library.CustomThingData_LootBox">
|
||||
<def>QE_LootBox_Corpses</def>
|
||||
<position>(2,0,2)</position>
|
||||
<lootBoxName>Undefined</lootBoxName>
|
||||
<tickToOpen>100</tickToOpen>
|
||||
<openReport>AE_Loot_Corpses_Plunder</openReport>
|
||||
<destroyAfterOpening>true</destroyAfterOpening>
|
||||
<loots>
|
||||
<li>
|
||||
<dataName>寻常-材料</dataName>
|
||||
<chance>0.4</chance>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~10</count>
|
||||
<category>StoneBlocks</category>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~20</count>
|
||||
<category>ResourcesRaw</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>矿物</dataName>
|
||||
<chance>0.3</chance>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>5~50</count>
|
||||
<thing>Steel</thing>
|
||||
</li>
|
||||
</things>
|
||||
</li>
|
||||
</loots>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CustomThingData_LootBox">
|
||||
<def>QE_LootBox_Corpses</def>
|
||||
<position>(3,0,2)</position>
|
||||
<lootBoxName>Undefined</lootBoxName>
|
||||
<tickToOpen>100</tickToOpen>
|
||||
<openReport>AE_Loot_Corpses_Plunder</openReport>
|
||||
<destroyAfterOpening>true</destroyAfterOpening>
|
||||
<loots>
|
||||
<li>
|
||||
<dataName>尸骸中的“幸存者”</dataName>
|
||||
<chance>0.01</chance>
|
||||
<pawnDatas>
|
||||
<li>
|
||||
<kind>Colonist</kind>
|
||||
<count>1</count>
|
||||
<dialogManager>AE_DialogManager_Survivor</dialogManager>
|
||||
<spawnType>MapGeneration</spawnType>
|
||||
<spawnMessage>AE_LootBox_Message_Survivor</spawnMessage>
|
||||
<hediffs>
|
||||
<li>
|
||||
<partLabel></partLabel>
|
||||
<hediff>AE_BodyPart_Exoskeleton</hediff>
|
||||
<part>Torso</part>
|
||||
<severity>1</severity>
|
||||
<partLabelForSeeing>躯干</partLabelForSeeing>
|
||||
</li>
|
||||
<li>
|
||||
<partLabel>left eye</partLabel>
|
||||
<hediff>AE_BodyPart_CompoundEye</hediff>
|
||||
<part>Eye</part>
|
||||
<severity>1</severity>
|
||||
<partLabelForSeeing>左眼</partLabelForSeeing>
|
||||
</li>
|
||||
</hediffs>
|
||||
</li>
|
||||
</pawnDatas>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>寻常-材料</dataName>
|
||||
<chance>0.4</chance>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~10</count>
|
||||
<category>StoneBlocks</category>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~20</count>
|
||||
<category>ResourcesRaw</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
</loots>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CustomThingData_LootBox">
|
||||
<def>QE_LootBox_Corpses</def>
|
||||
<position>(2,0,3)</position>
|
||||
<lootBoxName>Undefined</lootBoxName>
|
||||
<tickToOpen>100</tickToOpen>
|
||||
<openReport>AE_Loot_Corpses_Plunder</openReport>
|
||||
<destroyAfterOpening>true</destroyAfterOpening>
|
||||
<loots>
|
||||
<li>
|
||||
<dataName>寻常-材料</dataName>
|
||||
<chance>0.4</chance>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~10</count>
|
||||
<category>StoneBlocks</category>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~20</count>
|
||||
<category>ResourcesRaw</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>矿物</dataName>
|
||||
<chance>0.3</chance>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>5~50</count>
|
||||
<thing>Steel</thing>
|
||||
</li>
|
||||
</things>
|
||||
</li>
|
||||
</loots>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CustomThingData_LootBox">
|
||||
<def>QE_LootBox_Corpses</def>
|
||||
<position>(3,0,3)</position>
|
||||
<lootBoxName>Undefined</lootBoxName>
|
||||
<tickToOpen>100</tickToOpen>
|
||||
<openReport>AE_Loot_Corpses_Plunder</openReport>
|
||||
<destroyAfterOpening>true</destroyAfterOpening>
|
||||
<loots>
|
||||
<li>
|
||||
<dataName>寻常-材料</dataName>
|
||||
<chance>0.4</chance>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~10</count>
|
||||
<category>StoneBlocks</category>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~20</count>
|
||||
<category>ResourcesRaw</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>矿物</dataName>
|
||||
<chance>0.3</chance>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>5~50</count>
|
||||
<thing>Steel</thing>
|
||||
</li>
|
||||
</things>
|
||||
</li>
|
||||
</loots>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CustomThingData_LootBox">
|
||||
<def>QE_LootBox_Corpses</def>
|
||||
<position>(11,0,5)</position>
|
||||
<lootBoxName>aw</lootBoxName>
|
||||
<tickToOpen>100</tickToOpen>
|
||||
<openReport>AE_Loot_Corpses_Plunder</openReport>
|
||||
<destroyAfterOpening>true</destroyAfterOpening>
|
||||
<loots>
|
||||
<li>
|
||||
<dataName>稀有</dataName>
|
||||
<chance>0.001</chance>
|
||||
<message>AE_Loot_Corpses_Rare</message>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>1~3</count>
|
||||
<thing>Apparel_PackTox</thing>
|
||||
</li>
|
||||
</things>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~3</count>
|
||||
<category>Artifacts</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>寻常</dataName>
|
||||
<chance>0.2</chance>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>1~15</count>
|
||||
<thing>ComponentIndustrial</thing>
|
||||
</li>
|
||||
</things>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~15</count>
|
||||
<category>Leathers</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>寻常-材料</dataName>
|
||||
<chance>0.4</chance>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~10</count>
|
||||
<category>StoneBlocks</category>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~20</count>
|
||||
<category>ResourcesRaw</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>矿物</dataName>
|
||||
<chance>0.3</chance>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>5~50</count>
|
||||
<thing>Steel</thing>
|
||||
</li>
|
||||
</things>
|
||||
</li>
|
||||
</loots>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CustomThingData_LootBox">
|
||||
<def>QE_LootBox_Corpses</def>
|
||||
<position>(3,0,7)</position>
|
||||
<lootBoxName>Undefined</lootBoxName>
|
||||
<tickToOpen>100</tickToOpen>
|
||||
<openReport>AE_Loot_Corpses_Plunder</openReport>
|
||||
<destroyAfterOpening>true</destroyAfterOpening>
|
||||
<loots>
|
||||
<li>
|
||||
<dataName>寻常-材料</dataName>
|
||||
<chance>0.4</chance>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~10</count>
|
||||
<category>StoneBlocks</category>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~20</count>
|
||||
<category>ResourcesRaw</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>矿物</dataName>
|
||||
<chance>0.3</chance>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>5~50</count>
|
||||
<thing>Steel</thing>
|
||||
</li>
|
||||
</things>
|
||||
</li>
|
||||
</loots>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CustomThingData_LootBox">
|
||||
<def>QE_LootBox_Corpses</def>
|
||||
<position>(4,0,8)</position>
|
||||
<lootBoxName>Undefined</lootBoxName>
|
||||
<tickToOpen>100</tickToOpen>
|
||||
<openReport>AE_Loot_Corpses_Plunder</openReport>
|
||||
<destroyAfterOpening>true</destroyAfterOpening>
|
||||
<loots>
|
||||
<li>
|
||||
<dataName>寻常-材料</dataName>
|
||||
<chance>0.4</chance>
|
||||
<categorys>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~10</count>
|
||||
<category>StoneBlocks</category>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFThingCategoryCount">
|
||||
<count>1~20</count>
|
||||
<category>ResourcesRaw</category>
|
||||
</li>
|
||||
</categorys>
|
||||
</li>
|
||||
<li>
|
||||
<dataName>矿物</dataName>
|
||||
<chance>0.3</chance>
|
||||
<things>
|
||||
<li Class="QuestEditor_Library.CQFThingDefCount">
|
||||
<count>5~50</count>
|
||||
<thing>Steel</thing>
|
||||
</li>
|
||||
</things>
|
||||
</li>
|
||||
</loots>
|
||||
</li>
|
||||
</customThings>
|
||||
<pawns>
|
||||
<li>
|
||||
<key>(6, 0, 4)</key>
|
||||
<value>
|
||||
<li>
|
||||
<kind>Megaspider</kind>
|
||||
<enableLord>true</enableLord>
|
||||
<count>5</count>
|
||||
<faction>Insect</faction>
|
||||
<routeName></routeName>
|
||||
<duty>Defend</duty>
|
||||
<spawnType>MapGeneration</spawnType>
|
||||
</li>
|
||||
</value>
|
||||
</li>
|
||||
<li>
|
||||
<key>(6, 0, 6)</key>
|
||||
<value>
|
||||
<li>
|
||||
<kind>Megaspider</kind>
|
||||
<enableLord>true</enableLord>
|
||||
<count>5</count>
|
||||
<faction>Insect</faction>
|
||||
<routeName></routeName>
|
||||
<duty>Defend</duty>
|
||||
<spawnType>MapGeneration</spawnType>
|
||||
</li>
|
||||
</value>
|
||||
</li>
|
||||
</pawns>
|
||||
<roofRects>
|
||||
<li>
|
||||
<key>RoofRockThin</key>
|
||||
<value>
|
||||
<li>(1,1,8,3)</li>
|
||||
<li>(2,0,8,0)</li>
|
||||
<li>(1,4,2,9)</li>
|
||||
<li>(3,8,12,9)</li>
|
||||
<li>(9,4,12,7)</li>
|
||||
<li>(3,10,10,11)</li>
|
||||
</value>
|
||||
</li>
|
||||
<li>
|
||||
<key>RoofRockThick</key>
|
||||
<value>
|
||||
<li>(3,4,8,7)</li>
|
||||
</value>
|
||||
</li>
|
||||
</roofRects>
|
||||
<terrainsRect>
|
||||
<li>
|
||||
<key>Sandstone_RoughHewn</key>
|
||||
<value>
|
||||
<li>(1,1,8,9)</li>
|
||||
<li>(2,0,8,0)</li>
|
||||
<li>(9,4,12,9)</li>
|
||||
<li>(3,10,10,11)</li>
|
||||
</value>
|
||||
</li>
|
||||
</terrainsRect>
|
||||
<thingDatas>
|
||||
<li>
|
||||
<def>Sandstone</def>
|
||||
<hitPoint>400</hitPoint>
|
||||
<allRect>
|
||||
<li>(1,1,3,1)</li>
|
||||
<li>(2,0,9,0)</li>
|
||||
<li>(1,2,1,9)</li>
|
||||
<li>(2,6,2,10)</li>
|
||||
<li>(8,2,9,3)</li>
|
||||
<li>(3,9,3,11)</li>
|
||||
<li>(9,4,12,4)</li>
|
||||
<li>(10,3,11,3)</li>
|
||||
<li>(4,11,11,11)</li>
|
||||
<li>(12,5,12,10)</li>
|
||||
<li>(10,9,11,10)</li>
|
||||
</allRect>
|
||||
</li>
|
||||
<li>
|
||||
<def>InsectJelly</def>
|
||||
<count>20</count>
|
||||
<hitPoint>0</hitPoint>
|
||||
<allRect>
|
||||
<li>(2,4,2,4)</li>
|
||||
<li>(5,1,5,1)</li>
|
||||
<li>(9,6,9,6)</li>
|
||||
</allRect>
|
||||
</li>
|
||||
<li>
|
||||
<def>Hive</def>
|
||||
<faction>Insect</faction>
|
||||
<hitPoint>130</hitPoint>
|
||||
<allRect>
|
||||
<li>(3,5,3,5)</li>
|
||||
<li>(6,2,6,2)</li>
|
||||
<li>(9,7,9,7)</li>
|
||||
</allRect>
|
||||
</li>
|
||||
<li>
|
||||
<def>GlowPod</def>
|
||||
<hitPoint>50</hitPoint>
|
||||
<allRect>
|
||||
<li>(3,4,3,4)</li>
|
||||
<li>(7,3,7,3)</li>
|
||||
<li>(10,8,10,8)</li>
|
||||
</allRect>
|
||||
</li>
|
||||
<li>
|
||||
<def>InsectJelly</def>
|
||||
<count>20</count>
|
||||
<hitPoint>60</hitPoint>
|
||||
<allRect>
|
||||
<li>(3,4,3,4)</li>
|
||||
<li>(10,7,10,7)</li>
|
||||
</allRect>
|
||||
</li>
|
||||
</thingDatas>
|
||||
</QuestEditor_Library.CustomMapDataDef>
|
||||
</Defs>
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
<QuestEditor_Library.CustomMapDataDef>
|
||||
<defName>Test_Cave</defName>
|
||||
<label>Test Cave</label>
|
||||
<size>(15, 1, 15)</size>
|
||||
<isPart>true</isPart>
|
||||
<commonality>0.8</commonality>
|
||||
<customThings>
|
||||
<li Class="QuestEditor_Library.CustomThingData_CustomMapExit">
|
||||
<def>QE_CustomMapExit</def>
|
||||
<position>(6,0,8)</position>
|
||||
<exitName>CaveExit</exitName>
|
||||
</li>
|
||||
</customThings>
|
||||
<roofRects>
|
||||
<li>
|
||||
<key>RoofRockThick</key>
|
||||
<value>
|
||||
<li>(0,0,14,14)</li>
|
||||
</value>
|
||||
</li>
|
||||
</roofRects>
|
||||
<terrainsRect>
|
||||
<li>
|
||||
<key>Sandstone_RoughHewn</key>
|
||||
<value>
|
||||
<li>(0,0,14,14)</li>
|
||||
</value>
|
||||
</li>
|
||||
</terrainsRect>
|
||||
<thingDatas>
|
||||
<li>
|
||||
<def>Sandstone</def>
|
||||
<hitPoint>400</hitPoint>
|
||||
<allRect>
|
||||
<li>(0,0,14,0)</li>
|
||||
<li>(0,1,0,14)</li>
|
||||
<li>(1,14,14,14)</li>
|
||||
<li>(14,1,14,13)</li>
|
||||
</allRect>
|
||||
</li>
|
||||
</thingDatas>
|
||||
</QuestEditor_Library.CustomMapDataDef>
|
||||
</Defs>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
<QuestEditor_Library.DialogManagerDef>
|
||||
<defName>AE_DialogManager_Survivor</defName>
|
||||
<trees>
|
||||
<li>
|
||||
<tree>AE_Dialog_Survivor</tree>
|
||||
<conditions />
|
||||
</li>
|
||||
</trees>
|
||||
</QuestEditor_Library.DialogManagerDef>
|
||||
</Defs>
|
||||
@@ -1,91 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
<QuestEditor_Library.DialogTreeDef>
|
||||
<defName>AE_Dialog_Survivor</defName>
|
||||
<title>AE_Survivor_Title</title>
|
||||
<requireNonHostile>true</requireNonHostile>
|
||||
<dialogReportKey>DefaultDialogKey</dialogReportKey>
|
||||
<curIndex>2</curIndex>
|
||||
<idleNodes />
|
||||
<nodeMoulds>
|
||||
<li>
|
||||
<key>0</key>
|
||||
<value>
|
||||
<text>AE_Survivor_Text_First</text>
|
||||
<index>0</index>
|
||||
<subNodeIndexs>
|
||||
<li>1</li>
|
||||
</subNodeIndexs>
|
||||
<options>
|
||||
<li>
|
||||
<text>AE_Survivor_Option_Quest</text>
|
||||
<hideWhenDisabled>false</hideWhenDisabled>
|
||||
<results>
|
||||
<li>
|
||||
<resultName>Normal</resultName>
|
||||
<actions />
|
||||
<nextIndex>1</nextIndex>
|
||||
</li>
|
||||
</results>
|
||||
</li>
|
||||
</options>
|
||||
</value>
|
||||
</li>
|
||||
<li>
|
||||
<key>1</key>
|
||||
<value>
|
||||
<text>AE_Survivor_Text_Survival</text>
|
||||
<index>1</index>
|
||||
<parentIndex>0</parentIndex>
|
||||
<subNodeIndexs />
|
||||
<options>
|
||||
<li>
|
||||
<text>AE_Survivor_Welcome</text>
|
||||
<hideWhenDisabled>false</hideWhenDisabled>
|
||||
<results>
|
||||
<li>
|
||||
<resultName>Undefined</resultName>
|
||||
<actions>
|
||||
<li Class="QuestEditor_Library.CQFAction_RemoveDialogManager">
|
||||
<targetsText>
|
||||
<li>Interviewee</li>
|
||||
</targetsText>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFAction_Faction">
|
||||
<targetsText>
|
||||
<li>Interviewee</li>
|
||||
</targetsText>
|
||||
<faction>PlayerColony</faction>
|
||||
</li>
|
||||
</actions>
|
||||
</li>
|
||||
</results>
|
||||
</li>
|
||||
<li>
|
||||
<text>AE_Survivor_WelcomeAE_Survivor_Attack</text>
|
||||
<hideWhenDisabled>false</hideWhenDisabled>
|
||||
<results>
|
||||
<li>
|
||||
<resultName>Undefined</resultName>
|
||||
<actions>
|
||||
<li Class="QuestEditor_Library.CQFAction_RemoveDialogManager">
|
||||
<targetsText>
|
||||
<li>Interviewee</li>
|
||||
</targetsText>
|
||||
</li>
|
||||
<li Class="QuestEditor_Library.CQFAction_Faction">
|
||||
<targetsText>
|
||||
<li>Interviewee</li>
|
||||
</targetsText>
|
||||
<faction>Insect</faction>
|
||||
</li>
|
||||
</actions>
|
||||
</li>
|
||||
</results>
|
||||
</li>
|
||||
</options>
|
||||
</value>
|
||||
</li>
|
||||
</nodeMoulds>
|
||||
</QuestEditor_Library.DialogTreeDef>
|
||||
</Defs>
|
||||
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
|
||||
<HediffDef ParentName="ImplantHediffBase">
|
||||
<defName>AE_BodyPart_Exoskeleton</defName>
|
||||
<label>insect-like exoskeleton</label>
|
||||
<labelNoun>insect-like exoskeleton</labelNoun>
|
||||
<description>The body of this pawn has been partially changed to insect-like, resulting in exoskeletons resembling those of the insect.</description>
|
||||
<defaultLabelColor>(0.98,0,0,0.9)</defaultLabelColor>
|
||||
<addedPartProps>
|
||||
<solid>true</solid>
|
||||
<partEfficiency>1.5</partEfficiency>
|
||||
<betterThanNatural>false</betterThanNatural>
|
||||
</addedPartProps>
|
||||
<stages>
|
||||
<li>
|
||||
<statFactors>
|
||||
<IncomingDamageFactor>0.7</IncomingDamageFactor>
|
||||
</statFactors>
|
||||
</li>
|
||||
</stages>
|
||||
</HediffDef>
|
||||
|
||||
<HediffDef ParentName="AddedBodyPartBase">
|
||||
<defName>AE_BodyPart_CompoundEye</defName>
|
||||
<label>compound eye</label>
|
||||
<labelNoun>compound eye</labelNoun>
|
||||
<description>The eye of this pawn has been partially changed to insect-like,his "eyes" are sharper now.</description>
|
||||
<defaultLabelColor>(0.98,0,0,0.9)</defaultLabelColor>
|
||||
<addedPartProps>
|
||||
<solid>true</solid>
|
||||
<partEfficiency>1.5</partEfficiency>
|
||||
<betterThanNatural>false</betterThanNatural>
|
||||
</addedPartProps>
|
||||
</HediffDef>
|
||||
|
||||
</Defs>
|
||||
@@ -1,122 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
<QuestScriptDef>
|
||||
<root Class="RimWorld.QuestGen.QuestNode_Sequence">
|
||||
<nodes>
|
||||
<li Class="QuestEditor_Library.QuestNode_RandomCustomMap">
|
||||
<datas>
|
||||
<li>
|
||||
<key>AE_CustomMap_SmallInsectCave</key>
|
||||
<value>1</value>
|
||||
</li>
|
||||
</datas>
|
||||
<buffer>5</buffer>
|
||||
<bufferMin>10</bufferMin>
|
||||
<siteIconPath>Things/Building/Natural/Hive/HiveC</siteIconPath>
|
||||
<expandingIconPath>Things/Building/Natural/Hive/HiveC</expandingIconPath>
|
||||
<storeAs>Hive</storeAs>
|
||||
<faction>AncientsHostile</faction>
|
||||
<distance>
|
||||
<min>5</min>
|
||||
<max>10</max>
|
||||
</distance>
|
||||
<blacklist />
|
||||
</li>
|
||||
<li Class="QuestNode_ResolveQuestName">
|
||||
<rules>
|
||||
<rulesStrings>
|
||||
<li>questName->乌拉的呼唤</li>
|
||||
</rulesStrings>
|
||||
</rules>
|
||||
</li>
|
||||
<li Class="QuestNode_ResolveQuestDescription">
|
||||
<rules>
|
||||
<rulesStrings>
|
||||
<li>questDescription->一个强大的心灵实体将它的意志强加于你的意识之中。</li>
|
||||
</rulesStrings>
|
||||
</rules>
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.QuestNode_Root_EventLetter">
|
||||
<letterLabel>乌拉需要你的注意</letterLabel>
|
||||
<letterTitle>乌拉需要你的注意</letterTitle>
|
||||
<letterText>一个强大的心灵实体将它的意志强加于你的意识之中。它自称为“乌拉”,并要求你阅览它的消息。这股力量是压倒性的,不容拒绝。</letterText>
|
||||
<options>
|
||||
<li>
|
||||
<label>阅览消息</label>
|
||||
<optionEffects>
|
||||
<li>
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
|
||||
<defName>Wula_UI_Anisia_1</defName>
|
||||
</li>
|
||||
</effects>
|
||||
</li>
|
||||
</optionEffects>
|
||||
</li>
|
||||
<li>
|
||||
<label>尝试抵抗(但失败了)</label>
|
||||
<optionEffects>
|
||||
<li>
|
||||
<effects>
|
||||
<li Class="WulaFallenEmpire.Effect_OpenCustomUI">
|
||||
<defName>Wula_UI_Anisia_1</defName>
|
||||
</li>
|
||||
<li Class="WulaFallenEmpire.Effect_ShowMessage">
|
||||
<message>你试图抵抗心灵入侵,但这股力量过于强大。无论如何,消息还是涌入了你的脑海。</message>
|
||||
<messageTypeDef>NegativeEvent</messageTypeDef>
|
||||
</li>
|
||||
</effects>
|
||||
</li>
|
||||
</optionEffects>
|
||||
</li>
|
||||
</options>
|
||||
</li>
|
||||
</nodes>
|
||||
</root>
|
||||
<rootSelectionWeight>0</rootSelectionWeight>
|
||||
<rootMinPoints>0</rootMinPoints>
|
||||
<rootMinProgressScore>0</rootMinProgressScore>
|
||||
<rootIncreasesPopulation>False</rootIncreasesPopulation>
|
||||
<minRefireDays>0</minRefireDays>
|
||||
<decreeSelectionWeight>0</decreeSelectionWeight>
|
||||
<questDescriptionRules>
|
||||
<rulesStrings>
|
||||
<li>questDescription->You found a small insect hive nearby.</li>
|
||||
</rulesStrings>
|
||||
<rulesFiles />
|
||||
<rulesRaw IsNull="True" />
|
||||
<include IsNull="True" />
|
||||
</questDescriptionRules>
|
||||
<questNameRules>
|
||||
<rulesStrings>
|
||||
<li>questName -> Small [Adj] Hive</li>
|
||||
</rulesStrings>
|
||||
<rulesFiles>
|
||||
<li>Adj -> Words/Adjectives/Badass</li>
|
||||
</rulesFiles>
|
||||
<rulesRaw IsNull="True" />
|
||||
<include IsNull="True" />
|
||||
</questNameRules>
|
||||
<autoAccept>True</autoAccept>
|
||||
<hideOnCleanup>False</hideOnCleanup>
|
||||
<expireDaysRange>
|
||||
<min>-1</min>
|
||||
<max>-1</max>
|
||||
</expireDaysRange>
|
||||
<nameMustBeUnique>False</nameMustBeUnique>
|
||||
<defaultChallengeRating>-1</defaultChallengeRating>
|
||||
<defaultHidden>False</defaultHidden>
|
||||
<isRootSpecial>True</isRootSpecial>
|
||||
<canGiveRoyalFavor>False</canGiveRoyalFavor>
|
||||
<hideInvolvedFactionsInfo>False</hideInvolvedFactionsInfo>
|
||||
<affectedByPopulation>False</affectedByPopulation>
|
||||
<affectedByPoints>True</affectedByPoints>
|
||||
<defaultCharity>False</defaultCharity>
|
||||
<sendAvailableLetter>True</sendAvailableLetter>
|
||||
<epic>False</epic>
|
||||
<endOnColonyMove>True</endOnColonyMove>
|
||||
<defName>AE_Quest_SmalInsectCave</defName>
|
||||
<ignoreConfigErrors>False</ignoreConfigErrors>
|
||||
<ignoreIllegalLabelCharacterConfigError>False</ignoreIllegalLabelCharacterConfigError>
|
||||
</QuestScriptDef>
|
||||
</Defs>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Defs>
|
||||
|
||||
<!-- <ThoughtDef>
|
||||
<defName>AE_Thought_CompoundEye</defName>
|
||||
<workerClass>ThoughtWorker_Hediff</workerClass>
|
||||
<hediff>AE_BodyPart_CompoundEye</hediff>
|
||||
<validWhileDespawned>true</validWhileDespawned>
|
||||
<stages>
|
||||
<li>
|
||||
<label>pregnancy mood collapse</label>
|
||||
<description>This pregnancy is so hard. My body always feels awful. So exhausting!</description>
|
||||
<baseMoodEffect>-14</baseMoodEffect>
|
||||
</li>
|
||||
</stages>
|
||||
</ThoughtDef> -->
|
||||
|
||||
|
||||
|
||||
|
||||
</Defs>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LanguageData>
|
||||
|
||||
<AE_CustomMap_SmallInsectCave.label>小型虫巢</AE_CustomMap_SmallInsectCave.label>
|
||||
|
||||
</LanguageData>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LanguageData>
|
||||
|
||||
<AE_BodyPart_Exoskeleton.label>虫族外骨骼</AE_BodyPart_Exoskeleton.label>
|
||||
<AE_BodyPart_Exoskeleton.labelNoun>虫族外骨骼</AE_BodyPart_Exoskeleton.labelNoun>
|
||||
<AE_BodyPart_Exoskeleton.description>这家伙的身体被部分虫化了,一具虫族的外骨骼生长在这家伙的躯干上。</AE_BodyPart_Exoskeleton.description>
|
||||
|
||||
<AE_BodyPart_CompoundEye.label>复眼</AE_BodyPart_CompoundEye.label>
|
||||
<AE_BodyPart_CompoundEye.labelNoun>复眼</AE_BodyPart_CompoundEye.labelNoun>
|
||||
<AE_BodyPart_CompoundEye.description>这家伙的身体被部分虫化了,现在Ta的“眼睛”会更加敏锐。</AE_BodyPart_CompoundEye.description>
|
||||
|
||||
</LanguageData>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LanguageData>
|
||||
|
||||
<AE_Quest_SmalInsectCave.questDescriptionRules.rulesStrings>
|
||||
<li>questDescription->你在附近发现了一个小型虫族巢穴。</li>
|
||||
</AE_Quest_SmalInsectCave.questDescriptionRules.rulesStrings>
|
||||
|
||||
<AE_Quest_SmalInsectCave.questNameRules.rulesStrings>
|
||||
<li>questName -> 小型[Adj]巢穴</li>
|
||||
</AE_Quest_SmalInsectCave.questNameRules.rulesStrings>
|
||||
</LanguageData>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LanguageData>
|
||||
|
||||
<AE_Loot_Corpses_Plunder>搜刮。</AE_Loot_Corpses_Plunder>
|
||||
<AE_Loot_Corpses_Rare>哇哦,看来你幸运地搜刮到了死在这里的人留下的宝藏。</AE_Loot_Corpses_Rare>
|
||||
|
||||
<AE_LootBox_Message_Survivor>一个人从尸骸中浮现出来。</AE_LootBox_Message_Survivor>
|
||||
<AE_Survivor_Title>与尸骸中的“幸存者”的交谈</AE_Survivor_Title>
|
||||
<AE_Survivor_Text_First>你在尸骸中搜刮到了一个“人类”,值得注意的是你留意到了{Interviewee_possessive}身上的虫族特征。</AE_Survivor_Text_First>
|
||||
<AE_Survivor_Option_Quest>你是什么人?</AE_Survivor_Option_Quest>
|
||||
<AE_Survivor_Text_Survival>“我是{Interviewee_nameDef},我在一次虫潮中被虫群所俘获,老实说我也不知道为什么它们唯独没有杀我,我醒来后身体就成这样了。我现在无处可去,请让我加入你们”</AE_Survivor_Text_Survival>
|
||||
<AE_Survivor_Welcome>欢迎</AE_Survivor_Welcome>
|
||||
<AE_Survivor_WelcomeAE_Survivor_Attack>一定是新型虫族!必须歼灭!</AE_Survivor_WelcomeAE_Survivor_Attack>
|
||||
|
||||
|
||||
|
||||
</LanguageData>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LanguageData>
|
||||
|
||||
<AE_Loot_Corpses_Plunder>Plunder</AE_Loot_Corpses_Plunder>
|
||||
|
||||
<AE_Loot_Corpses_Rare>Wow, it seems you were lucky enough to find some treasure left by the people who died here.</AE_Loot_Corpses_Rare>
|
||||
|
||||
<AE_LootBox_Message_Survivor>A person emerged from the corpse.</AE_LootBox_Message_Survivor>
|
||||
<AE_Survivor_Title>Talk with the 'Survivor'</AE_Survivor_Title>
|
||||
<AE_Survivor_Text_First>You found a "human" in the corpse and you can't help but notice insect characteristics on {Interviewee_objective}.</AE_Survivor_Text_First>
|
||||
<AE_Survivor_Option_Quest>Who are you?</AE_Survivor_Option_Quest>
|
||||
<AE_Survivor_Text_Survival>"I am {Interviewee_nameDef}. I was captured by a swarm of insects during a wave. To be honest, I don't know why they only didn't kill me. When I woke up, my body was like this. I've got nowhere to go now, please let me join you."</AE_Survivor_Text_Survival>
|
||||
<AE_Survivor_Welcome>Welcome</AE_Survivor_Welcome>
|
||||
<AE_Survivor_WelcomeAE_Survivor_Attack>It must be a new type of Insect! It must be annihilated!</AE_Survivor_WelcomeAE_Survivor_Attack>
|
||||
|
||||
|
||||
</LanguageData>
|
||||
Reference in New Issue
Block a user