This commit is contained in:
2025-09-12 15:31:34 +08:00
parent 1b3c01d8bc
commit 5c32da8634
10 changed files with 447 additions and 1 deletions

Binary file not shown.

View File

@@ -31,6 +31,46 @@
</li>
</comps>
</AbilityDef>
<AbilityDef>
<defName>ARA_TrapSpew</defName>
<label>酸爆茧投射</label>
<description>发射一排阿拉克涅休眠茧,其中装有阿拉克涅酸爆种,这些危险的虫族被唤醒后就会开始寻仇,匆匆了结自己的生命.</description>
<iconPath>UI/Commands/EggSpew</iconPath>
<cooldownTicksRange>5000</cooldownTicksRange> <!-- 2 hours -->
<aiCanUse>true</aiCanUse>
<displayOrder>300</displayOrder>
<displayGizmoWhileUndrafted>true</displayGizmoWhileUndrafted>
<disableGizmoWhileUndrafted>false</disableGizmoWhileUndrafted>
<warmupStartSound>AcidSpray_Warmup</warmupStartSound>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<range>14.9</range>
<warmupTime>0.25</warmupTime>
<soundCast>AcidSpray_Resolve</soundCast>
<targetParams>
<canTargetLocations>true</canTargetLocations>
</targetParams>
</verbProperties>
<comps>
<li Class="CompProperties_AbilitySprayLiquid">
<projectileDef>ARA_Proj_StrongSludgeSpray</projectileDef>
<numCellsToHit>8</numCellsToHit>
<sprayEffecter>AcidSpray_Directional</sprayEffecter>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityLaunchMultiProjectile">
<projectileDef>ARA_Proj_Trap</projectileDef>
<numProjectiles>3</numProjectiles>
</li>
</comps>
</AbilityDef>
<ThingDef ParentName="ARA_EggSpew_Base_Proj">
<defName>ARA_Proj_Trap</defName>
<label>阿拉克涅酸爆茧</label>
<projectile>
<spawnsThingDef>Arachnae_Trap_ReleaseRandom</spawnsThingDef>
</projectile>
</ThingDef>
<AbilityDef>
<defName>ARA_TumorSpew</defName>
<label>菌瘤释放</label>

View File

@@ -131,6 +131,60 @@
</tools>
</ThingDef>
<ThingDef ParentName="ARA_BaseDroneSwarm">
<defName>ArachnaeBase_Race_Acidling</defName>
<label>阿拉克涅酸爆种</label>
<description>阿拉克涅辅虫之一,智力低下,一般被作为自杀式无人机打出,靠近敌人后就会引爆酸囊产生危险的酸性雾气。</description>
<race>
<thinkTreeConstant>HunterDroneConstant</thinkTreeConstant>
<baseBodySize>0.5</baseBodySize>
<!-- <hasCorpse>false</hasCorpse> -->
<body>BeetleLikeWithClaw</body>
<lifeStageAges>
<li>
<def>EusocialInsectAdult</def>
<minAge>0</minAge>
<soundWounded>Pawn_Spelopede_Pain</soundWounded>
<soundDeath>Pawn_Spelopede_Death</soundDeath>
<soundCall>Pawn_Spelopede_Call</soundCall>
<soundAngry>Pawn_Spelopede_Angry</soundAngry>
</li>
</lifeStageAges>
</race>
<statBases>
<MoveSpeed>6</MoveSpeed>
</statBases>
<comps>
<li Class="CompProperties_MechPowerCell">
<totalPowerTicks>4400</totalPowerTicks> <!-- 2 hours -->
<labelOverride>寿命</labelOverride>
<tooltipOverride>这种特殊的阿拉克涅辅虫从出生起就走在死亡的道路上了——它们的寿命就是如此短暂。</tooltipOverride>
<showGizmoOnNonPlayerControlled>true</showGizmoOnNonPlayerControlled>
</li>
<li Class="ArachnaeSwarm.CompProperties_HunterExplosion">
<explosionRadius>6</explosionRadius> <!-- Mapped from smokeRadius -->
<explosionDamageType>ARA_AcidBurn</explosionDamageType>
<explosionDamageAmount>40</explosionDamageAmount>
<armorPenetration>0.5</armorPenetration>
<soundExplosion>Explosion_Smoke</soundExplosion>
<postExplosionSpawnThingDef>ARA_AcidGasCloud</postExplosionSpawnThingDef>
<postExplosionSpawnChance>1</postExplosionSpawnChance>
<postExplosionSpawnThingCount>1</postExplosionSpawnThingCount>
</li>
</comps>
<tools Inherit="False">
<li>
<label>酸性巨颚</label>
<capacities>
<li>ARA_AcidCut</li>
</capacities>
<power>6</power>
<cooldownTime>2.6</cooldownTime>
<linkedBodyPartsGroup>HeadAttackTool</linkedBodyPartsGroup>
<ensureLinkedBodyPartsGroupAlwaysUsable>true</ensureLinkedBodyPartsGroupAlwaysUsable>
</li>
</tools>
</ThingDef>
<ToolCapacityDef>
<defName>ARA_AcidCut</defName>
<label>酸性撕咬</label>

