This commit is contained in:
2025-09-30 16:24:32 +08:00
parent 80cbaff5fa
commit 8aa2550d01
5 changed files with 237 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<ThingDef ParentName="BaseHumanMakeableGun">
<defName>ARA_RW_Basic_FireSpewer_Gun</defName>
<label>武装器官"害虫克星"</label>
<description>阿拉克涅虫群督虫使用基础远程武装器官,可以加压喷出热熔气团。瞬间伤害路径范围内的所有敌人。</description>
<tickerType>Normal</tickerType>
<techLevel>Animal</techLevel>
<descriptionHyperlinks>
<ThingDef>ARA_Cocoon_Weapon</ThingDef>
</descriptionHyperlinks>
<graphicData>
<texPath>ArachnaeSwarm/Weapon/ARA_RW_Basic_Acid_Bladder_Gun</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>1.2</drawSize>
</graphicData>
<soundInteract>SpitterSpawn</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False" />
<researchPrerequisite>ARA_Technology_7VXI</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<statBases>
<WorkToMake>1300</WorkToMake>
<!-- <MarketValue>370</MarketValue> -->
<Mass>3.5</Mass>
<AccuracyTouch>0.5</AccuracyTouch>
<AccuracyShort>0.6</AccuracyShort>
<AccuracyMedium>0.45</AccuracyMedium>
<AccuracyLong>0.3</AccuracyLong>
<RangedWeapon_Cooldown>2.5</RangedWeapon_Cooldown>
</statBases>
<verbs>
<li Class="ArachnaeSwarm.VerbProperties_FireSpew">
<verbClass>ArachnaeSwarm.Verb_ShootFireSpew</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<warmupTime>1.5</warmupTime>
<range>16</range> <!-- Standard range property -->
<ticksBetweenBurstShots>12</ticksBetweenBurstShots>
<burstShotCount>3</burstShotCount>
<soundCast>Shot_MiniFlameblaster</soundCast>
<soundCastTail>GunTail_Medium</soundCastTail>
<muzzleFlashScale>9</muzzleFlashScale>
<!-- Custom Properties for the modified Verb_SpewFire logic -->
<degrees>15</degrees>
<damageDef>ARA_AcidBurn</damageDef>
<damageAmount>20</damageAmount>
<filthDef>Filth_SpentAcid</filthDef>
<effecterDef>Fire_Spew</effecterDef>
<propagationSpeed>0.8</propagationSpeed>
<chanceToStartFire>0</chanceToStartFire>
<avoidFriendlyFire>true</avoidFriendlyFire>
</li>
</verbs>
<costList Inherit="False">
<ARA_Carapace>50</ARA_Carapace>
</costList>
<weaponTags>
<li>ARA_Armed_Organ</li>
<li>ARA_Armed_Organ_Ranged</li>
<li>ARA_Armed_Organ_T1</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
<comps>
<li Class="ArachnaeSwarm.CompProperties_CustomUniqueWeapon" MayRequire="Ludeon.RimWorld.Odyssey">
<forcedTraits>
<li>ARA_Weapon_Damage_Acid</li>
</forcedTraits>
<numTraitsRange>
<min>1</min>
<max>1</max>
</numTraitsRange>
</li>
</comps>
</ThingDef>
</Defs>

View File

@@ -225,6 +225,8 @@
<Compile Include="Verbs\Verb_ShootArc.cs" />
<Compile Include="Verbs\Verb_ShootMeltBeam.cs" />
<Compile Include="Verbs\Verb_ShootShotgun.cs" />
<Compile Include="Verbs\Verb_ShootFireSpew.cs" />
<Compile Include="Verbs\VerbProperties_FireSpew.cs" />
<Compile Include="Verbs\Verb_ShootConsumeNutrition.cs" />
<Compile Include="Verbs\VerbProperties_Excalibur.cs" />
<Compile Include="Verbs\Verb_ShootSprayMulti.cs" />

View File

@@ -0,0 +1,23 @@
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class VerbProperties_FireSpew : VerbProperties
{
public float degrees = 26f; // Default based on original Verb_SpewFire (13 * 2)
public DamageDef damageDef;
public int damageAmount = -1;
public float armorPenetration = -1f;
public ThingDef filthDef;
public EffecterDef effecterDef;
public float propagationSpeed = -1f; // Speed of the explosion spread
public float chanceToStartFire = -1f; // Chance to start a fire
public bool avoidFriendlyFire = true; // Avoids damaging non-hostile targets
public VerbProperties_FireSpew()
{
this.verbClass = typeof(Verb_ShootFireSpew);
}
}
}

View File

