This commit is contained in:
2025-10-15 17:37:42 +08:00
20 changed files with 818 additions and 118 deletions

View File

@@ -17,7 +17,7 @@
</li>
<li>
<tick>240</tick>
<angle>3</angle>
<angle>2</angle>
</li>
<li>
<tick>540</tick>
@@ -25,7 +25,7 @@
</li>
<li>
<tick>780</tick>
<angle>-3</angle>
<angle>-2</angle>
</li>
<li>
<tick>1080</tick>

View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<HediffDef>
<defName>ARA_Hediff_FrostCoverd_after</defName>
<label>霜冻覆盖</label>
<description>此人被一片冰霜之云所覆盖。</description>
<defaultLabelColor>(1, 1, 0.8)</defaultLabelColor>
<hediffClass>HediffWithComps</hediffClass>
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-4</severityPerDay>
</li>
<li Class="ArachnaeSwarm.HediffCompProperties_TimedExplosion">
<damageDef>ARA_Damage_Freeze</damageDef>
<soundDef>Explosion_Stun</soundDef>
<explosionRadius>3</explosionRadius>
<showRemainingTime>true</showRemainingTime>
<damageAmount>5</damageAmount>
<armorPenetration>0.10</armorPenetration>
<disappearsAfterTicks>60~120</disappearsAfterTicks>
<postExplosionSpawnThingDef>ARA_FrostGasCloud</postExplosionSpawnThingDef>
<postExplosionSpawnChance>1</postExplosionSpawnChance>
<postExplosionSpawnThingCount>1</postExplosionSpawnThingCount>
</li>
<li Class="HediffCompProperties_DisappearsOnDeath"/>
</comps>
<stages>
</stages>
</HediffDef>
<HediffDef>
<defName>ARA_CryoShock</defName>
<label>低温休克</label>
<description>此人正处于低温休克状态。</description>
<defaultLabelColor>(1, 1, 0.8)</defaultLabelColor>
<hediffClass>ArachnaeSwarm.HediffCurseFlame</hediffClass>
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-6</severityPerDay>
</li>
</comps>
<modExtensions>
<li Class="ArachnaeSwarm.CurseFlameModExt">
<damageDefName>Stun</damageDefName>
<damageRange>1~2</damageRange>
<damageIntervalTicks>80</damageIntervalTicks>
</li>
</modExtensions>
<injuryProps>
<painPerSeverity>0.02</painPerSeverity>
</injuryProps>
<stages>
<li>
<label>轻微</label>
<becomeVisible>true</becomeVisible>
</li>
<li>
<label>轻微</label>
<minSeverity>0.2</minSeverity>
<statOffsets>
<ComfyTemperatureMin>40</ComfyTemperatureMin>
</statOffsets>
<capMods>
<li>
<capacity>Consciousness</capacity>
<offset>-0.10</offset>
</li>
</capMods>
</li>
<li>
<label>中等</label>
<minSeverity>0.35</minSeverity>
<statOffsets>
<ComfyTemperatureMin>80</ComfyTemperatureMin>
</statOffsets>
<capMods>
<li>
<capacity>Consciousness</capacity>
<offset>-0.20</offset>
</li>
</capMods>
</li>
<li>
<label>严重</label>
<minSeverity>0.5</minSeverity>
<statOffsets>
<ComfyTemperatureMin>120</ComfyTemperatureMin>
</statOffsets>
<capMods>
<li>
<capacity>Consciousness</capacity>
<offset>-0.20</offset>
</li>
</capMods>
</li>
<li>
<label>极度</label>
<minSeverity>0.65</minSeverity>
<statOffsets>
<ComfyTemperatureMin>160</ComfyTemperatureMin>
</statOffsets>
<capMods>
<li>
<capacity>Consciousness</capacity>
<offset>-0.30</offset>
</li>
</capMods>
</li>
<li>
<label>完全</label>
<minSeverity>0.85</minSeverity>
<statOffsets>
<ComfyTemperatureMin>200</ComfyTemperatureMin>
</statOffsets>
<capMods>
<li>
<capacity>Consciousness</capacity>
<offset>-0.30</offset>
</li>
</capMods>
</li>
</stages>
</HediffDef>
<!-- 然后是冷冻伤害本身的定义 -->
<DamageDef Name="ARA_Damage_Freeze">
<defName>ARA_Damage_Freeze</defName>
<label>冷冻</label>
<workerClass>DamageWorker_Frostbite</workerClass> <!-- 使用原版的伤害工人 -->
<externalViolence>true</externalViolence>
<deathMessage>{0}的身体在极寒中化为了冰雕,随后碎裂四散。</deathMessage>
<hediff>Frostbite</hediff> <!-- 核心将伤害转化为这个Hediff的严重性 -->
<harmsHealth>false</harmsHealth> <!-- 核心!不造成常规生命值伤害 -->
<armorCategory>Heat</armorCategory> <!-- 可以被抗热/冷的护甲减免 -->
<overkillPctToDestroyPart>0</overkillPctToDestroyPart> <!-- 伤害足够时直接摧毁我们的C#代码会处理这个) -->
<defaultDamage>3</defaultDamage>
<defaultArmorPenetration>0</defaultArmorPenetration>
<buildingDamageFactorImpassable>0</buildingDamageFactorImpassable>
<buildingDamageFactorPassable>0</buildingDamageFactorPassable>
<explosionHeatEnergyPerCell>-15</explosionHeatEnergyPerCell>
<explosionCellFleck>ARA_BlastFrost</explosionCellFleck>
<explosionColorCenter>(0.52, 0.7, 0.95,0.5)</explosionColorCenter>
<explosionColorEdge>(0.52, 1, 0.95,0.5)</explosionColorEdge>
<soundExplosion>Explosion_Stun</soundExplosion>
</DamageDef>
<!-- 然后是冷冻伤害本身的定义 -->
<DamageDef ParentName="ARA_Damage_Freeze">
<defName>ARA_Damage_Freeze_ex</defName>
<additionalHediffs>
<li>
<hediff>ARA_CryoShock</hediff>
<severityPerDamageDealt>0.01</severityPerDamageDealt>
</li>
<li>
<hediff>ARA_Hediff_FrostCoverd_after</hediff>
<severityPerDamageDealt>0.01</severityPerDamageDealt>
<inverseStatScaling>true</inverseStatScaling>
<victimSeverityScalingByInvBodySize>true</victimSeverityScalingByInvBodySize>
</li>
</additionalHediffs>
</DamageDef>
</Defs>