View File

@@ -40,4 +40,32 @@
</modExtensions>
</ThingDef>
<!-- Gas : Thing -->
<ThingDef ParentName="DRM_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>
</Defs>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef ParentName="TrapReleaseBase">
<defName>Arachnae_Trap_ReleaseRandom</defName>
<label>阿拉克涅酸爆茧</label>
<description>一种阿拉克涅休眠茧,内藏有休眠中的阿拉克涅虫族。如果靠太近则会唤醒其中的虫族。</description>
<thingClass>ArachnaeSwarm.Building_TrapReleaseRandom</thingClass>
<!-- 基础外观和物理属性 -->
<size>(1,1)</size>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<selectable>true</selectable>
<fillPercent>0.20</fillPercent>
<leaveResourcesWhenKilled>false</leaveResourcesWhenKilled>
<rotatable>false</rotatable>
<tickerType>Normal</tickerType>
<repairEffect>EatVegetarian</repairEffect>
<filthLeaving>Filth_Slime</filthLeaving>
<category>Buildings</category>
<costList>
<Steel>20</Steel>
</costList>
<statBases>
<MaxHitPoints>10</MaxHitPoints>
<Mass>4</Mass>
<Flammability>1.0</Flammability>
<MarketValue>50</MarketValue>
</statBases>
<graphicData>
<texPath>ArachnaeSwarm/Building/ARA_Tumor</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>1.2</drawSize>
<shadowData>
<volume>(0.3, 0.6, 0.3)</volume>
<offset>(0,0,-0.1)</offset>
</shadowData>
</graphicData>
<!-- XML-assignable fields from Building_TrapReleaseRandom -->
<countToSpawn>3</countToSpawn>
<pawnKinds>
<li>ArachnaeBase_Race_Acidling</li>
<li>ArachnaeBase_Race_Acid</li>
</pawnKinds>
<!-- 组件:发光、生命周期、延迟地面生成(与风格示例对应) -->
<comps>
<li Class="CompProperties_Glower">
<glowRadius>3</glowRadius>
<glowColor>(220,210,171,0)</glowColor>
</li>
</comps>
</ThingDef>
</Defs>

View File

@@ -22,4 +22,8 @@
<ARA_TipString_ControlledDrones>控制的虫族:</ARA_TipString_ControlledDrones>
<ARA_TipString_NoDronesBound>尚未绑定任何虫族。</ARA_TipString_NoDronesBound>
<!-- Trap messages -->
<MessageTrapSprung>{TRAP} 被 {PAWN} 触发!</MessageTrapSprung>
<MessageTrapSprung_NoInstigator>{TRAP} 被触发!</MessageTrapSprung_NoInstigator>
</LanguageData>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="15.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -161,6 +162,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CompAbilityEffect_LaunchMultiProjectile.cs" />
<Compile Include="Building_TrapReleaseRandom.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Abilities\CompAbilityEffect_TrackingCharge.cs" />
@@ -192,6 +194,8 @@
<Compile Include="Hediff_ConfigurableMutant.cs" />
<Compile Include="HediffComp_Symbiosis.cs" />
<Compile Include="HediffGiver_RandomWithSeverity.cs" />
<Compile Include="CompHunterExplosion.cs" />
<Compile Include="CompProperties_HunterExplosion.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Morphable\CompProperties_Morphable.cs" />

View File