@@ -0,0 +1,131 @@
using RimWorld;
using UnityEngine;
using Verse;
using System.Collections.Generic; // Added for List and HashSet
namespace ArachnaeSwarm
{
public class Verb_ShootFireSpew : Verb
{
protected override int ShotsPerBurst => base.BurstShotCount;
public override void WarmupComplete()
{
base.WarmupComplete();
}
protected override bool TryCastShot()
{
if (currentTarget.HasThing && currentTarget.Thing.Map != caster.Map)
{
return false;
}
if (this.verbProps is not VerbProperties_FireSpew verbPropsFireSpew)
{
return false;
}
if (base.EquipmentSource != null)
{
base.EquipmentSource.GetComp<CompChangeableProjectile>()?.Notify_ProjectileLaunched();
base.EquipmentSource.GetComp<CompApparelReloadable>()?.UsedOnce();
}
IntVec3 position = caster.Position;
float num = Mathf.Atan2(-(currentTarget.Cell.z - position.z), currentTarget.Cell.x - position.x) * 57.29578f;
float halfAngle = verbPropsFireSpew.degrees / 2f;
FloatRange value = new FloatRange(num - halfAngle, num + halfAngle);
IntVec3 center = position;
Map mapHeld = caster.Map; // Use Caster.Map for consistency
float effectiveRange = EffectiveRange;
DamageDef damage = verbPropsFireSpew.damageDef ?? DamageDefOf.Flame;
ThingDef filth = verbPropsFireSpew.filthDef ?? ThingDefOf.Filth_FlammableBile;
int damageAmount = verbPropsFireSpew.damageAmount > 0 ? verbPropsFireSpew.damageAmount : damage.defaultDamage;
float armorPenetration = verbPropsFireSpew.armorPenetration >= 0 ? verbPropsFireSpew.armorPenetration : damage.defaultArmorPenetration;
float propagationSpeed = verbPropsFireSpew.propagationSpeed >= 0 ? verbPropsFireSpew.propagationSpeed : 0.6f;
float chanceToStartFire = verbPropsFireSpew.chanceToStartFire >= 0f ? verbPropsFireSpew.chanceToStartFire : 0f;
List<Thing> ignoredThings = null;
if (verbPropsFireSpew.avoidFriendlyFire)
{
ignoredThings = new List<Thing>();
var affectedCells = new HashSet<IntVec3>(GenRadial.RadialCellsAround(center, effectiveRange, true));
foreach (IntVec3 cell in affectedCells)
{
if (cell.InBounds(mapHeld))
{
List<Thing> thingList = mapHeld.thingGrid.ThingsListAt(cell);
for (int i = 0; i < thingList.Count; i++) // Corrected .Count usage
{
Thing t = thingList[i];
if (!ignoredThings.Contains(t) && t.Faction != null && !t.HostileTo(caster))
{
ignoredThings.Add(t);
}
}
}
}
}
FloatRange? affectedAngle = value;
GenExplosion.DoExplosion(
center: center,
map: mapHeld,
radius: effectiveRange,
damType: damage,
instigator: caster,
damAmount: damageAmount,
armorPenetration: armorPenetration,
explosionSound: null,
weapon: base.EquipmentSource?.def,
projectile: this.verbProps.defaultProjectile,
intendedTarget: null,
postExplosionSpawnThingDef: filth,
postExplosionSpawnChance: 1f,
postExplosionSpawnThingCount: 1,
applyDamageToExplosionCellsNeighbors: false,
preExplosionSpawnThingDef: null,
preExplosionSpawnChance: 0f,
preExplosionSpawnThingCount: 1,
chanceToStartFire: chanceToStartFire,
damageFalloff: false,
direction: null,
ignoredThings: ignoredThings, // Correct position for ignoredThings
affectedAngle: affectedAngle,
doVisualEffects: false,
propagationSpeed: propagationSpeed,
screenShakeFactor: 0f,
doSoundEffects: false
);
if (verbPropsFireSpew.effecterDef != null)
{
AddEffecterToMaintain(verbPropsFireSpew.effecterDef.Spawn(caster.Position, currentTarget.Cell, caster.Map), caster.Position, currentTarget.Cell, 14, caster.Map);
}
lastShotTick = Find.TickManager.TicksGame;
return true;
}
public override bool Available()
{
if (!base.Available())
{
return false;
}
if (CasterIsPawn)
{
Pawn casterPawn = CasterPawn;
if (casterPawn.Faction != Faction.OfPlayer && casterPawn.mindState.MeleeThreatStillThreat && casterPawn.mindState.meleeThreat.Position.AdjacentTo8WayOrInside(casterPawn.Position))
{
return false;
}
}
return true;
}
}
}