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

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;
}
}
}