@@ -0,0 +1,85 @@
using System.Collections.Generic;
using Verse;
using Verse.Sound;
using RimWorld;
namespace ArachnaeSwarm;
public class Building_TrapReleaseRandom : Building_TrapReleaseEntity
{
// XML-assignable: number of pawns to spawn
public int countToSpawn = 1;
// XML-assignable: list of PawnKindDefs to choose from randomly when spawning
public List<PawnKindDef> pawnKinds = new List<PawnKindDef>();
protected override int CountToSpawn => countToSpawn;
protected override PawnKindDef PawnToSpawn => (pawnKinds == null || pawnKinds.Count == 0) ? PawnKindDefOf.Drone_Hunter : pawnKinds.RandomElement();
// Ensure spawned pawns use the building's faction when possible; otherwise spawn as wild (null faction)
protected override void SpringSub(Pawn p)
{
if (base.Spawned)
{
SoundStarter.PlayOneShot(SoundDefOf.DroneTrapSpring, new TargetInfo(base.Position, base.Map));
if (base.Faction != Faction.OfPlayer)
{
// Show a localized message including the trap name and, if available, the pawn who triggered it.
if (p != null)
{
Messages.Message("MessageTrapSprung".Translate(this.Named("TRAP"), p.Named("PAWN")), new LookTargets(base.Position, base.Map), MessageTypeDefOf.NegativeEvent);
}
else
{
Messages.Message("MessageTrapSprung_NoInstigator".Translate(this.Named("TRAP")), new LookTargets(base.Position, base.Map), MessageTypeDefOf.NegativeEvent);
}
}
for (int i = 0; i < CountToSpawn; i++)
{
PawnKindDef pawnToSpawn = PawnToSpawn;
// Prefer the building's faction (usually the faction that built it). If none, pass null to produce a wild pawn.
Faction spawnFaction = this.Faction;
float? fixedBiologicalAge = 0f;
float? fixedChronologicalAge = 0f;
Pawn pawn = PawnGenerator.GeneratePawn(new PawnGenerationRequest(
pawnToSpawn,
spawnFaction,
PawnGenerationContext.NonPlayer,
null,
forceGenerateNewPawn: false,
allowDead: false,
allowDowned: false,
canGeneratePawnRelations: true,
mustBeCapableOfViolence: false,
1f,
forceAddFreeWarmLayerIfNeeded: false,
allowGay: true,
allowPregnant: false,
allowFood: true,
allowAddictions: true,
inhabitant: false,
certainlyBeenInCryptosleep: false,
forceRedressWorldPawnIfFormerColonist: false,
worldPawnFactionDoesntMatter: false,
0f,
0f,
null,
1f,
null,
null,
null,
null,
null,
fixedBiologicalAge,
fixedChronologicalAge));
pawn.mindState.enemyTarget = p;
GenSpawn.Spawn(pawn, base.Position, base.Map);
}
}
}
}

View File