View File

@@ -12,4 +12,16 @@
<shaderType>MoteGlow</shaderType> <!-- Use standard Mote shader -->
</graphicData>
</FleckDef>
<FleckDef ParentName="FleckBase">
<defName>ARA_BlastFrost</defName>
<graphicData>
<texPath>ArachnaeSwarm/Mote/BlastFrost</texPath>
<shaderType>TransparentPostLight</shaderType>
<renderInstanced>true</renderInstanced>
</graphicData>
<altitudeLayer>MoteOverhead</altitudeLayer>
<solidTime>0</solidTime>
<fadeOutTime>0.2</fadeOutTime>
</FleckDef>
</Defs>

View File

@@ -14,7 +14,7 @@
<capMods>
<li>
<capacity>Moving</capacity>
<offset>-0.4</offset>
<offset>-0.5</offset>
</li>
</capMods>
</li>

View File

@@ -41,31 +41,86 @@
</ThingDef>
<!-- Gas : Thing -->
<ThingDef ParentName="ARA_BaseGas">
<defName>ARA_AcidGasCloud</defName>
<label>阿拉克涅酸雾</label>
<graphicData>
<texPath>Things/Gas/Puff</texPath>
<drawSize>2.6</drawSize>
<color>(0.56, 1, 0.03,0.5)</color>
</graphicData>
<gas>
<expireSeconds>
<min>30</min>
<max>50</max>
</expireSeconds>
<!--<blockTurretTracking>true</blockTurretTracking>
<ThingDef ParentName="ARA_BaseGas">
<defName>ARA_AcidGasCloud</defName>
<label>阿拉克涅酸雾</label>
<graphicData>
<texPath>Things/Gas/Puff</texPath>
<drawSize>2.6</drawSize>
<color>(0.56, 1, 0.03,0.5)</color>
</graphicData>
<gas>
<expireSeconds>
<min>30</min>
<max>50</max>
</expireSeconds>
<!--<blockTurretTracking>true</blockTurretTracking>
<accuracyPenalty>0.3</accuracyPenalty>-->
<rotationSpeed>20</rotationSpeed>
</gas>
<thingClass>ArachnaeSwarm.OPToxicGas</thingClass>
<modExtensions>
<li Class="ArachnaeSwarm.OPToxicDefs">
<OPToxicHediff>ARA_AcidCoverd</OPToxicHediff> <!-- Name of HediffDef to apply, change to own HediffDef -->
<OPToxicSeverity>0.1</OPToxicSeverity> <!-- Severity build up per Tick period -->
<OPSevUpTickPeriod>240</OPSevUpTickPeriod> <!-- No of game ticks per each build up, recommend >= 120 -->
</li>
</modExtensions>
</ThingDef>
<rotationSpeed>20</rotationSpeed>
</gas>
<thingClass>ArachnaeSwarm.OPToxicGas</thingClass>
<modExtensions>
<li Class="ArachnaeSwarm.OPToxicDefs">
<OPToxicHediff>ARA_AcidCoverd</OPToxicHediff> <!-- Name of HediffDef to apply, change to own HediffDef -->
<OPToxicSeverity>0.1</OPToxicSeverity> <!-- Severity build up per Tick period -->
<OPSevUpTickPeriod>240</OPSevUpTickPeriod> <!-- No of game ticks per each build up, recommend >= 120 -->
</li>
</modExtensions>
</ThingDef>
<!-- Gas : Thing -->
<ThingDef ParentName="ARA_BaseGas">
<defName>ARA_FrostGasCloud_Ex</defName>
<label>Frost cloud</label>
<graphicData>
<texPath>Things/Gas/Puff</texPath>
<drawSize>2.6</drawSize>
<color>(0.52, 1, 0.95,0.5)</color>
</graphicData>
<gas>
<expireSeconds>
<min>5</min>
<max>10</max>
</expireSeconds>
<!--<blockTurretTracking>true</blockTurretTracking>
<accuracyPenalty>0.3</accuracyPenalty>-->
<rotationSpeed>5</rotationSpeed>
</gas>
<thingClass>ArachnaeSwarm.OPToxicGas</thingClass>
<modExtensions>
<li Class="ArachnaeSwarm.OPToxicDefs">
<OPToxicHediff>ARA_Hediff_FrostCoverd_after</OPToxicHediff> <!-- Name of HediffDef to apply, change to own HediffDef -->
<OPToxicSeverity>0.1</OPToxicSeverity> <!-- Severity build up per Tick period -->
<OPSevUpTickPeriod>240</OPSevUpTickPeriod> <!-- No of game ticks per each build up, recommend >= 120 -->
</li>
</modExtensions>
</ThingDef>
<ThingDef ParentName="ARA_BaseGas">
<defName>ARA_FrostGasCloud</defName>
<label>Frost cloud</label>
<graphicData>
<texPath>Things/Gas/Puff</texPath>
<drawSize>2.6</drawSize>
<color>(0.52, 1, 0.95,0.5)</color>
</graphicData>
<gas>
<expireSeconds>
<min>5</min>
<max>10</max>
</expireSeconds>
<!--<blockTurretTracking>true</blockTurretTracking>
<accuracyPenalty>0.3</accuracyPenalty>-->
<rotationSpeed>5</rotationSpeed>
</gas>
<thingClass>ArachnaeSwarm.OPToxicGas</thingClass>
<modExtensions>
<li Class="ArachnaeSwarm.OPToxicDefs">
<OPToxicHediff>ARA_CryoShock</OPToxicHediff> <!-- Name of HediffDef to apply, change to own HediffDef -->
<OPToxicSeverity>0.1</OPToxicSeverity> <!-- Severity build up per Tick period -->
<OPSevUpTickPeriod>240</OPSevUpTickPeriod> <!-- No of game ticks per each build up, recommend >= 120 -->
</li>
</modExtensions>
</ThingDef>
</Defs>

View File

@@ -37,13 +37,13 @@
<apparelDef>ARA_SpiderOne_PowerArmor</apparelDef>
</li>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelCapacity>10.0</fuelCapacity>
<fuelCapacity>100.0</fuelCapacity>
<fuelFilter>
<categories>
<li>FoodMeals</li>
<li>FoodRaw</li>
</categories>
<thingDefs>
<li>ARA_InsectJelly</li>
</thingDefs>
</fuelFilter>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<initialFuelPercent>1</initialFuelPercent>
<autoRefuelPercent>0.5</autoRefuelPercent>
<showFuelGizmo>true</showFuelGizmo>
@@ -66,6 +66,7 @@
<buildingDef>ARA_Building_SpiderOne</buildingDef>
<hediffOnEmptyFuel>ARA_PowerArmor_NoFuel</hediffOnEmptyFuel>
<fuelConsumptionRate>0.5</fuelConsumptionRate>
<powerArmorWeapon>ARA_RW_Icez_Mortar_Turretgun</powerArmorWeapon>
</li>
</modExtensions>
<recipeMaker>
@@ -110,13 +111,14 @@
</cocoonDefs>
</li>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelCapacity>10.0</fuelCapacity>
<silent>true</silent>
<fuelCapacity>100.0</fuelCapacity>
<fuelFilter>
<categories>
<li>FoodMeals</li>
<li>FoodRaw</li>
</categories>
<thingDefs>
<li>ARA_InsectJelly</li>
</thingDefs>
</fuelFilter>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<initialFuelPercent>1</initialFuelPercent>
<autoRefuelPercent>0.5</autoRefuelPercent>
<showFuelGizmo>true</showFuelGizmo>

View File