@@ -0,0 +1,123 @@
using Verse;
using Verse.Sound;
using System.Collections.Generic;
using RimWorld;
namespace ArachnaeSwarm;
public class CompHunterExplosion : ThingComp
{
private bool wickStarted;
private int wickTicks;
[Unsaved(false)]
private Sustainer wickSoundSustainer;
[Unsaved(false)]
private OverlayHandle? overlayBurningWick;
private CompProperties_HunterExplosion Props => (CompProperties_HunterExplosion)props;
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref wickStarted, "wickStarted", defaultValue: false);
Scribe_Values.Look(ref wickTicks, "wickTicks", 0);
}
public override void CompTickInterval(int delta)
{
if (!wickStarted && parent.IsHashIntervalTick(30, delta) && parent is Pawn { Spawned: not false, Downed: false } pawn && PawnUtility.EnemiesAreNearby(pawn, 9, passDoors: true, 1.5f))
{
StartWick();
}
}
public override void CompTick()
{
if (wickStarted)
{
wickSoundSustainer.Maintain();
wickTicks--;
if (wickTicks <= 0)
{
Detonate();
}
}
}
private void StartWick()
{
if (!wickStarted)
{
wickStarted = true;
overlayBurningWick = parent.Map.overlayDrawer.Enable(parent, OverlayTypes.BurningWick);
wickSoundSustainer = SoundDefOf.HissSmall.TrySpawnSustainer(SoundInfo.InMap(parent, MaintenanceType.PerTick));
wickTicks = 120;
}
}
public override void Notify_Killed(Map prevMap, DamageInfo? dinfo = null)
{
if (dinfo.HasValue)
{
Detonate(prevMap);
}
}
private void Detonate(Map map = null)
{
IntVec3 position = parent.Position;
if (map == null)
{
map = parent.Map;
}
if (!parent.Destroyed)
{
parent.Destroy();
}
GenExplosion.DoExplosion(
center: position,
map: map,
radius: Props.explosionRadius,
damType: Props.explosionDamageType,
instigator: parent,
damAmount: Props.explosionDamageAmount,
armorPenetration: Props.armorPenetration,
explosionSound: Props.explosionSound,
weapon: Props.weapon,
projectile: Props.projectile,
intendedTarget: Props.intendedTarget,
postExplosionSpawnThingDef: Props.postExplosionSpawnThingDef,
postExplosionSpawnChance: Props.postExplosionSpawnChance,
postExplosionSpawnThingCount: Props.postExplosionSpawnThingCount,
postExplosionGasType: Props.postExplosionGasType,
postExplosionGasRadiusOverride: Props.postExplosionGasRadiusOverride,
postExplosionGasAmount: Props.postExplosionGasAmount,
applyDamageToExplosionCellsNeighbors: Props.applyDamageToExplosionCellsNeighbors,
preExplosionSpawnThingDef: Props.preExplosionSpawnThingDef,
preExplosionSpawnChance: Props.preExplosionSpawnChance,
preExplosionSpawnThingCount: Props.preExplosionSpawnThingCount,
chanceToStartFire: Props.chanceToStartFire,
damageFalloff: Props.damageFalloff,
direction: Props.direction,
ignoredThings: Props.ignoredThings,
affectedAngle: Props.affectedAngle,
doVisualEffects: Props.doVisualEffects,
propagationSpeed: Props.propagationSpeed,
excludeRadius: Props.excludeRadius,
doSoundEffects: Props.doSoundEffects,
postExplosionSpawnThingDefWater: Props.postExplosionSpawnThingDefWater,
screenShakeFactor: Props.screenShakeFactor,
flammabilityChanceCurve: Props.flammabilityChanceCurve,
overrideCells: Props.overrideCells,
postExplosionSpawnSingleThingDef: Props.postExplosionSpawnSingleThingDef,
preExplosionSpawnSingleThingDef: Props.preExplosionSpawnSingleThingDef
);
if (base.ParentHolder is Corpse corpse)
{
corpse.Destroy();
}
}
}

View File

@@ -0,0 +1,56 @@
using System.Collections.Generic;
using RimWorld;
using Verse;
namespace ArachnaeSwarm;
public class CompProperties_HunterExplosion : CompProperties
{
public float explosionRadius = 1.9f;
public DamageDef explosionDamageType;
public int explosionDamageAmount = 50;
public float armorPenetration = -1f;
public SoundDef explosionSound;
public ThingDef weapon;
public ThingDef projectile;
public Thing intendedTarget;
public ThingDef postExplosionSpawnThingDef;
public float postExplosionSpawnChance;
public int postExplosionSpawnThingCount = 1;
public GasType? postExplosionGasType;
public float? postExplosionGasRadiusOverride;
public int postExplosionGasAmount = 255;
public bool applyDamageToExplosionCellsNeighbors;
public ThingDef preExplosionSpawnThingDef;
public float preExplosionSpawnChance;
public int preExplosionSpawnThingCount = 1;
public float chanceToStartFire;
public bool damageFalloff;
public float? direction;
public List<Thing> ignoredThings;
public FloatRange? affectedAngle;
public bool doVisualEffects = true;
public float propagationSpeed = 1f;
public float excludeRadius;
public bool doSoundEffects = true;
public ThingDef postExplosionSpawnThingDefWater;
public float screenShakeFactor = 1f;
public SimpleCurve flammabilityChanceCurve;
public List<IntVec3> overrideCells;
public ThingDef postExplosionSpawnSingleThingDef;
public ThingDef preExplosionSpawnSingleThingDef;
public CompProperties_HunterExplosion()
{
compClass = typeof(CompHunterExplosion);
}
public override void ResolveReferences(ThingDef parentDef)
{
base.ResolveReferences(parentDef);
if (explosionDamageType == null)
{
explosionDamageType = DamageDefOf.Bomb;
}
}
}