@@ -1103,7 +1103,7 @@
<isMortar>true</isMortar>
<requireLineOfSight>false</requireLineOfSight>
<minRange>5</minRange>
<range>120</range>
<range>80</range>
<burstShotCount>1</burstShotCount>
<soundCast>SpitterSpit</soundCast>
<targetParams>

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<ThingDef ParentName="BaseHumanMakeableGun" Name="ARA_RW_Icez_Mortar">
<defName>ARA_RW_Icez_Mortar</defName>
<label>武装器官"霜星炮"</label>
<description>阿拉克涅虫群督虫使用大型远程武装器官,可以发射极度冰冷的霜冻气团,被接触到的敌人会产生霜冻爆炸,一旦被冻结不会立即致死,但随之而来的任何外来伤害都会立刻摧毁脆弱的冰雕。</description>
<tickerType>Normal</tickerType>
<techLevel>Animal</techLevel>
<descriptionHyperlinks>
<ThingDef>ARA_Cocoon_Weapon_2Stage</ThingDef>
</descriptionHyperlinks>
<graphicData>
<texPath>ArachnaeSwarm/Weapon/ARA_RW_Acid_Mortar</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>1.5</drawSize>
</graphicData>
<uiIconScale>0.75</uiIconScale>
<soundInteract>SpitterSpawn</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False" />
<researchPrerequisite>ARA_Technology_9VXI</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<statBases>
<MarketValue>2500</MarketValue>
<WorkToMake>1300</WorkToMake>
<!-- <MarketValue>370</MarketValue> -->
<Mass>3.5</Mass>
<AccuracyTouch>0.3</AccuracyTouch>
<AccuracyShort>0.3</AccuracyShort>
<AccuracyMedium>0.25</AccuracyMedium>
<AccuracyLong>0.1</AccuracyLong>
<RangedWeapon_Cooldown>3.5</RangedWeapon_Cooldown>
<ARA_IncubationCost>450</ARA_IncubationCost>
<ARA_IncubationTime>15</ARA_IncubationTime>
</statBases>
<verbs>
<li>
<verbClass>Verb_Shoot</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<forceNormalTimeSpeed>false</forceNormalTimeSpeed>
<warmupTime>1.5</warmupTime>
<forcedMissRadius>3</forcedMissRadius>
<defaultProjectile>Bullet_ARA_RW_Icez_Mortar</defaultProjectile>
<isMortar>false</isMortar>
<requireLineOfSight>false</requireLineOfSight>
<minRange>5</minRange>
<range>49</range>
<burstShotCount>1</burstShotCount>
<soundCast>SpitterSpit</soundCast>
<targetParams>
<canTargetLocations>true</canTargetLocations>
</targetParams>
</li>
</verbs>
<costList Inherit="False">
<ARA_Carapace>50</ARA_Carapace>
</costList>
<weaponTags Inherit="False">
<li>ARA_Armed_Organ</li>
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T2</li>
</weaponTags>
<generateCommonality>0</generateCommonality>
<tradeability>None</tradeability>
<thingSetMakerTags Inherit="False"/>
<comps>
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
<cocoonDefs>
<li>ARA_Cocoon_Weapon_2Stage</li>
<li>ARA_BioforgeIncubator_Thing</li>
</cocoonDefs>
</li>
<li Class="ArachnaeSwarm.CompProperties_CustomUniqueWeapon" MayRequire="Ludeon.RimWorld.Odyssey">
<forcedTraits>
<li>ARA_Huge_Weapon</li>
<li>ARA_Weapon_Damage_FrostExplode</li>
</forcedTraits>
<numTraitsRange>
<min>2</min>
<max>2</max>
</numTraitsRange>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BaseBullet">
<defName>Bullet_ARA_RW_Icez_Mortar</defName>
<label>大型霜雾团</label>
<graphicData>
<graphicClass>Graphic_Single_AgeSecs</graphicClass>
<texPath>Things/Projectile/WaterSplash</texPath>
<drawSize>(.75, .75)</drawSize>
<shaderType>MoteGlow</shaderType>
</graphicData>
<uiIconScale>0.8</uiIconScale>
<thingClass>Projectile_Explosive</thingClass>
<projectile>
<useGraphicClass>True</useGraphicClass>
<shadowSize>1</shadowSize>
<damageDef>ARA_Damage_Freeze_ex</damageDef>
<damageAmountBase>25</damageAmountBase>
<speed>75</speed>
<arcHeightFactor>1</arcHeightFactor>
<explosionRadius>3.5</explosionRadius>
<flyOverhead></flyOverhead>
<postExplosionSpawnThingDef>ARA_FrostGasCloud_Ex</postExplosionSpawnThingDef> <!-- ARL_ToxicGasCloud -->
<postExplosionSpawnChance>1</postExplosionSpawnChance>
<postExplosionSpawnThingCount>1</postExplosionSpawnThingCount>
<soundExplode>Explosion_Stun</soundExplode>
</projectile>
<comps>
<!--<li Class="CompProperties_ProjectileEffecter">
<effecterDef>Shell_AcidSpitStream</effecterDef>
</li>
<li Class="CompProperties_ProjectileEffecter">
<effecterDef>Shell_AcidSpitLaunched</effecterDef>
</li>-->
</comps>
</ThingDef>
<ThingDef ParentName="ARA_RW_Icez_Mortar">
<defName>ARA_RW_Icez_Mortar_Turretgun</defName>
<destroyOnDrop>true</destroyOnDrop>
<recipeMaker Inherit="False">
</recipeMaker>
</ThingDef>
</Defs>

View File

@@ -77,4 +77,15 @@
<statFactors>
</statFactors>
</WeaponTraitDef>
<WeaponTraitDef>
<defName>ARA_Weapon_Damage_FrostExplode</defName>
<label>霜冻爆炸</label>
<description>这种武装器官发射的霜冻气雾能够沾染敌人,被沾染的敌人会再次爆炸。</description>
<commonality>1</commonality>
<weaponCategory>ARA_Weapon_Damage_Category</weaponCategory>
<statOffsets>
</statOffsets>
<statFactors>
</statFactors>
</WeaponTraitDef>
</Defs>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
<!-- Power Armor Interaction -->
<EnterPowerArmor>进入 {0}</EnterPowerArmor>
<CannotEnterPowerArmor>无法进入外骨骼</CannotEnterPowerArmor>
<PowerArmorBroken>{0} 已经被损毁!</PowerArmorBroken>
<!-- Gizmo Labels -->
<StructurePoints>结构点</StructurePoints>
</LanguageData>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
<!-- Power Armor Interaction -->
<EnterPowerArmor>Enter {0}</EnterPowerArmor>
<CannotEnterPowerArmor>Cannot enter power armor</CannotEnterPowerArmor>
<PowerArmorBroken>{0} has been broken!</PowerArmorBroken>
<!-- Gizmo Labels -->
<StructurePoints>Structure</StructurePoints>
</LanguageData>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -1,19 +1,18 @@
using RimWorld;
using Verse;
using Verse.Sound;
namespace ArachnaeSwarm
{
[DefOf]
public static class ARA_HediffDefOf
{
public static HediffDef ARA_AcidCoverd;
public static HediffDef ARA_HiveMindMaster;
public static HediffDef ARA_HiveMindDrone;
public static HediffDef ARA_HiveMindWorker;
public static HediffDef ARA_HiveMindWorker; // 如果存在这个Def
static ARA_HediffDefOf()
{
DefOfHelper.EnsureInitializedInCtor(typeof(HediffDefOf));
DefOfHelper.EnsureInitializedInCtor(typeof(ARA_HediffDefOf));
}
}
}

View File

@@ -193,6 +193,7 @@
<Compile Include="Hediffs\MoharHediffs\HediffComp_Spawner.cs" />
<Compile Include="Hediffs\MoharHediffs\Tools.cs" />
<Compile Include="Hediffs\ProphecyGearEffect.cs" />
<Compile Include="HediffComp_TimedExplosion.cs" />
<Compile Include="Hediffs\WULA_HediffDamgeShield\DRMDamageShield.cs" />
<Compile Include="Hediffs\WULA_HediffDamgeShield\Hediff_DamageShield.cs" />
<Compile Include="Thing_Comps\ARA_ThingComp_GuardianPsyField\Hediff_DynamicInterceptor.cs" />
@@ -278,7 +279,6 @@
<Compile Include="PowerArmor\CompPowerArmorStation.cs" />
<Compile Include="PowerArmor\Gizmo_StructurePanel.cs" />
<Compile Include="PowerArmor\JobDriver_EnterPowerArmor.cs" />
<Compile Include="PowerArmor\Harmony_ThingWithComps_GetFloatMenuOptions.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Jobs\JobDriver_SuperCarry\SuperCarryExtension.cs" />

View File

@@ -7,13 +7,27 @@ namespace ArachnaeSwarm
{
public class CompProperties_RefuelableNutrition : CompProperties_Refuelable
{
public bool silent = false;
public CompProperties_RefuelableNutrition()
{
compClass = typeof(CompRefuelableNutrition);
// 默认启用这些Gizmo除非在XML中明确设置为false
this.targetFuelLevelConfigurable = true;
this.showAllowAutoRefuelToggle = true;
}
public override IEnumerable<StatDrawEntry> SpecialDisplayStats(StatRequest req)
{
if (silent)
{
yield break; // If silent, return nothing.
}
foreach (var stat in base.SpecialDisplayStats(req))
{
yield return stat;
}
}
}
[StaticConstructorOnStartup]

View File

@@ -0,0 +1,244 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using Verse.Noise;
namespace ArachnaeSwarm // 修正命名空间
{
public class HediffComp_TimedExplosion : HediffComp
{
// 倒计时相关字段
public int ticksToDisappear;
public int disappearsAfterTicks;
public int seed;
// 配置属性快捷访问
public HediffCompProperties_TimedExplosion Props =>
(HediffCompProperties_TimedExplosion)props;
// 消失判定属性
public override bool CompShouldRemove
{
get
{
if (ticksToDisappear > 0) return false;
if (Props.requiredMentalState != null)
{
return parent.pawn.MentalStateDef != Props.requiredMentalState;
}
return true;
}
}
// 进度计算
public float Progress =>
1f - (float)ticksToDisappear / Mathf.Max(1, disappearsAfterTicks);
public int EffectiveTicksToDisappear => ticksToDisappear / TicksLostPerTick;
public float NoisyProgress => AddNoiseToProgress(Progress, seed);
public virtual int TicksLostPerTick => 1;
public override string CompLabelInBracketsExtra
{
get
{
if (Props.showRemainingTime)
{
if (EffectiveTicksToDisappear < 2500)
{
return EffectiveTicksToDisappear.ToStringSecondsFromTicks("F0");
}
return EffectiveTicksToDisappear.ToStringTicksToPeriod(allowSeconds: true, shortForm: true, canUseDecimals: true, allowYears: true, Props.canUseDecimalsShortForm);
}
return base.CompLabelInBracketsExtra;
}
}
private static float AddNoiseToProgress(float progress, int seed)
{
float num = (float)Perlin.GetValue(progress, 0.0, 0.0, 9.0, seed);
float num2 = 0.25f * (1f - progress);
return Mathf.Clamp01(progress + num2 * num);
}
// 初始化
public override void CompPostMake()
{
base.CompPostMake();
disappearsAfterTicks = Props.disappearsAfterTicks.RandomInRange;
seed = Rand.Int;
ticksToDisappear = disappearsAfterTicks;
}
// 每帧更新
public override void CompPostTick(ref float severityAdjustment)
{
ticksToDisappear--;
if (CompShouldRemove)
{
parent.pawn.health.RemoveHediff(parent);
}
}
// 移除后处理
public override void CompPostPostRemoved()
{
base.CompPostPostRemoved();
// 处理新鲜伤口状态
if (!Props.leaveFreshWounds && parent.Part != null)
{
foreach (BodyPartRecord part in parent.Part.GetPartAndAllChildParts())
{
Hediff_MissingPart hediff = parent.pawn.health.hediffSet.GetMissingPartFor(part) as Hediff_MissingPart;
if (hediff != null)
{
hediff.IsFresh = false;
}
}
}
// 触发爆炸逻辑
if (ShouldTriggerExplosion())
{
TriggerExplosion();
DestroyGearIfNeeded();
}
// 发送消息通知
if (!Props.messageOnDisappear.NullOrEmpty() && PawnUtility.ShouldSendNotificationAbout(parent.pawn))
{
Messages.Message(
Props.messageOnDisappear.Formatted(parent.pawn.Named("PAWN")),
parent.pawn,
MessageTypeDefOf.NeutralEvent
);
}
// 发送信件通知
if (!Props.letterTextOnDisappear.NullOrEmpty() &&
!Props.letterLabelOnDisappear.NullOrEmpty() &&
PawnUtility.ShouldSendNotificationAbout(parent.pawn))
{
Find.LetterStack.ReceiveLetter(
Props.letterLabelOnDisappear.Formatted(parent.pawn.Named("PAWN")),
Props.letterTextOnDisappear.Formatted(parent.pawn.Named("PAWN")),
LetterDefOf.NegativeEvent,
parent.pawn
);
}
}
// 爆炸条件检查
private bool ShouldTriggerExplosion()
{
return parent.pawn.Spawned &&
Props.explosionRadius > 0.01f &&
Props.damageDef != null &&
parent.pawn.Map != null;
}
// 执行爆炸
private void TriggerExplosion()
{
GenExplosion.DoExplosion(
center: parent.pawn.Position,
map: parent.pawn.Map,
radius: Props.explosionRadius,
damType: Props.damageDef,
instigator: parent.pawn,
damAmount: Props.damageAmount,
armorPenetration: Props.armorPenetration,
explosionSound: Props.soundDef,
weapon: null,
projectile: null,
intendedTarget: null,
postExplosionSpawnThingDef: Props.postExplosionSpawnThingDef,
postExplosionSpawnChance: Props.postExplosionSpawnChance,
postExplosionSpawnThingCount: Props.postExplosionSpawnThingCount,
postExplosionGasType: null,
applyDamageToExplosionCellsNeighbors: false,
chanceToStartFire: Props.chanceToStartFire,
damageFalloff: Props.damageFalloff,
direction: null,
ignoredThings: new List<Thing> { parent.pawn }
);
}
// 装备销毁
private void DestroyGearIfNeeded()
{
if (!Props.destroyGear) return;
if (parent.pawn.equipment != null)
{
parent.pawn.equipment.DestroyAllEquipment(DestroyMode.Vanish);
}
if (parent.pawn.apparel != null)
{
parent.pawn.apparel.DestroyAll(DestroyMode.Vanish);
}
}
// 数据持久化
public override void CompExposeData()
{
Scribe_Values.Look(ref ticksToDisappear, "ticksToDisappear", 0);
Scribe_Values.Look(ref disappearsAfterTicks, "disappearsAfterTicks", 0);
Scribe_Values.Look(ref seed, "seed", 0);
}
// 调试信息
public override string CompDebugString()
{
return $"倒计时: {ticksToDisappear}\n爆炸半径: {Props.explosionRadius}";
}
}
public class HediffCompProperties_TimedExplosion : HediffCompProperties
{
[Header("消失设置")]
public IntRange disappearsAfterTicks = new IntRange(600, 1200);
public bool showRemainingTime = true;
public bool canUseDecimalsShortForm;
public MentalStateDef requiredMentalState;
public bool leaveFreshWounds = true;
[Header("爆炸设置")]
public float explosionRadius = 3f;
public DamageDef damageDef;
public int damageAmount = 20;
public float armorPenetration;
public SoundDef soundDef;
public float chanceToStartFire;
public bool damageFalloff = true;
[Header("后续效果")]
public bool destroyGear;
public GasType gasType;
public ThingDef postExplosionSpawnThingDef;
public float postExplosionSpawnChance;
public int postExplosionSpawnThingCount = 1;
[Header("通知设置")]
[MustTranslate]
public string messageOnDisappear;
[MustTranslate]
public string letterLabelOnDisappear;
[MustTranslate]
public string letterTextOnDisappear;
public bool sendLetterOnDisappearIfDead = true;
public HediffCompProperties_TimedExplosion()
{
compClass = typeof(HediffComp_TimedExplosion);
}
}
}

View File

@@ -19,6 +19,7 @@ namespace ArachnaeSwarm
public float structurePointsMax = 500f;
public HediffDef hediffOnEmptyFuel;
public float fuelConsumptionRate = 0.5f; // Nutrition per day
public ThingDef powerArmorWeapon;
}
[StaticConstructorOnStartup]
@@ -51,6 +52,18 @@ namespace ArachnaeSwarm
public float StructurePointsPercent => StructurePoints / StructurePointsMax;
public Building sourceBuilding;
private ThingWithComps originalWeapon; // Still needed to store pawn's original weapon
private ThingWithComps currentPowerArmorWeapon; // Track the currently equipped power armor weapon
public void SetOriginalWeapon(ThingWithComps weapon)
{
originalWeapon = weapon;
}
public void SetCurrentPowerArmorWeapon(ThingWithComps weapon)
{
currentPowerArmorWeapon = weapon;
}
#endregion
#region Ticker
@@ -110,6 +123,8 @@ namespace ArachnaeSwarm
base.ExposeData();
Scribe_Values.Look(ref structurePoints, "structurePoints", -1f);
Scribe_References.Look(ref sourceBuilding, "sourceBuilding");
Scribe_References.Look(ref originalWeapon, "originalWeapon");
Scribe_References.Look(ref currentPowerArmorWeapon, "currentPowerArmorWeapon"); // Save/load current power armor weapon
}
#endregion
@@ -137,12 +152,49 @@ namespace ArachnaeSwarm
}
}
#endregion
#region State-Switching
public override void Notify_Unequipped(Pawn pawn)
{
base.Notify_Unequipped(pawn);
// Handle power armor weapon destruction and original weapon restoration
if (Ext?.powerArmorWeapon != null)
{
// Destroy the power armor weapon, wherever it might be.
// We track it with currentPowerArmorWeapon, so we don't rely on pawn.equipment.Primary.
if (currentPowerArmorWeapon != null)
{
if (pawn?.equipment != null && pawn.equipment.Contains(currentPowerArmorWeapon))
{
pawn.equipment.Remove(currentPowerArmorWeapon);
}
else if (pawn?.inventory?.innerContainer != null && pawn.inventory.innerContainer.Contains(currentPowerArmorWeapon))
{
pawn.inventory.innerContainer.Remove(currentPowerArmorWeapon);
}
// If it's on the map, destroy it there.
else if (currentPowerArmorWeapon.Spawned)
{
currentPowerArmorWeapon.DeSpawn();
}
string destroyedWeaponLabel = currentPowerArmorWeapon.Label;
currentPowerArmorWeapon.Destroy();
Log.Message($"[PA_Debug] Notify_Unequipped: Destroyed power armor weapon {destroyedWeaponLabel}.");
currentPowerArmorWeapon = null;
}
// Restore original weapon if saved
if (originalWeapon != null && pawn?.equipment != null)
{
string originalWeaponLabel = originalWeapon.Label;
pawn.equipment.MakeRoomFor(originalWeapon);
pawn.equipment.AddEquipment(originalWeapon);
Log.Message($"[PA_Debug] Notify_Unequipped: Restored original weapon {originalWeaponLabel}.");
originalWeapon = null;
}
}
Building building = sourceBuilding;
// If the source building reference is lost, create a new one as a fallback.
@@ -171,6 +223,12 @@ namespace ArachnaeSwarm
buildingFuelComp.ReceiveFuel(apparelFuelComp.Fuel);
}
// Sync quality back to building
if (this.TryGetComp<CompQuality>() is CompQuality apparelQuality && building.TryGetComp<CompQuality>() is CompQuality buildingQuality)
{
buildingQuality.SetQuality(apparelQuality.Quality, ArtGenerationContext.Colony);
}
Log.Message($"[PA_Debug] Notify_Unequipped: Before spawning building (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
// Ensure stackCount is at least 1 for buildings, as 0 stackCount causes errors during spawning

View File

@@ -1,5 +1,8 @@
using RimWorld;
using Verse;
using System.Collections.Generic;
using Verse.AI; // For PathEndMode and Danger
using UnityEngine; // For Texture2D
namespace ArachnaeSwarm
{
@@ -16,5 +19,45 @@ namespace ArachnaeSwarm
public class CompPowerArmorStation : ThingComp
{
public CompProperties_PowerArmorStation Props => (CompProperties_PowerArmorStation)props;
public override void CompTick()
{
base.CompTick();
var fuelComp = parent.GetComp<CompRefuelableNutrition>();
if (fuelComp != null)
{
// Set consumption rate to 0 when in building form
fuelComp.currentConsumptionRate = 0f;
}
}
public override IEnumerable<FloatMenuOption> CompFloatMenuOptions(Pawn selPawn)
{
foreach (FloatMenuOption option in base.CompFloatMenuOptions(selPawn))
{
yield return option;
}
// Check if there's an apparelDef defined
if (Props.apparelDef == null)
{
yield break; // No apparel to wear
}
// Check if the pawn can interact with the building
if (!selPawn.CanReserveAndReach(parent, PathEndMode.InteractionCell, Danger.Deadly))
{
yield return new FloatMenuOption("CannotEnterPowerArmor".Translate() + ": " + "CannotReach".Translate(), null);
}
else
{
void enterAction()
{
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_EnterPowerArmor"), parent);
selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
}
yield return new FloatMenuOption("EnterPowerArmor".Translate(parent.Label), enterAction);
}
}
}
}

View File

@@ -1,59 +0,0 @@
using HarmonyLib;
using RimWorld;
using System.Collections.Generic;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
[HarmonyPatch(typeof(ThingWithComps), "GetFloatMenuOptions")]
public static class Harmony_ThingWithComps_GetFloatMenuOptions
{
[HarmonyPostfix]
public static IEnumerable<FloatMenuOption> Postfix(IEnumerable<FloatMenuOption> values, ThingWithComps __instance, Pawn selPawn)
{
// First, return all original options
foreach (var value in values)
{
yield return value;
}
// --- DEBUG LOGGING ---
// Use a more specific check to avoid log spam
if (__instance.def.defName != null && __instance.def.defName.StartsWith("ARA_"))
{
Log.Message($"[PA_Debug] GetFloatMenuOptions Postfix triggered for: {__instance.def.defName}");
}
// Check if the thing is our power armor building
var comp = __instance.GetComp<CompPowerArmorStation>();
if (comp == null && __instance.def.defName != null && __instance.def.defName.StartsWith("ARA_"))
{
Log.Message($"[PA_Debug] CompPowerArmorStation is NULL for {__instance.def.defName}");
}
if (comp != null)
{
Log.Message($"[PA_Debug] CompPowerArmorStation FOUND for {__instance.def.defName}. Checking reachability.");
// Check if the pawn can interact
if (!selPawn.CanReserveAndReach(__instance, PathEndMode.InteractionCell, Danger.Deadly))
{
yield return new FloatMenuOption("CannotEnterPowerArmor".Translate() + ": " + "CannotReach".Translate(), null);
}
else
{
// Action to give the job
void enterAction()
{
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_EnterPowerArmor"), __instance);
selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
}
yield return new FloatMenuOption("EnterPowerArmor".Translate(__instance.Label), enterAction);
}
}
}
}
}

View File

@@ -50,11 +50,40 @@ namespace ArachnaeSwarm
apparelFuelComp.ReceiveFuel(buildingFuelComp.Fuel);
}
// Sync quality
if (building.TryGetComp<CompQuality>() is CompQuality buildingQuality && apparel.TryGetComp<CompQuality>() is CompQuality apparelQuality)
{
apparelQuality.SetQuality(buildingQuality.Quality, ArtGenerationContext.Colony);
}
// Wear the apparel. The second argument 'false' is lockWhileWorn.
// The third argument 'false' is playerForced, which is CRITICAL.
// If playerForced is true, the game automatically locks the apparel.
actor.apparel.Wear(apparel, false, false);
// Handle weapon switching
if (apparel.Ext.powerArmorWeapon != null)
{
if (actor.equipment.Primary != null)
{
apparel.SetOriginalWeapon(actor.equipment.Primary);
actor.equipment.TryDropEquipment(actor.equipment.Primary, out _, actor.Position, false);
}
ThingWithComps weapon = (ThingWithComps)ThingMaker.MakeThing(apparel.Ext.powerArmorWeapon);
// Sync weapon quality with armor quality
if (apparel.TryGetComp<CompQuality>() is CompQuality existingApparelQuality && weapon.TryGetComp<CompQuality>() is CompQuality weaponQuality)
{
weaponQuality.SetQuality(existingApparelQuality.Quality, ArtGenerationContext.Colony);
}
actor.equipment.MakeRoomFor(weapon);
actor.equipment.AddEquipment(weapon);
// Track the power armor weapon so it can be destroyed later
apparel.SetCurrentPowerArmorWeapon(weapon);
}
// Despawn the building
building.DeSpawn();
}